Merge "Define MACsec HAL and ref impl" into main
diff --git a/audio/aidl/Android.bp b/audio/aidl/Android.bp
index 5a009fe..89d186c 100644
--- a/audio/aidl/Android.bp
+++ b/audio/aidl/Android.bp
@@ -34,6 +34,7 @@
     name: "android.hardware.audio.common",
     defaults: [
         "android.hardware.audio_defaults",
+        "latest_android_media_audio_common_types_import_interface",
     ],
     srcs: [
         "android/hardware/audio/common/AudioOffloadMetadata.aidl",
@@ -42,10 +43,7 @@
         "android/hardware/audio/common/SinkMetadata.aidl",
         "android/hardware/audio/common/SourceMetadata.aidl",
     ],
-    frozen: true,
-    imports: [
-        "android.media.audio.common.types-V2",
-    ],
+    frozen: false,
     backend: {
         cpp: {
             enabled: true,
@@ -83,7 +81,7 @@
 }
 
 // Note: This should always be one version ahead of the last frozen version
-latest_android_hardware_audio_common = "android.hardware.audio.common-V2"
+latest_android_hardware_audio_common = "android.hardware.audio.common-V3"
 
 // Modules that depend on android.hardware.audio.common directly can include
 // the following cc_defaults to avoid explicitly managing dependency versions
@@ -109,10 +107,21 @@
     ],
 }
 
+aidl_interface_defaults {
+    name: "latest_android_hardware_audio_common_import_interface",
+    imports: [
+        latest_android_hardware_audio_common,
+    ],
+}
+
 aidl_interface {
     name: "android.hardware.audio.core",
     defaults: [
         "android.hardware.audio_defaults",
+        "latest_android_hardware_audio_common_import_interface",
+        "latest_android_hardware_audio_core_sounddose_import_interface",
+        "latest_android_hardware_audio_effect_import_interface",
+        "latest_android_media_audio_common_types_import_interface",
     ],
     srcs: [
         "android/hardware/audio/core/AudioPatch.aidl",
@@ -137,10 +146,6 @@
     imports: [
         "android.hardware.common-V2",
         "android.hardware.common.fmq-V1",
-        "android.hardware.audio.common-V2",
-        "android.hardware.audio.core.sounddose-V1",
-        "android.hardware.audio.effect-V1",
-        "android.media.audio.common.types-V2",
     ],
     backend: {
         // The C++ backend is disabled transitively due to use of FMQ.
@@ -167,11 +172,11 @@
         // IMPORTANT: Update latest_android_hardware_audio_core every time you
         // add the latest frozen version to versions_with_info
     ],
-    frozen: true,
+    frozen: false,
 }
 
 // Note: This should always be one version ahead of the last frozen version
-latest_android_hardware_audio_core = "android.hardware.audio.core-V1"
+latest_android_hardware_audio_core = "android.hardware.audio.core-V2"
 
 // Modules that depend on android.hardware.audio.core directly can include
 // the following cc_defaults to avoid explicitly managing dependency versions
@@ -190,18 +195,23 @@
     ],
 }
 
+aidl_interface_defaults {
+    name: "latest_android_hardware_audio_core_import_interface",
+    imports: [
+        latest_android_hardware_audio_core,
+    ],
+}
+
 // Used for the standalone sounddose HAL
 aidl_interface {
     name: "android.hardware.audio.core.sounddose",
     defaults: [
         "android.hardware.audio_defaults",
+        "latest_android_media_audio_common_types_import_interface",
     ],
     srcs: [
         "android/hardware/audio/core/sounddose/ISoundDose.aidl",
     ],
-    imports: [
-        "android.media.audio.common.types-V2",
-    ],
     backend: {
         // The C++ backend is disabled transitively due to use of FMQ by the core HAL.
         cpp: {
@@ -220,11 +230,11 @@
         // IMPORTANT: Update latest_android_hardware_audio_core_sounddose every time you
         // add the latest frozen version to versions_with_info
     ],
-    frozen: true,
+    frozen: false,
 }
 
 // Note: This should always be one version ahead of the last frozen version
-latest_android_hardware_audio_core_sounddose = "android.hardware.audio.core.sounddose-V1"
+latest_android_hardware_audio_core_sounddose = "android.hardware.audio.core.sounddose-V2"
 
 // Modules that depend on android.hardware.audio.core.sounddose directly can include
 // the following cc_defaults to avoid explicitly managing dependency versions
@@ -237,16 +247,32 @@
 }
 
 cc_defaults {
+    name: "latest_android_hardware_audio_core_sounddose_ndk_export_shared_lib_header",
+    export_shared_lib_headers: [
+        latest_android_hardware_audio_core_sounddose + "-ndk",
+    ],
+}
+
+cc_defaults {
     name: "latest_android_hardware_audio_core_sounddose_ndk_static",
     static_libs: [
         latest_android_hardware_audio_core_sounddose + "-ndk",
     ],
 }
 
+aidl_interface_defaults {
+    name: "latest_android_hardware_audio_core_sounddose_import_interface",
+    imports: [
+        latest_android_hardware_audio_core_sounddose,
+    ],
+}
+
 aidl_interface {
     name: "android.hardware.audio.effect",
     defaults: [
         "android.hardware.audio_defaults",
+        "latest_android_hardware_audio_common_import_interface",
+        "latest_android_media_audio_common_types_import_interface",
     ],
     srcs: [
         "android/hardware/audio/effect/AcousticEchoCanceler.aidl",
@@ -271,6 +297,7 @@
         "android/hardware/audio/effect/PresetReverb.aidl",
         "android/hardware/audio/effect/Processing.aidl",
         "android/hardware/audio/effect/Range.aidl",
+        "android/hardware/audio/effect/Spatializer.aidl",
         "android/hardware/audio/effect/State.aidl",
         "android/hardware/audio/effect/VendorExtension.aidl",
         "android/hardware/audio/effect/Virtualizer.aidl",
@@ -280,8 +307,6 @@
     imports: [
         "android.hardware.common-V2",
         "android.hardware.common.fmq-V1",
-        "android.hardware.audio.common-V2",
-        "android.media.audio.common.types-V2",
     ],
     backend: {
         // The C++ backend is disabled transitively due to use of FMQ.
@@ -303,11 +328,11 @@
             ],
         },
     ],
-    frozen: true,
+    frozen: false,
 
 }
 
-latest_android_hardware_audio_effect = "android.hardware.audio.effect-V1"
+latest_android_hardware_audio_effect = "android.hardware.audio.effect-V2"
 
 cc_defaults {
     name: "latest_android_hardware_audio_effect_ndk_shared",
@@ -322,3 +347,10 @@
         latest_android_hardware_audio_effect + "-ndk",
     ],
 }
+
+aidl_interface_defaults {
+    name: "latest_android_hardware_audio_effect_import_interface",
+    imports: [
+        latest_android_hardware_audio_effect,
+    ],
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl
index 0422bd9..7313b57 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl
@@ -60,6 +60,7 @@
     android.hardware.audio.effect.Visualizer.Id visualizerTag;
     android.hardware.audio.effect.Volume.Id volumeTag;
     android.hardware.audio.effect.Parameter.Tag commonTag;
+    android.hardware.audio.effect.Spatializer.Id spatializerTag;
   }
   @VintfStability
   parcelable Common {
@@ -91,5 +92,6 @@
     android.hardware.audio.effect.Virtualizer virtualizer;
     android.hardware.audio.effect.Visualizer visualizer;
     android.hardware.audio.effect.Volume volume;
+    android.hardware.audio.effect.Spatializer spatializer;
   }
 }
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Range.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Range.aidl
index 93edc5e..40ee6b5 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Range.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Range.aidl
@@ -50,6 +50,7 @@
   android.hardware.audio.effect.Range.VirtualizerRange[] virtualizer;
   android.hardware.audio.effect.Range.VisualizerRange[] visualizer;
   android.hardware.audio.effect.Range.VolumeRange[] volume;
+  android.hardware.audio.effect.Range.SpatializerRange[] spatializer;
   @VintfStability
   parcelable AcousticEchoCancelerRange {
     android.hardware.audio.effect.AcousticEchoCanceler min;
@@ -111,6 +112,11 @@
     android.hardware.audio.effect.PresetReverb max;
   }
   @VintfStability
+  parcelable SpatializerRange {
+    android.hardware.audio.effect.Spatializer min;
+    android.hardware.audio.effect.Spatializer max;
+  }
+  @VintfStability
   parcelable VendorExtensionRange {
     android.hardware.audio.effect.VendorExtension min;
     android.hardware.audio.effect.VendorExtension max;
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/SatelliteCapabilities.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Spatializer.aidl
similarity index 66%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/SatelliteCapabilities.aidl
copy to audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Spatializer.aidl
index e0af71a..98ecee0 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/SatelliteCapabilities.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Spatializer.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,13 +31,20 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
-/* @hide */
-@JavaDerive(toString=true) @VintfStability
-parcelable SatelliteCapabilities {
-  android.hardware.radio.satellite.NTRadioTechnology[] supportedRadioTechnologies;
-  boolean isAlwaysOn;
-  boolean needsPointingToSatellite;
-  android.hardware.radio.satellite.SatelliteFeature[] supportedFeatures;
-  boolean needsSeparateSimProfile;
+package android.hardware.audio.effect;
+@VintfStability
+union Spatializer {
+  android.hardware.audio.effect.VendorExtension vendor;
+  android.media.audio.common.AudioChannelLayout[] supportedChannelLayout;
+  android.media.audio.common.Spatialization.Level spatializationLevel;
+  android.media.audio.common.Spatialization.Mode spatializationMode;
+  int headTrackingSensorId;
+  android.media.audio.common.HeadTracking.Mode headTrackingMode;
+  android.media.audio.common.HeadTracking.ConnectionMode headTrackingConnectionMode;
+  android.media.audio.common.HeadTracking.SensorData headTrackingSensorData;
+  @VintfStability
+  union Id {
+    android.hardware.audio.effect.VendorExtension vendorExtensionTag;
+    android.hardware.audio.effect.Spatializer.Tag commonTag;
+  }
 }
diff --git a/audio/aidl/android/hardware/audio/effect/Parameter.aidl b/audio/aidl/android/hardware/audio/effect/Parameter.aidl
index 0954055..6ec7226 100644
--- a/audio/aidl/android/hardware/audio/effect/Parameter.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Parameter.aidl
@@ -28,6 +28,7 @@
 import android.hardware.audio.effect.LoudnessEnhancer;
 import android.hardware.audio.effect.NoiseSuppression;
 import android.hardware.audio.effect.PresetReverb;
+import android.hardware.audio.effect.Spatializer;
 import android.hardware.audio.effect.VendorExtension;
 import android.hardware.audio.effect.Virtualizer;
 import android.hardware.audio.effect.Visualizer;
@@ -103,6 +104,11 @@
          * directly.
          */
         Parameter.Tag commonTag;
+
+        /**
+         * Parameter tag defined for Spatializer parameters.
+         */
+        Spatializer.Id spatializerTag;
     }
 
     /**
@@ -189,6 +195,7 @@
         Virtualizer virtualizer;
         Visualizer visualizer;
         Volume volume;
+        Spatializer spatializer;
     }
     Specific specific;
 }
diff --git a/audio/aidl/android/hardware/audio/effect/Range.aidl b/audio/aidl/android/hardware/audio/effect/Range.aidl
index 567320a..e5acb68 100644
--- a/audio/aidl/android/hardware/audio/effect/Range.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Range.aidl
@@ -28,6 +28,7 @@
 import android.hardware.audio.effect.LoudnessEnhancer;
 import android.hardware.audio.effect.NoiseSuppression;
 import android.hardware.audio.effect.PresetReverb;
+import android.hardware.audio.effect.Spatializer;
 import android.hardware.audio.effect.VendorExtension;
 import android.hardware.audio.effect.Virtualizer;
 import android.hardware.audio.effect.Visualizer;
@@ -169,6 +170,12 @@
     }
 
     @VintfStability
+    parcelable SpatializerRange {
+        Spatializer min;
+        Spatializer max;
+    }
+
+    @VintfStability
     parcelable VendorExtensionRange {
         VendorExtension min;
         VendorExtension max;
@@ -217,4 +224,5 @@
     VirtualizerRange[] virtualizer;
     VisualizerRange[] visualizer;
     VolumeRange[] volume;
+    SpatializerRange[] spatializer;
 }
diff --git a/audio/aidl/android/hardware/audio/effect/Spatializer.aidl b/audio/aidl/android/hardware/audio/effect/Spatializer.aidl
new file mode 100644
index 0000000..6ebe0d5
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/Spatializer.aidl
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.audio.effect;
+
+import android.hardware.audio.effect.VendorExtension;
+import android.media.audio.common.AudioChannelLayout;
+import android.media.audio.common.HeadTracking;
+import android.media.audio.common.Spatialization;
+
+/**
+ * Union representing parameters for audio spatialization effects.
+ *
+ * Sound spatialization simulates sounds around the listener as if they were emanating from virtual
+ * positions based on the original recording.
+ * For more details, refer to the documentation:
+ * https://developer.android.com/reference/android/media/Spatializer.
+ *
+ * android.hardware.audio.effect.Spatializer specifies parameters for the implementation of audio
+ * spatialization effects.
+ *
+ * A Spatializer implementation must report its supported parameter ranges using Capability.Range.
+ * spatializer.
+ */
+@VintfStability
+union Spatializer {
+    /**
+     * Parameter tag to identify the parameters for getParameter().
+     */
+    @VintfStability
+    union Id {
+        VendorExtension vendorExtensionTag;
+        Spatializer.Tag commonTag;
+    }
+
+    /**
+     * Vendor extension implementation for additional parameters.
+     */
+    VendorExtension vendor;
+
+    /**
+     * List of supported input channel layouts.
+     */
+    AudioChannelLayout[] supportedChannelLayout;
+
+    /**
+     * Level of spatialization.
+     */
+    Spatialization.Level spatializationLevel;
+
+    /**
+     * Spatialization mode, Binaural or Transaural for example.
+     */
+    Spatialization.Mode spatializationMode;
+
+    /**
+     * Head tracking sensor ID.
+     */
+    int headTrackingSensorId;
+
+    /**
+     * Head tracking mode for spatialization.
+     */
+    HeadTracking.Mode headTrackingMode;
+
+    /**
+     * Head tracking sensor connection mode for spatialization.
+     */
+    HeadTracking.ConnectionMode headTrackingConnectionMode;
+
+    /**
+     * Headtracking sensor data.
+     */
+    HeadTracking.SensorData headTrackingSensorData;
+}
diff --git a/audio/aidl/common/Android.bp b/audio/aidl/common/Android.bp
index 4c6a74e..85ece3b 100644
--- a/audio/aidl/common/Android.bp
+++ b/audio/aidl/common/Android.bp
@@ -45,8 +45,8 @@
     name: "libaudioaidlranges",
     host_supported: true,
     vendor_available: true,
-    static_libs: [
-        "android.hardware.audio.effect-V1-ndk",
+    defaults: [
+        "latest_android_hardware_audio_effect_ndk_shared",
     ],
     export_include_dirs: ["include"],
     header_libs: ["libaudioaidl_headers"],
@@ -59,8 +59,10 @@
     name: "libaudioaidlcommon_test",
     host_supported: true,
     vendor_available: true,
+    defaults: [
+        "latest_android_media_audio_common_types_ndk_static",
+    ],
     static_libs: [
-        "android.media.audio.common.types-V1-ndk",
         "libaudioaidlcommon",
     ],
     shared_libs: [
diff --git a/audio/aidl/common/include/Utils.h b/audio/aidl/common/include/Utils.h
index 3b08de7..ef312d5 100644
--- a/audio/aidl/common/include/Utils.h
+++ b/audio/aidl/common/include/Utils.h
@@ -174,4 +174,12 @@
     return result;
 }
 
+constexpr int32_t frameCountFromDurationUs(long durationUs, int32_t sampleRateHz) {
+    return (static_cast<long long>(durationUs) * sampleRateHz) / 1000000LL;
+}
+
+constexpr int32_t frameCountFromDurationMs(int32_t durationMs, int32_t sampleRateHz) {
+    return frameCountFromDurationUs(durationMs * 1000, sampleRateHz);
+}
+
 }  // namespace aidl::android::hardware::audio::common
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index bb8d76f..11bd7d3 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -12,6 +12,7 @@
     vendor: true,
     shared_libs: [
         "libalsautilsv2",
+        "libaudio_aidl_conversion_common_ndk",
         "libaudioaidlcommon",
         "libaudioutils",
         "libbase",
@@ -19,6 +20,8 @@
         "libcutils",
         "libfmq",
         "libnbaio_mono",
+        "liblog",
+        "libmedia_helper",
         "libstagefright_foundation",
         "libtinyalsav2",
         "libutils",
@@ -31,28 +34,8 @@
         "libaudioaidl_headers",
         "libxsdc-utils",
     ],
-}
-
-cc_library {
-    name: "libaudioservicesounddoseimpl",
-    vendor: true,
-    defaults: [
-        "latest_android_media_audio_common_types_ndk_shared",
-        "latest_android_hardware_audio_core_sounddose_ndk_shared",
-        "latest_android_hardware_audio_sounddose_ndk_shared",
-    ],
-    export_include_dirs: ["include"],
-    srcs: [
-        "SoundDose.cpp",
-    ],
-    shared_libs: [
-        "libbase",
-        "libbinder_ndk",
-        "libcutils",
-        "libutils",
-    ],
-    visibility: [
-        "//hardware/interfaces/audio/aidl/sounddose/default",
+    cflags: [
+        "-DBACKEND_NDK",
     ],
 }
 
@@ -63,6 +46,7 @@
         "latest_android_media_audio_common_types_ndk_shared",
         "latest_android_hardware_audio_core_ndk_shared",
         "latest_android_hardware_audio_core_sounddose_ndk_shared",
+        "latest_android_hardware_bluetooth_audio_ndk_shared",
     ],
     export_include_dirs: ["include"],
     srcs: [
@@ -78,6 +62,7 @@
         "Stream.cpp",
         "StreamSwitcher.cpp",
         "Telephony.cpp",
+        "XsdcConversion.cpp",
         "alsa/Mixer.cpp",
         "alsa/ModuleAlsa.cpp",
         "alsa/StreamAlsa.cpp",
@@ -109,10 +94,12 @@
         "audio_policy_engine_configuration_aidl_default",
     ],
     shared_libs: [
-        "android.hardware.bluetooth.audio-V3-ndk",
         "libaudio_aidl_conversion_common_ndk",
+        "libaudioutils",
         "libbluetooth_audio_session_aidl",
+        "liblog",
         "libmedia_helper",
+        "libmediautils_vendor",
         "libstagefright_foundation",
     ],
     export_shared_lib_headers: [
@@ -134,17 +121,18 @@
     vintf_fragments: ["android.hardware.audio.service-aidl.xml"],
     defaults: [
         "aidlaudioservice_defaults",
-        "latest_android_media_audio_common_types_ndk_shared",
         "latest_android_hardware_audio_core_sounddose_ndk_shared",
         "latest_android_hardware_audio_core_ndk_shared",
+        "latest_android_hardware_bluetooth_audio_ndk_shared",
+        "latest_android_media_audio_common_types_ndk_shared",
     ],
     static_libs: [
         "libaudioserviceexampleimpl",
     ],
     shared_libs: [
-        "android.hardware.bluetooth.audio-V3-ndk",
         "libaudio_aidl_conversion_common_ndk",
         "libbluetooth_audio_session_aidl",
+        "liblog",
         "libmedia_helper",
         "libstagefright_foundation",
     ],
@@ -172,6 +160,7 @@
         "libbase",
         "libbinder_ndk",
         "libcutils",
+        "libfmq",
         "libmedia_helper",
         "libstagefright_foundation",
         "libutils",
@@ -184,9 +173,11 @@
     ],
     generated_sources: [
         "audio_policy_configuration_aidl_default",
+        "audio_policy_engine_configuration_aidl_default",
     ],
     generated_headers: [
         "audio_policy_configuration_aidl_default",
+        "audio_policy_engine_configuration_aidl_default",
     ],
     srcs: [
         "AudioPolicyConfigXmlConverter.cpp",
diff --git a/audio/aidl/default/AudioPolicyConfigXmlConverter.cpp b/audio/aidl/default/AudioPolicyConfigXmlConverter.cpp
index 7452c8e..2f1282a 100644
--- a/audio/aidl/default/AudioPolicyConfigXmlConverter.cpp
+++ b/audio/aidl/default/AudioPolicyConfigXmlConverter.cpp
@@ -30,6 +30,7 @@
 
 #include "core-impl/AidlConversionXsdc.h"
 #include "core-impl/AudioPolicyConfigXmlConverter.h"
+#include "core-impl/XsdcConversion.h"
 
 using aidl::android::media::audio::common::AudioFormatDescription;
 using aidl::android::media::audio::common::AudioHalEngineConfig;
@@ -37,60 +38,39 @@
 using aidl::android::media::audio::common::AudioHalVolumeGroup;
 using aidl::android::media::audio::common::AudioStreamType;
 
-namespace xsd = android::audio::policy::configuration;
+namespace ap_xsd = android::audio::policy::configuration;
 
 namespace aidl::android::hardware::audio::core::internal {
 
 static const int kDefaultVolumeIndexMin = 0;
 static const int kDefaultVolumeIndexMax = 100;
 static const int KVolumeIndexDeferredToAudioService = -1;
-/**
- * Valid curve points take the form "<index>,<attenuationMb>", where the index
- * must be in the range [0,100]. kInvalidCurvePointIndex is used to indicate
- * that a point was formatted incorrectly (e.g. if a vendor accidentally typed a
- * '.' instead of a ',' in their XML) -- using such a curve point will result in
- * failed VTS tests.
- */
-static const int8_t kInvalidCurvePointIndex = -1;
 
-AudioHalVolumeCurve::CurvePoint AudioPolicyConfigXmlConverter::convertCurvePointToAidl(
-        const std::string& xsdcCurvePoint) {
-    AudioHalVolumeCurve::CurvePoint aidlCurvePoint{};
-    if (sscanf(xsdcCurvePoint.c_str(), "%" SCNd8 ",%d", &aidlCurvePoint.index,
-               &aidlCurvePoint.attenuationMb) != 2) {
-        aidlCurvePoint.index = kInvalidCurvePointIndex;
-    }
-    return aidlCurvePoint;
-}
-
-AudioHalVolumeCurve AudioPolicyConfigXmlConverter::convertVolumeCurveToAidl(
-        const xsd::Volume& xsdcVolumeCurve) {
+ConversionResult<AudioHalVolumeCurve> AudioPolicyConfigXmlConverter::convertVolumeCurveToAidl(
+        const ap_xsd::Volume& xsdcVolumeCurve) {
     AudioHalVolumeCurve aidlVolumeCurve;
     aidlVolumeCurve.deviceCategory =
             static_cast<AudioHalVolumeCurve::DeviceCategory>(xsdcVolumeCurve.getDeviceCategory());
     if (xsdcVolumeCurve.hasRef()) {
         if (mVolumesReferenceMap.empty()) {
-            mVolumesReferenceMap = generateReferenceMap<xsd::Volumes, xsd::Reference>(
+            mVolumesReferenceMap = generateReferenceMap<ap_xsd::Volumes, ap_xsd::Reference>(
                     getXsdcConfig()->getVolumes());
         }
-        aidlVolumeCurve.curvePoints =
-                convertCollectionToAidlUnchecked<std::string, AudioHalVolumeCurve::CurvePoint>(
+        aidlVolumeCurve.curvePoints = VALUE_OR_FATAL(
+                (convertCollectionToAidl<std::string, AudioHalVolumeCurve::CurvePoint>(
                         mVolumesReferenceMap.at(xsdcVolumeCurve.getRef()).getPoint(),
-                        std::bind(&AudioPolicyConfigXmlConverter::convertCurvePointToAidl, this,
-                                  std::placeholders::_1));
+                        &convertCurvePointToAidl)));
     } else {
-        aidlVolumeCurve.curvePoints =
-                convertCollectionToAidlUnchecked<std::string, AudioHalVolumeCurve::CurvePoint>(
-                        xsdcVolumeCurve.getPoint(),
-                        std::bind(&AudioPolicyConfigXmlConverter::convertCurvePointToAidl, this,
-                                  std::placeholders::_1));
+        aidlVolumeCurve.curvePoints = VALUE_OR_FATAL(
+                (convertCollectionToAidl<std::string, AudioHalVolumeCurve::CurvePoint>(
+                        xsdcVolumeCurve.getPoint(), &convertCurvePointToAidl)));
     }
     return aidlVolumeCurve;
 }
 
-void AudioPolicyConfigXmlConverter::mapStreamToVolumeCurve(const xsd::Volume& xsdcVolumeCurve) {
+void AudioPolicyConfigXmlConverter::mapStreamToVolumeCurve(const ap_xsd::Volume& xsdcVolumeCurve) {
     mStreamToVolumeCurvesMap[xsdcVolumeCurve.getStream()].push_back(
-            convertVolumeCurveToAidl(xsdcVolumeCurve));
+            VALUE_OR_FATAL(convertVolumeCurveToAidl(xsdcVolumeCurve)));
 }
 
 const SurroundSoundConfig& AudioPolicyConfigXmlConverter::getSurroundSoundConfig() {
@@ -109,6 +89,11 @@
     return aidlSurroundSoundConfig;
 }
 
+std::unique_ptr<AudioPolicyConfigXmlConverter::ModuleConfigs>
+AudioPolicyConfigXmlConverter::releaseModuleConfigs() {
+    return std::move(mModuleConfigurations);
+}
+
 const AudioHalEngineConfig& AudioPolicyConfigXmlConverter::getAidlEngineConfig() {
     if (mAidlEngineConfig.volumeGroups.empty() && getXsdcConfig() &&
         getXsdcConfig()->hasVolumes()) {
@@ -160,8 +145,8 @@
 
 void AudioPolicyConfigXmlConverter::mapStreamsToVolumeCurves() {
     if (getXsdcConfig()->hasVolumes()) {
-        for (const xsd::Volumes& xsdcWrapperType : getXsdcConfig()->getVolumes()) {
-            for (const xsd::Volume& xsdcVolume : xsdcWrapperType.getVolume()) {
+        for (const ap_xsd::Volumes& xsdcWrapperType : getXsdcConfig()->getVolumes()) {
+            for (const ap_xsd::Volume& xsdcVolume : xsdcWrapperType.getVolume()) {
                 mapStreamToVolumeCurve(xsdcVolume);
             }
         }
@@ -171,7 +156,7 @@
 void AudioPolicyConfigXmlConverter::addVolumeGroupstoEngineConfig() {
     for (const auto& [xsdcStream, volumeCurves] : mStreamToVolumeCurvesMap) {
         AudioHalVolumeGroup volumeGroup;
-        volumeGroup.name = xsd::toString(xsdcStream);
+        volumeGroup.name = ap_xsd::toString(xsdcStream);
         if (static_cast<int>(xsdcStream) >= AUDIO_STREAM_PUBLIC_CNT) {
             volumeGroup.minIndex = kDefaultVolumeIndexMin;
             volumeGroup.maxIndex = kDefaultVolumeIndexMax;
@@ -190,4 +175,24 @@
         addVolumeGroupstoEngineConfig();
     }
 }
+
+void AudioPolicyConfigXmlConverter::init() {
+    if (!getXsdcConfig()->hasModules()) return;
+    for (const ap_xsd::Modules& xsdcModulesType : getXsdcConfig()->getModules()) {
+        if (!xsdcModulesType.has_module()) continue;
+        for (const ap_xsd::Modules::Module& xsdcModule : xsdcModulesType.get_module()) {
+            // 'primary' in the XML schema used by HIDL is equivalent to 'default' module.
+            const std::string name =
+                    xsdcModule.getName() != "primary" ? xsdcModule.getName() : "default";
+            if (name != "r_submix") {
+                mModuleConfigurations->emplace_back(
+                        name, VALUE_OR_FATAL(convertModuleConfigToAidl(xsdcModule)));
+            } else {
+                // See the note on the 'getRSubmixConfiguration' function.
+                mModuleConfigurations->emplace_back(name, nullptr);
+            }
+        }
+    }
+}
+
 }  // namespace aidl::android::hardware::audio::core::internal
diff --git a/audio/aidl/default/Configuration.cpp b/audio/aidl/default/Configuration.cpp
index 85e4e24..254eb46 100644
--- a/audio/aidl/default/Configuration.cpp
+++ b/audio/aidl/default/Configuration.cpp
@@ -41,8 +41,8 @@
 using aidl::android::media::audio::common::AudioPortMixExt;
 using aidl::android::media::audio::common::AudioProfile;
 using aidl::android::media::audio::common::Int;
-using aidl::android::media::audio::common::MicrophoneInfo;
 using aidl::android::media::audio::common::PcmType;
+using Configuration = aidl::android::hardware::audio::core::Module::Configuration;
 
 namespace aidl::android::hardware::audio::core::internal {
 
@@ -105,15 +105,11 @@
     return port;
 }
 
-static AudioPortConfig createPortConfig(int32_t id, int32_t portId, PcmType pcmType, int32_t layout,
-                                        int32_t sampleRate, int32_t flags, bool isInput,
-                                        const AudioPortExt& ext) {
+static AudioPortConfig createDynamicPortConfig(int32_t id, int32_t portId, int32_t flags,
+                                               bool isInput, const AudioPortExt& ext) {
     AudioPortConfig config;
     config.id = id;
     config.portId = portId;
-    config.sampleRate = Int{.value = sampleRate};
-    config.channelMask = AudioChannelLayout::make<AudioChannelLayout::layoutMask>(layout);
-    config.format = AudioFormatDescription{.type = AudioFormatType::PCM, .pcm = pcmType};
     config.gain = AudioGainConfig();
     config.flags = isInput ? AudioIoFlags::make<AudioIoFlags::Tag::input>(flags)
                            : AudioIoFlags::make<AudioIoFlags::Tag::output>(flags);
@@ -121,6 +117,16 @@
     return config;
 }
 
+static AudioPortConfig createPortConfig(int32_t id, int32_t portId, PcmType pcmType, int32_t layout,
+                                        int32_t sampleRate, int32_t flags, bool isInput,
+                                        const AudioPortExt& ext) {
+    AudioPortConfig config = createDynamicPortConfig(id, portId, flags, isInput, ext);
+    config.sampleRate = Int{.value = sampleRate};
+    config.channelMask = AudioChannelLayout::make<AudioChannelLayout::layoutMask>(layout);
+    config.format = AudioFormatDescription{.type = AudioFormatType::PCM, .pcm = pcmType};
+    return config;
+}
+
 static AudioRoute createRoute(const std::vector<AudioPort>& sources, const AudioPort& sink) {
     AudioRoute route;
     route.sinkPortId = sink.id;
@@ -129,6 +135,22 @@
     return route;
 }
 
+std::vector<AudioProfile> getStandard16And24BitPcmAudioProfiles() {
+    auto createStdPcmAudioProfile = [](const PcmType& pcmType) {
+        return AudioProfile{
+                .format = AudioFormatDescription{.type = AudioFormatType::PCM, .pcm = pcmType},
+                .channelMasks = {AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
+                                         AudioChannelLayout::LAYOUT_MONO),
+                                 AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
+                                         AudioChannelLayout::LAYOUT_STEREO)},
+                .sampleRates = {8000, 11025, 16000, 32000, 44100, 48000}};
+    };
+    return {
+            createStdPcmAudioProfile(PcmType::INT_16_BIT),
+            createStdPcmAudioProfile(PcmType::INT_24_BIT),
+    };
+}
+
 // Primary (default) configuration:
 //
 // Device ports:
@@ -147,8 +169,7 @@
 //  * "primary output", PRIMARY, 1 max open, 1 max active stream
 //    - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
 //  * "primary input", 1 max open, 1 max active stream
-//    - profile PCM 16-bit; MONO, STEREO;
-//        8000, 11025, 16000, 32000, 44100, 48000
+//    - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
 //  * "telephony_tx", 1 max open, 1 max active stream
 //    - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
 //  * "telephony_rx", 1 max open, 1 max active stream
@@ -164,11 +185,11 @@
 //  "FM Tuner" -> "fm_tuner"
 //
 // Initial port configs:
-//  * "Speaker" device port: PCM 16-bit; STEREO; 48000
-//  * "Built-In Mic" device port: PCM 16-bit; MONO; 48000
-//  * "Telephony Tx" device port: PCM 16-bit; MONO; 48000
-//  * "Telephony Rx" device port: PCM 16-bit; MONO; 48000
-//  * "FM Tuner" device port: PCM 16-bit; STEREO; 48000
+//  * "Speaker" device port: dynamic configuration
+//  * "Built-In Mic" device port: dynamic configuration
+//  * "Telephony Tx" device port: dynamic configuration
+//  * "Telephony Rx" device port: dynamic configuration
+//  * "FM Tuner" device port: dynamic configuration
 //
 std::unique_ptr<Configuration> getPrimaryConfiguration() {
     static const Configuration configuration = []() {
@@ -186,9 +207,8 @@
                                            1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE));
         c.ports.push_back(speakerOutDevice);
         c.initialConfigs.push_back(
-                createPortConfig(speakerOutDevice.id, speakerOutDevice.id, PcmType::INT_16_BIT,
-                                 AudioChannelLayout::LAYOUT_STEREO, 48000, 0, false,
-                                 createDeviceExt(AudioDeviceType::OUT_SPEAKER, 0)));
+                createDynamicPortConfig(speakerOutDevice.id, speakerOutDevice.id, 0, false,
+                                        createDeviceExt(AudioDeviceType::OUT_SPEAKER, 0)));
 
         AudioPort micInDevice =
                 createPort(c.nextPortId++, "Built-In Mic", 0, true,
@@ -196,35 +216,31 @@
                                            1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE));
         c.ports.push_back(micInDevice);
         c.initialConfigs.push_back(
-                createPortConfig(micInDevice.id, micInDevice.id, PcmType::INT_16_BIT,
-                                 AudioChannelLayout::LAYOUT_MONO, 48000, 0, true,
-                                 createDeviceExt(AudioDeviceType::IN_MICROPHONE, 0)));
+                createDynamicPortConfig(micInDevice.id, micInDevice.id, 0, true,
+                                        createDeviceExt(AudioDeviceType::IN_MICROPHONE, 0)));
 
         AudioPort telephonyTxOutDevice =
                 createPort(c.nextPortId++, "Telephony Tx", 0, false,
                            createDeviceExt(AudioDeviceType::OUT_TELEPHONY_TX, 0));
         c.ports.push_back(telephonyTxOutDevice);
         c.initialConfigs.push_back(
-                createPortConfig(telephonyTxOutDevice.id, telephonyTxOutDevice.id,
-                                 PcmType::INT_16_BIT, AudioChannelLayout::LAYOUT_MONO, 48000, 0,
-                                 false, createDeviceExt(AudioDeviceType::OUT_TELEPHONY_TX, 0)));
+                createDynamicPortConfig(telephonyTxOutDevice.id, telephonyTxOutDevice.id, 0, false,
+                                        createDeviceExt(AudioDeviceType::OUT_TELEPHONY_TX, 0)));
 
         AudioPort telephonyRxInDevice =
                 createPort(c.nextPortId++, "Telephony Rx", 0, true,
                            createDeviceExt(AudioDeviceType::IN_TELEPHONY_RX, 0));
         c.ports.push_back(telephonyRxInDevice);
         c.initialConfigs.push_back(
-                createPortConfig(telephonyRxInDevice.id, telephonyRxInDevice.id,
-                                 PcmType::INT_16_BIT, AudioChannelLayout::LAYOUT_MONO, 48000, 0,
-                                 true, createDeviceExt(AudioDeviceType::IN_TELEPHONY_RX, 0)));
+                createDynamicPortConfig(telephonyRxInDevice.id, telephonyRxInDevice.id, 0, true,
+                                        createDeviceExt(AudioDeviceType::IN_TELEPHONY_RX, 0)));
 
         AudioPort fmTunerInDevice = createPort(c.nextPortId++, "FM Tuner", 0, true,
                                                createDeviceExt(AudioDeviceType::IN_FM_TUNER, 0));
         c.ports.push_back(fmTunerInDevice);
         c.initialConfigs.push_back(
-                createPortConfig(fmTunerInDevice.id, fmTunerInDevice.id, PcmType::INT_16_BIT,
-                                 AudioChannelLayout::LAYOUT_STEREO, 48000, 0, true,
-                                 createDeviceExt(AudioDeviceType::IN_FM_TUNER, 0)));
+                createDynamicPortConfig(fmTunerInDevice.id, fmTunerInDevice.id, 0, true,
+                                        createDeviceExt(AudioDeviceType::IN_FM_TUNER, 0)));
 
         // Mix ports
 
@@ -237,7 +253,7 @@
         c.ports.push_back(primaryOutMix);
 
         AudioPort primaryInMix =
-                createPort(c.nextPortId++, "primary input", 0, true, createPortMixExt(0, 0));
+                createPort(c.nextPortId++, "primary input", 0, true, createPortMixExt(0, 1));
         primaryInMix.profiles.push_back(
                 createProfile(PcmType::INT_16_BIT,
                               {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
@@ -252,14 +268,14 @@
         c.ports.push_back(telephonyTxOutMix);
 
         AudioPort telephonyRxInMix =
-                createPort(c.nextPortId++, "telephony_rx", 0, true, createPortMixExt(1, 1));
+                createPort(c.nextPortId++, "telephony_rx", 0, true, createPortMixExt(0, 1));
         telephonyRxInMix.profiles.insert(telephonyRxInMix.profiles.begin(),
                                          standardPcmAudioProfiles.begin(),
                                          standardPcmAudioProfiles.end());
         c.ports.push_back(telephonyRxInMix);
 
         AudioPort fmTunerInMix =
-                createPort(c.nextPortId++, "fm_tuner", 0, true, createPortMixExt(1, 1));
+                createPort(c.nextPortId++, "fm_tuner", 0, true, createPortMixExt(0, 1));
         fmTunerInMix.profiles.insert(fmTunerInMix.profiles.begin(),
                                      standardPcmAudioProfiles.begin(),
                                      standardPcmAudioProfiles.end());
@@ -273,13 +289,6 @@
 
         c.portConfigs.insert(c.portConfigs.end(), c.initialConfigs.begin(), c.initialConfigs.end());
 
-        MicrophoneInfo mic;
-        mic.id = "mic";
-        mic.device = micInDevice.ext.get<AudioPortExt::Tag::device>().device;
-        mic.group = 0;
-        mic.indexInTheGroup = 0;
-        c.microphones = std::vector<MicrophoneInfo>{mic};
-
         return c;
     }();
     return std::make_unique<Configuration>(configuration);
@@ -287,13 +296,16 @@
 
 // Note: When transitioning to loading of XML configs, either keep the configuration
 // of the remote submix sources from this static configuration, or update the XML
-// config to match it. There are two reasons for that:
-//   1. The canonical r_submix configuration only lists 'STEREO' and '48000',
+// config to match it. There are several reasons for that:
+//   1. The "Remote Submix In" device is listed in the XML config as "attached",
+//      however in the AIDL scheme its device type has a "virtual" connection.
+//   2. The canonical r_submix configuration only lists 'STEREO' and '48000',
 //      however the framework attempts to open streams for other sample rates
 //      as well. The legacy r_submix implementation allowed that, but libaudiohal@aidl
-//      will not find a mix port to use. Because of that, list all channel
-//      masks and sample rates that the legacy implementation allowed.
-//   2. The legacy implementation had a hard limit on the number of routes (10),
+//      will not find a mix port to use. Because of that, list all sample rates that
+//      the legacy implementation allowed (note that mono was not allowed, the framework
+//      is expected to upmix mono tracks into stereo if needed).
+//   3. The legacy implementation had a hard limit on the number of routes (10),
 //      and this is checked indirectly by AudioPlaybackCaptureTest#testPlaybackCaptureDoS
 //      CTS test. Instead of hardcoding the number of routes, we can use
 //      "maxOpen/ActiveStreamCount" to enforce a similar limit. However, the canonical
@@ -320,9 +332,8 @@
 std::unique_ptr<Configuration> getRSubmixConfiguration() {
     static const Configuration configuration = []() {
         Configuration c;
-        const std::vector<AudioProfile> standardPcmAudioProfiles{
-                createProfile(PcmType::INT_16_BIT,
-                              {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
+        const std::vector<AudioProfile> remoteSubmixPcmAudioProfiles{
+                createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO},
                               {8000, 11025, 16000, 32000, 44100, 48000})};
 
         // Device ports
@@ -331,26 +342,26 @@
                 createPort(c.nextPortId++, "Remote Submix Out", 0, false,
                            createDeviceExt(AudioDeviceType::OUT_SUBMIX, 0,
                                            AudioDeviceDescription::CONNECTION_VIRTUAL));
-        rsubmixOutDevice.profiles = standardPcmAudioProfiles;
         c.ports.push_back(rsubmixOutDevice);
+        c.connectedProfiles[rsubmixOutDevice.id] = remoteSubmixPcmAudioProfiles;
 
         AudioPort rsubmixInDevice =
                 createPort(c.nextPortId++, "Remote Submix In", 0, true,
                            createDeviceExt(AudioDeviceType::IN_SUBMIX, 0,
                                            AudioDeviceDescription::CONNECTION_VIRTUAL));
-        rsubmixInDevice.profiles = standardPcmAudioProfiles;
         c.ports.push_back(rsubmixInDevice);
+        c.connectedProfiles[rsubmixInDevice.id] = remoteSubmixPcmAudioProfiles;
 
         // Mix ports
 
         AudioPort rsubmixOutMix =
                 createPort(c.nextPortId++, "r_submix output", 0, false, createPortMixExt(20, 10));
-        rsubmixOutMix.profiles = standardPcmAudioProfiles;
+        rsubmixOutMix.profiles = remoteSubmixPcmAudioProfiles;
         c.ports.push_back(rsubmixOutMix);
 
         AudioPort rsubmixInMix =
                 createPort(c.nextPortId++, "r_submix input", 0, true, createPortMixExt(20, 10));
-        rsubmixInMix.profiles = standardPcmAudioProfiles;
+        rsubmixInMix.profiles = remoteSubmixPcmAudioProfiles;
         c.ports.push_back(rsubmixInMix);
 
         c.routes.push_back(createRoute({rsubmixOutMix}, rsubmixOutDevice));
@@ -384,7 +395,7 @@
 //  * "usb_device output" -> "USB Headset Out"
 //  * "USB Device In", "USB Headset In" -> "usb_device input"
 //
-// Profiles for device port connected state:
+// Profiles for device port connected state (when simulating connections):
 //  * "USB Device Out", "USB Headset Out":
 //    - profile PCM 16-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
 //    - profile PCM 24-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
@@ -441,7 +452,7 @@
         c.ports.push_back(usbDeviceOutMix);
 
         AudioPort usbDeviceInMix =
-                createPort(c.nextPortId++, "usb_device input", 0, true, createPortMixExt(1, 1));
+                createPort(c.nextPortId++, "usb_device input", 0, true, createPortMixExt(0, 1));
         c.ports.push_back(usbDeviceInMix);
 
         c.routes.push_back(createRoute({usbDeviceOutMix}, usbOutDevice));
@@ -461,9 +472,9 @@
 //  * "Test In", IN_AFE_PROXY
 //    - no profiles specified
 //  * "Wired Headset", OUT_HEADSET
-//    - profile PCM 24-bit; STEREO; 48000
+//    - no profiles specified
 //  * "Wired Headset Mic", IN_HEADSET
-//    - profile PCM 24-bit; MONO; 48000
+//    - no profiles specified
 //
 // Mix ports:
 //  * "test output", 1 max open, 1 max active stream
@@ -486,6 +497,10 @@
 //  * "Test Out" device port: PCM 24-bit; STEREO; 48000
 //  * "Test In" device port: PCM 24-bit; MONO; 48000
 //
+// Profiles for device port connected state (when simulating connections):
+//  * "Wired Headset": dynamic profiles
+//  * "Wired Headset Mic": dynamic profiles
+//
 std::unique_ptr<Configuration> getStubConfiguration() {
     static const Configuration configuration = []() {
         Configuration c;
@@ -504,8 +519,6 @@
                 createPort(c.nextPortId++, "Wired Headset", 0, false,
                            createDeviceExt(AudioDeviceType::OUT_HEADSET, 0,
                                            AudioDeviceDescription::CONNECTION_ANALOG));
-        headsetOutDevice.profiles.push_back(
-                createProfile(PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000}));
         c.ports.push_back(headsetOutDevice);
 
         AudioPort testInDevice = createPort(c.nextPortId++, "Test In", 0, true,
@@ -520,8 +533,6 @@
                 createPort(c.nextPortId++, "Wired Headset Mic", 0, true,
                            createDeviceExt(AudioDeviceType::IN_HEADSET, 0,
                                            AudioDeviceDescription::CONNECTION_ANALOG));
-        headsetInDevice.profiles.push_back(
-                createProfile(PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_MONO}, {48000}));
         c.ports.push_back(headsetInDevice);
 
         // Mix ports
@@ -553,24 +564,24 @@
                               {44100, 48000}));
         c.ports.push_back(compressedOffloadOutMix);
 
-        AudioPort testInMIx =
+        AudioPort testInMix =
                 createPort(c.nextPortId++, "test input", 0, true, createPortMixExt(2, 2));
-        testInMIx.profiles.push_back(
+        testInMix.profiles.push_back(
                 createProfile(PcmType::INT_16_BIT,
                               {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO,
                                AudioChannelLayout::LAYOUT_FRONT_BACK},
                               {8000, 11025, 16000, 22050, 32000, 44100, 48000}));
-        testInMIx.profiles.push_back(
+        testInMix.profiles.push_back(
                 createProfile(PcmType::INT_24_BIT,
                               {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO,
                                AudioChannelLayout::LAYOUT_FRONT_BACK},
                               {8000, 11025, 16000, 22050, 32000, 44100, 48000}));
-        c.ports.push_back(testInMIx);
+        c.ports.push_back(testInMix);
 
         c.routes.push_back(
                 createRoute({testOutMix, testFastOutMix, compressedOffloadOutMix}, testOutDevice));
         c.routes.push_back(createRoute({testOutMix}, headsetOutDevice));
-        c.routes.push_back(createRoute({testInDevice, headsetInDevice}, testInMIx));
+        c.routes.push_back(createRoute({testInDevice, headsetInDevice}, testInMix));
 
         c.portConfigs.insert(c.portConfigs.end(), c.initialConfigs.begin(), c.initialConfigs.end());
 
@@ -603,11 +614,19 @@
 //  "a2dp output" -> "BT A2DP Speaker"
 //  "hearing aid output" -> "BT Hearing Aid Out"
 //
+// Profiles for device port connected state (when simulating connections):
+//  * "BT A2DP Out", "BT A2DP Headphones", "BT A2DP Speaker":
+//    - profile PCM 16-bit; STEREO; 44100, 48000, 88200, 96000
+//  * "BT Hearing Aid Out":
+//    - profile PCM 16-bit; STEREO; 16000, 24000
+//
 std::unique_ptr<Configuration> getBluetoothConfiguration() {
     static const Configuration configuration = []() {
         const std::vector<AudioProfile> standardPcmAudioProfiles = {
                 createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO},
                               {44100, 48000, 88200, 96000})};
+        const std::vector<AudioProfile> hearingAidAudioProfiles = {createProfile(
+                PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {16000, 24000})};
         Configuration c;
 
         // Device ports
@@ -645,8 +664,7 @@
                            createDeviceExt(AudioDeviceType::OUT_HEARING_AID, 0,
                                            AudioDeviceDescription::CONNECTION_WIRELESS));
         c.ports.push_back(btOutHearingAid);
-        c.connectedProfiles[btOutHearingAid.id] = std::vector<AudioProfile>(
-                {createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {16000})});
+        c.connectedProfiles[btOutHearingAid.id] = hearingAidAudioProfiles;
 
         // Mix ports
         AudioPort btOutMix =
@@ -655,8 +673,7 @@
 
         AudioPort btHearingOutMix =
                 createPort(c.nextPortId++, "hearing aid output", 0, false, createPortMixExt(1, 1));
-        btHearingOutMix.profiles.push_back(createProfile(
-                PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {16000, 24000}));
+        btHearingOutMix.profiles = hearingAidAudioProfiles;
         c.ports.push_back(btHearingOutMix);
 
         c.routes.push_back(createRoute({btOutMix}, btOutDevice));
@@ -669,4 +686,19 @@
     return std::make_unique<Configuration>(configuration);
 }
 
+std::unique_ptr<Module::Configuration> getConfiguration(Module::Type moduleType) {
+    switch (moduleType) {
+        case Module::Type::DEFAULT:
+            return getPrimaryConfiguration();
+        case Module::Type::R_SUBMIX:
+            return getRSubmixConfiguration();
+        case Module::Type::STUB:
+            return getStubConfiguration();
+        case Module::Type::USB:
+            return getUsbConfiguration();
+        case Module::Type::BLUETOOTH:
+            return getBluetoothConfiguration();
+    }
+}
+
 }  // namespace aidl::android::hardware::audio::core::internal
diff --git a/audio/aidl/default/EngineConfigXmlConverter.cpp b/audio/aidl/default/EngineConfigXmlConverter.cpp
index 96b555c..631cdce 100644
--- a/audio/aidl/default/EngineConfigXmlConverter.cpp
+++ b/audio/aidl/default/EngineConfigXmlConverter.cpp
@@ -20,11 +20,14 @@
 #include <functional>
 #include <unordered_map>
 
+#define LOG_TAG "AHAL_Config"
 #include <aidl/android/media/audio/common/AudioFlag.h>
 #include <aidl/android/media/audio/common/AudioHalEngineConfig.h>
 #include <aidl/android/media/audio/common/AudioProductStrategyType.h>
+#include <android-base/logging.h>
 
 #include "core-impl/EngineConfigXmlConverter.h"
+#include "core-impl/XsdcConversion.h"
 
 using aidl::android::media::audio::common::AudioAttributes;
 using aidl::android::media::audio::common::AudioContentType;
@@ -40,20 +43,13 @@
 using aidl::android::media::audio::common::AudioSource;
 using aidl::android::media::audio::common::AudioStreamType;
 using aidl::android::media::audio::common::AudioUsage;
+using ::android::BAD_VALUE;
+using ::android::base::unexpected;
 
-namespace xsd = android::audio::policy::engine::configuration;
+namespace eng_xsd = android::audio::policy::engine::configuration;
 
 namespace aidl::android::hardware::audio::core::internal {
 
-/**
- * Valid curve points take the form "<index>,<attenuationMb>", where the index
- * must be in the range [0,100]. kInvalidCurvePointIndex is used to indicate
- * that a point was formatted incorrectly (e.g. if a vendor accidentally typed a
- * '.' instead of a ',' in their XML)-- using such a curve point will result in
- * failed VTS tests.
- */
-static const int8_t kInvalidCurvePointIndex = -1;
-
 void EngineConfigXmlConverter::initProductStrategyMap() {
 #define STRATEGY_ENTRY(name) {"STRATEGY_" #name, static_cast<int>(AudioProductStrategyType::name)}
 
@@ -68,7 +64,7 @@
 #undef STRATEGY_ENTRY
 }
 
-int EngineConfigXmlConverter::convertProductStrategyNameToAidl(
+ConversionResult<int> EngineConfigXmlConverter::convertProductStrategyNameToAidl(
         const std::string& xsdcProductStrategyName) {
     const auto [it, success] = mProductStrategyMap.insert(
             std::make_pair(xsdcProductStrategyName, mNextVendorStrategy));
@@ -85,12 +81,12 @@
             (attributes.tags.empty()));
 }
 
-AudioAttributes EngineConfigXmlConverter::convertAudioAttributesToAidl(
-        const xsd::AttributesType& xsdcAudioAttributes) {
+ConversionResult<AudioAttributes> EngineConfigXmlConverter::convertAudioAttributesToAidl(
+        const eng_xsd::AttributesType& xsdcAudioAttributes) {
     if (xsdcAudioAttributes.hasAttributesRef()) {
         if (mAttributesReferenceMap.empty()) {
             mAttributesReferenceMap =
-                    generateReferenceMap<xsd::AttributesRef, xsd::AttributesRefType>(
+                    generateReferenceMap<eng_xsd::AttributesRef, eng_xsd::AttributesRefType>(
                             getXsdcConfig()->getAttributesRef());
         }
         return convertAudioAttributesToAidl(
@@ -111,16 +107,16 @@
                 static_cast<AudioSource>(xsdcAudioAttributes.getFirstSource()->getValue());
     }
     if (xsdcAudioAttributes.hasFlags()) {
-        std::vector<xsd::FlagType> xsdcFlagTypeVec =
+        std::vector<eng_xsd::FlagType> xsdcFlagTypeVec =
                 xsdcAudioAttributes.getFirstFlags()->getValue();
-        for (const xsd::FlagType& xsdcFlagType : xsdcFlagTypeVec) {
-            if (xsdcFlagType != xsd::FlagType::AUDIO_FLAG_NONE) {
+        for (const eng_xsd::FlagType& xsdcFlagType : xsdcFlagTypeVec) {
+            if (xsdcFlagType != eng_xsd::FlagType::AUDIO_FLAG_NONE) {
                 aidlAudioAttributes.flags |= 1 << (static_cast<int>(xsdcFlagType) - 1);
             }
         }
     }
     if (xsdcAudioAttributes.hasBundle()) {
-        const xsd::BundleType* xsdcBundle = xsdcAudioAttributes.getFirstBundle();
+        const eng_xsd::BundleType* xsdcBundle = xsdcAudioAttributes.getFirstBundle();
         aidlAudioAttributes.tags[0] = xsdcBundle->getKey() + "=" + xsdcBundle->getValue();
     }
     if (isDefaultAudioAttributes(aidlAudioAttributes)) {
@@ -129,53 +125,54 @@
     return aidlAudioAttributes;
 }
 
-AudioHalAttributesGroup EngineConfigXmlConverter::convertAttributesGroupToAidl(
-        const xsd::AttributesGroup& xsdcAttributesGroup) {
+ConversionResult<AudioHalAttributesGroup> EngineConfigXmlConverter::convertAttributesGroupToAidl(
+        const eng_xsd::AttributesGroup& xsdcAttributesGroup) {
     AudioHalAttributesGroup aidlAttributesGroup;
     static const int kStreamTypeEnumOffset =
-            static_cast<int>(xsd::Stream::AUDIO_STREAM_VOICE_CALL) -
+            static_cast<int>(eng_xsd::Stream::AUDIO_STREAM_VOICE_CALL) -
             static_cast<int>(AudioStreamType::VOICE_CALL);
     aidlAttributesGroup.streamType = static_cast<AudioStreamType>(
             static_cast<int>(xsdcAttributesGroup.getStreamType()) - kStreamTypeEnumOffset);
     aidlAttributesGroup.volumeGroupName = xsdcAttributesGroup.getVolumeGroup();
     if (xsdcAttributesGroup.hasAttributes_optional()) {
         aidlAttributesGroup.attributes =
-                convertCollectionToAidlUnchecked<xsd::AttributesType, AudioAttributes>(
+                VALUE_OR_FATAL((convertCollectionToAidl<eng_xsd::AttributesType, AudioAttributes>(
                         xsdcAttributesGroup.getAttributes_optional(),
                         std::bind(&EngineConfigXmlConverter::convertAudioAttributesToAidl, this,
-                                  std::placeholders::_1));
+                                  std::placeholders::_1))));
     } else if (xsdcAttributesGroup.hasContentType_optional() ||
                xsdcAttributesGroup.hasUsage_optional() ||
                xsdcAttributesGroup.hasSource_optional() ||
                xsdcAttributesGroup.hasFlags_optional() ||
                xsdcAttributesGroup.hasBundle_optional()) {
-        aidlAttributesGroup.attributes.push_back(convertAudioAttributesToAidl(xsd::AttributesType(
-                xsdcAttributesGroup.getContentType_optional(),
-                xsdcAttributesGroup.getUsage_optional(), xsdcAttributesGroup.getSource_optional(),
-                xsdcAttributesGroup.getFlags_optional(), xsdcAttributesGroup.getBundle_optional(),
-                std::nullopt)));
+        aidlAttributesGroup.attributes.push_back(VALUE_OR_FATAL(convertAudioAttributesToAidl(
+                eng_xsd::AttributesType(xsdcAttributesGroup.getContentType_optional(),
+                                        xsdcAttributesGroup.getUsage_optional(),
+                                        xsdcAttributesGroup.getSource_optional(),
+                                        xsdcAttributesGroup.getFlags_optional(),
+                                        xsdcAttributesGroup.getBundle_optional(), std::nullopt))));
 
     } else {
-        // do nothing;
-        // TODO: check if this is valid or if we should treat as an error.
-        // Currently, attributes are not mandatory in schema, but an AttributesGroup
-        // without attributes does not make much sense.
+        LOG(ERROR) << __func__ << " Review Audio Policy config: no audio attributes provided for "
+                   << aidlAttributesGroup.toString();
+        return unexpected(BAD_VALUE);
     }
     return aidlAttributesGroup;
 }
 
-AudioHalProductStrategy EngineConfigXmlConverter::convertProductStrategyToAidl(
-        const xsd::ProductStrategies::ProductStrategy& xsdcProductStrategy) {
+ConversionResult<AudioHalProductStrategy> EngineConfigXmlConverter::convertProductStrategyToAidl(
+        const eng_xsd::ProductStrategies::ProductStrategy& xsdcProductStrategy) {
     AudioHalProductStrategy aidlProductStrategy;
 
-    aidlProductStrategy.id = convertProductStrategyNameToAidl(xsdcProductStrategy.getName());
+    aidlProductStrategy.id =
+            VALUE_OR_FATAL(convertProductStrategyNameToAidl(xsdcProductStrategy.getName()));
 
     if (xsdcProductStrategy.hasAttributesGroup()) {
-        aidlProductStrategy.attributesGroups =
-                convertCollectionToAidlUnchecked<xsd::AttributesGroup, AudioHalAttributesGroup>(
+        aidlProductStrategy.attributesGroups = VALUE_OR_FATAL(
+                (convertCollectionToAidl<eng_xsd::AttributesGroup, AudioHalAttributesGroup>(
                         xsdcProductStrategy.getAttributesGroup(),
                         std::bind(&EngineConfigXmlConverter::convertAttributesGroupToAidl, this,
-                                  std::placeholders::_1));
+                                  std::placeholders::_1))));
     }
     if ((mDefaultProductStrategyId != std::nullopt) && (mDefaultProductStrategyId.value() == -1)) {
         mDefaultProductStrategyId = aidlProductStrategy.id;
@@ -183,82 +180,42 @@
     return aidlProductStrategy;
 }
 
-AudioHalVolumeCurve::CurvePoint EngineConfigXmlConverter::convertCurvePointToAidl(
-        const std::string& xsdcCurvePoint) {
-    AudioHalVolumeCurve::CurvePoint aidlCurvePoint{};
-    if (sscanf(xsdcCurvePoint.c_str(), "%" SCNd8 ",%d", &aidlCurvePoint.index,
-               &aidlCurvePoint.attenuationMb) != 2) {
-        aidlCurvePoint.index = kInvalidCurvePointIndex;
-    }
-    return aidlCurvePoint;
-}
-
-AudioHalVolumeCurve EngineConfigXmlConverter::convertVolumeCurveToAidl(
-        const xsd::Volume& xsdcVolumeCurve) {
+ConversionResult<AudioHalVolumeCurve> EngineConfigXmlConverter::convertVolumeCurveToAidl(
+        const eng_xsd::Volume& xsdcVolumeCurve) {
     AudioHalVolumeCurve aidlVolumeCurve;
     aidlVolumeCurve.deviceCategory =
             static_cast<AudioHalVolumeCurve::DeviceCategory>(xsdcVolumeCurve.getDeviceCategory());
     if (xsdcVolumeCurve.hasRef()) {
         if (mVolumesReferenceMap.empty()) {
-            mVolumesReferenceMap = generateReferenceMap<xsd::VolumesType, xsd::VolumeRef>(
+            mVolumesReferenceMap = generateReferenceMap<eng_xsd::VolumesType, eng_xsd::VolumeRef>(
                     getXsdcConfig()->getVolumes());
         }
-        aidlVolumeCurve.curvePoints =
-                convertCollectionToAidlUnchecked<std::string, AudioHalVolumeCurve::CurvePoint>(
+        aidlVolumeCurve.curvePoints = VALUE_OR_FATAL(
+                (convertCollectionToAidl<std::string, AudioHalVolumeCurve::CurvePoint>(
                         mVolumesReferenceMap.at(xsdcVolumeCurve.getRef()).getPoint(),
-                        std::bind(&EngineConfigXmlConverter::convertCurvePointToAidl, this,
-                                  std::placeholders::_1));
+                        &convertCurvePointToAidl)));
     } else {
-        aidlVolumeCurve.curvePoints =
-                convertCollectionToAidlUnchecked<std::string, AudioHalVolumeCurve::CurvePoint>(
-                        xsdcVolumeCurve.getPoint(),
-                        std::bind(&EngineConfigXmlConverter::convertCurvePointToAidl, this,
-                                  std::placeholders::_1));
+        aidlVolumeCurve.curvePoints = VALUE_OR_FATAL(
+                (convertCollectionToAidl<std::string, AudioHalVolumeCurve::CurvePoint>(
+                        xsdcVolumeCurve.getPoint(), &convertCurvePointToAidl)));
     }
     return aidlVolumeCurve;
 }
 
-AudioHalVolumeGroup EngineConfigXmlConverter::convertVolumeGroupToAidl(
-        const xsd::VolumeGroupsType::VolumeGroup& xsdcVolumeGroup) {
+ConversionResult<AudioHalVolumeGroup> EngineConfigXmlConverter::convertVolumeGroupToAidl(
+        const eng_xsd::VolumeGroupsType::VolumeGroup& xsdcVolumeGroup) {
     AudioHalVolumeGroup aidlVolumeGroup;
     aidlVolumeGroup.name = xsdcVolumeGroup.getName();
     aidlVolumeGroup.minIndex = xsdcVolumeGroup.getIndexMin();
     aidlVolumeGroup.maxIndex = xsdcVolumeGroup.getIndexMax();
     aidlVolumeGroup.volumeCurves =
-            convertCollectionToAidlUnchecked<xsd::Volume, AudioHalVolumeCurve>(
+            VALUE_OR_FATAL((convertCollectionToAidl<eng_xsd::Volume, AudioHalVolumeCurve>(
                     xsdcVolumeGroup.getVolume(),
                     std::bind(&EngineConfigXmlConverter::convertVolumeCurveToAidl, this,
-                              std::placeholders::_1));
+                              std::placeholders::_1))));
     return aidlVolumeGroup;
 }
 
-AudioHalCapCriterion EngineConfigXmlConverter::convertCapCriterionToAidl(
-        const xsd::CriterionType& xsdcCriterion) {
-    AudioHalCapCriterion aidlCapCriterion;
-    aidlCapCriterion.name = xsdcCriterion.getName();
-    aidlCapCriterion.criterionTypeName = xsdcCriterion.getType();
-    aidlCapCriterion.defaultLiteralValue = xsdcCriterion.get_default();
-    return aidlCapCriterion;
-}
-
-std::string EngineConfigXmlConverter::convertCriterionTypeValueToAidl(
-        const xsd::ValueType& xsdcCriterionTypeValue) {
-    return xsdcCriterionTypeValue.getLiteral();
-}
-
-AudioHalCapCriterionType EngineConfigXmlConverter::convertCapCriterionTypeToAidl(
-        const xsd::CriterionTypeType& xsdcCriterionType) {
-    AudioHalCapCriterionType aidlCapCriterionType;
-    aidlCapCriterionType.name = xsdcCriterionType.getName();
-    aidlCapCriterionType.isInclusive = !(static_cast<bool>(xsdcCriterionType.getType()));
-    aidlCapCriterionType.values =
-            convertWrappedCollectionToAidlUnchecked<xsd::ValuesType, xsd::ValueType, std::string>(
-                    xsdcCriterionType.getValues(), &xsd::ValuesType::getValue,
-                    std::bind(&EngineConfigXmlConverter::convertCriterionTypeValueToAidl, this,
-                              std::placeholders::_1));
-    return aidlCapCriterionType;
-}
-
 AudioHalEngineConfig& EngineConfigXmlConverter::getAidlEngineConfig() {
     return mAidlEngineConfig;
 }
@@ -266,39 +223,42 @@
 void EngineConfigXmlConverter::init() {
     initProductStrategyMap();
     if (getXsdcConfig()->hasProductStrategies()) {
-        mAidlEngineConfig.productStrategies =
-                convertWrappedCollectionToAidlUnchecked<xsd::ProductStrategies,
-                                                        xsd::ProductStrategies::ProductStrategy,
-                                                        AudioHalProductStrategy>(
+        mAidlEngineConfig.productStrategies = VALUE_OR_FATAL(
+                (convertWrappedCollectionToAidl<eng_xsd::ProductStrategies,
+                                                eng_xsd::ProductStrategies::ProductStrategy,
+                                                AudioHalProductStrategy>(
                         getXsdcConfig()->getProductStrategies(),
-                        &xsd::ProductStrategies::getProductStrategy,
+                        &eng_xsd::ProductStrategies::getProductStrategy,
                         std::bind(&EngineConfigXmlConverter::convertProductStrategyToAidl, this,
-                                  std::placeholders::_1));
+                                  std::placeholders::_1))));
         if (mDefaultProductStrategyId) {
             mAidlEngineConfig.defaultProductStrategyId = mDefaultProductStrategyId.value();
         }
     }
     if (getXsdcConfig()->hasVolumeGroups()) {
-        mAidlEngineConfig.volumeGroups = convertWrappedCollectionToAidlUnchecked<
-                xsd::VolumeGroupsType, xsd::VolumeGroupsType::VolumeGroup, AudioHalVolumeGroup>(
-                getXsdcConfig()->getVolumeGroups(), &xsd::VolumeGroupsType::getVolumeGroup,
-                std::bind(&EngineConfigXmlConverter::convertVolumeGroupToAidl, this,
-                          std::placeholders::_1));
+        mAidlEngineConfig.volumeGroups = VALUE_OR_FATAL(
+                (convertWrappedCollectionToAidl<eng_xsd::VolumeGroupsType,
+                                                eng_xsd::VolumeGroupsType::VolumeGroup,
+                                                AudioHalVolumeGroup>(
+                        getXsdcConfig()->getVolumeGroups(),
+                        &eng_xsd::VolumeGroupsType::getVolumeGroup,
+                        std::bind(&EngineConfigXmlConverter::convertVolumeGroupToAidl, this,
+                                  std::placeholders::_1))));
     }
     if (getXsdcConfig()->hasCriteria() && getXsdcConfig()->hasCriterion_types()) {
         AudioHalEngineConfig::CapSpecificConfig capSpecificConfig;
-        capSpecificConfig.criteria =
-                convertWrappedCollectionToAidlUnchecked<xsd::CriteriaType, xsd::CriterionType,
-                                                        AudioHalCapCriterion>(
-                        getXsdcConfig()->getCriteria(), &xsd::CriteriaType::getCriterion,
-                        std::bind(&EngineConfigXmlConverter::convertCapCriterionToAidl, this,
-                                  std::placeholders::_1));
-        capSpecificConfig.criterionTypes = convertWrappedCollectionToAidlUnchecked<
-                xsd::CriterionTypesType, xsd::CriterionTypeType, AudioHalCapCriterionType>(
-                getXsdcConfig()->getCriterion_types(), &xsd::CriterionTypesType::getCriterion_type,
-                std::bind(&EngineConfigXmlConverter::convertCapCriterionTypeToAidl, this,
-                          std::placeholders::_1));
-        mAidlEngineConfig.capSpecificConfig = capSpecificConfig;
+        capSpecificConfig.criteria = VALUE_OR_FATAL(
+                (convertWrappedCollectionToAidl<eng_xsd::CriteriaType, eng_xsd::CriterionType,
+                                                AudioHalCapCriterion>(
+                        getXsdcConfig()->getCriteria(), &eng_xsd::CriteriaType::getCriterion,
+                        &convertCapCriterionToAidl)));
+        capSpecificConfig.criterionTypes =
+                VALUE_OR_FATAL((convertWrappedCollectionToAidl<eng_xsd::CriterionTypesType,
+                                                               eng_xsd::CriterionTypeType,
+                                                               AudioHalCapCriterionType>(
+                        getXsdcConfig()->getCriterion_types(),
+                        &eng_xsd::CriterionTypesType::getCriterion_type,
+                        &convertCapCriterionTypeToAidl)));
     }
 }
 }  // namespace aidl::android::hardware::audio::core::internal
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index 3117134..66c2169 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -18,13 +18,13 @@
 #include <set>
 
 #define LOG_TAG "AHAL_Module"
-#include <Utils.h>
 #include <aidl/android/media/audio/common/AudioInputFlags.h>
 #include <aidl/android/media/audio/common/AudioOutputFlags.h>
 #include <android-base/logging.h>
 #include <android/binder_ibinder_platform.h>
 #include <error/expected_utils.h>
 
+#include "core-impl/Configuration.h"
 #include "core-impl/Module.h"
 #include "core-impl/ModuleBluetooth.h"
 #include "core-impl/ModulePrimary.h"
@@ -34,6 +34,7 @@
 #include "core-impl/SoundDose.h"
 #include "core-impl/utils.h"
 
+using aidl::android::hardware::audio::common::frameCountFromDurationMs;
 using aidl::android::hardware::audio::common::getFrameSizeInBytes;
 using aidl::android::hardware::audio::common::isBitPositionFlagSet;
 using aidl::android::hardware::audio::common::isValidAudioMode;
@@ -42,6 +43,7 @@
 using aidl::android::hardware::audio::core::sounddose::ISoundDose;
 using aidl::android::media::audio::common::AudioChannelLayout;
 using aidl::android::media::audio::common::AudioDevice;
+using aidl::android::media::audio::common::AudioDeviceType;
 using aidl::android::media::audio::common::AudioFormatDescription;
 using aidl::android::media::audio::common::AudioFormatType;
 using aidl::android::media::audio::common::AudioInputFlags;
@@ -65,32 +67,56 @@
 
 namespace {
 
+inline bool hasDynamicChannelMasks(const std::vector<AudioChannelLayout>& channelMasks) {
+    return channelMasks.empty() ||
+           std::all_of(channelMasks.begin(), channelMasks.end(),
+                       [](const auto& channelMask) { return channelMask == AudioChannelLayout{}; });
+}
+
+inline bool hasDynamicFormat(const AudioFormatDescription& format) {
+    return format == AudioFormatDescription{};
+}
+
+inline bool hasDynamicSampleRates(const std::vector<int32_t>& sampleRates) {
+    return sampleRates.empty() ||
+           std::all_of(sampleRates.begin(), sampleRates.end(),
+                       [](const auto& sampleRate) { return sampleRate == 0; });
+}
+
+inline bool isDynamicProfile(const AudioProfile& profile) {
+    return hasDynamicFormat(profile.format) || hasDynamicChannelMasks(profile.channelMasks) ||
+           hasDynamicSampleRates(profile.sampleRates);
+}
+
+bool hasDynamicProfilesOnly(const std::vector<AudioProfile>& profiles) {
+    if (profiles.empty()) return true;
+    return std::all_of(profiles.begin(), profiles.end(), isDynamicProfile);
+}
+
+// Note: does not assign an ID to the config.
 bool generateDefaultPortConfig(const AudioPort& port, AudioPortConfig* config) {
+    const bool allowDynamicConfig = port.ext.getTag() == AudioPortExt::device;
     *config = {};
     config->portId = port.id;
-    if (port.profiles.empty()) {
-        LOG(ERROR) << __func__ << ": port " << port.id << " has no profiles";
-        return false;
+    for (const auto& profile : port.profiles) {
+        if (isDynamicProfile(profile)) continue;
+        config->format = profile.format;
+        config->channelMask = *profile.channelMasks.begin();
+        config->sampleRate = Int{.value = *profile.sampleRates.begin()};
+        config->flags = port.flags;
+        config->ext = port.ext;
+        return true;
     }
-    const auto& profile = port.profiles.begin();
-    config->format = profile->format;
-    if (profile->channelMasks.empty()) {
-        LOG(ERROR) << __func__ << ": the first profile in port " << port.id
-                   << " has no channel masks";
-        return false;
+    if (allowDynamicConfig) {
+        config->format = AudioFormatDescription{};
+        config->channelMask = AudioChannelLayout{};
+        config->sampleRate = Int{.value = 0};
+        config->flags = port.flags;
+        config->ext = port.ext;
+        return true;
     }
-    config->channelMask = *profile->channelMasks.begin();
-    if (profile->sampleRates.empty()) {
-        LOG(ERROR) << __func__ << ": the first profile in port " << port.id
-                   << " has no sample rates";
-        return false;
-    }
-    Int sampleRate;
-    sampleRate.value = *profile->sampleRates.begin();
-    config->sampleRate = sampleRate;
-    config->flags = port.flags;
-    config->ext = port.ext;
-    return true;
+    LOG(ERROR) << __func__ << ": port " << port.id << " only has dynamic profiles";
+    return false;
 }
 
 bool findAudioProfile(const AudioPort& port, const AudioFormatDescription& format,
@@ -108,21 +134,36 @@
 }  // namespace
 
 // static
-std::shared_ptr<Module> Module::createInstance(Type type) {
+std::shared_ptr<Module> Module::createInstance(Type type, std::unique_ptr<Configuration>&& config) {
     switch (type) {
         case Type::DEFAULT:
-            return ndk::SharedRefBase::make<ModulePrimary>();
+            return ndk::SharedRefBase::make<ModulePrimary>(std::move(config));
         case Type::R_SUBMIX:
-            return ndk::SharedRefBase::make<ModuleRemoteSubmix>();
+            return ndk::SharedRefBase::make<ModuleRemoteSubmix>(std::move(config));
         case Type::STUB:
-            return ndk::SharedRefBase::make<ModuleStub>();
+            return ndk::SharedRefBase::make<ModuleStub>(std::move(config));
         case Type::USB:
-            return ndk::SharedRefBase::make<ModuleUsb>();
+            return ndk::SharedRefBase::make<ModuleUsb>(std::move(config));
         case Type::BLUETOOTH:
-            return ndk::SharedRefBase::make<ModuleBluetooth>();
+            return ndk::SharedRefBase::make<ModuleBluetooth>(std::move(config));
     }
 }
 
+// static
+std::optional<Module::Type> Module::typeFromString(const std::string& type) {
+    if (type == "default")
+        return Module::Type::DEFAULT;
+    else if (type == "r_submix")
+        return Module::Type::R_SUBMIX;
+    else if (type == "stub")
+        return Module::Type::STUB;
+    else if (type == "usb")
+        return Module::Type::USB;
+    else if (type == "bluetooth")
+        return Module::Type::BLUETOOTH;
+    return {};
+}
+
 std::ostream& operator<<(std::ostream& os, Module::Type t) {
     switch (t) {
         case Module::Type::DEFAULT:
@@ -144,6 +185,11 @@
     return os;
 }
 
+Module::Module(Type type, std::unique_ptr<Configuration>&& config)
+    : mType(type), mConfig(std::move(config)) {
+    populateConnectedProfiles();
+}
+
 void Module::cleanUpPatch(int32_t patchId) {
     erase_all_values(mPatches, std::set<int32_t>{patchId});
 }
@@ -156,15 +202,17 @@
         LOG(ERROR) << __func__ << ": non-positive buffer size " << in_bufferSizeFrames;
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
-    if (in_bufferSizeFrames < kMinimumStreamBufferSizeFrames) {
-        LOG(ERROR) << __func__ << ": insufficient buffer size " << in_bufferSizeFrames
-                   << ", must be at least " << kMinimumStreamBufferSizeFrames;
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
-    }
     auto& configs = getConfig().portConfigs;
     auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
     // Since this is a private method, it is assumed that
     // validity of the portConfigId has already been checked.
+    const int32_t minimumStreamBufferSizeFrames = calculateBufferSizeFrames(
+            getNominalLatencyMs(*portConfigIt), portConfigIt->sampleRate.value().value);
+    if (in_bufferSizeFrames < minimumStreamBufferSizeFrames) {
+        LOG(ERROR) << __func__ << ": insufficient buffer size " << in_bufferSizeFrames
+                   << ", must be at least " << minimumStreamBufferSizeFrames;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
     const size_t frameSize =
             getFrameSizeInBytes(portConfigIt->format.value(), portConfigIt->channelMask.value());
     if (frameSize == 0) {
@@ -189,14 +237,19 @@
         StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs,
                                               mVendorDebug.forceTransientBurst,
                                               mVendorDebug.forceSynchronousDrain};
+        std::shared_ptr<ISoundDose> soundDose;
+        if (!getSoundDose(&soundDose).isOk()) {
+            LOG(ERROR) << __func__ << ": could not create sound dose instance";
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+        }
         StreamContext temp(
                 std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
                 std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
-                portConfigIt->portId, portConfigIt->format.value(),
-                portConfigIt->channelMask.value(), portConfigIt->sampleRate.value().value, flags,
+                portConfigIt->format.value(), portConfigIt->channelMask.value(),
+                portConfigIt->sampleRate.value().value, flags, getNominalLatencyMs(*portConfigIt),
                 portConfigIt->ext.get<AudioPortExt::mix>().handle,
                 std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
-                asyncCallback, outEventCallback, params);
+                asyncCallback, outEventCallback, mSoundDose.getInstance(), params);
         if (temp.isValid()) {
             *out_context = std::move(temp);
         } else {
@@ -279,6 +332,22 @@
     return ndk::ScopedAStatus::ok();
 }
 
+void Module::populateConnectedProfiles() {
+    Configuration& config = getConfig();
+    for (const AudioPort& port : config.ports) {
+        if (port.ext.getTag() == AudioPortExt::device) {
+            if (auto devicePort = port.ext.get<AudioPortExt::device>();
+                !devicePort.device.type.connection.empty() && port.profiles.empty()) {
+                if (auto connIt = config.connectedProfiles.find(port.id);
+                    connIt == config.connectedProfiles.end()) {
+                    config.connectedProfiles.emplace(
+                            port.id, internal::getStandard16And24BitPcmAudioProfiles());
+                }
+            }
+        }
+    }
+}
+
 template <typename C>
 std::set<int32_t> Module::portIdsFromPortConfigIds(C portConfigIds) {
     std::set<int32_t> result;
@@ -292,35 +361,53 @@
     return result;
 }
 
-std::unique_ptr<internal::Configuration> Module::initializeConfig() {
-    std::unique_ptr<internal::Configuration> config;
-    switch (getType()) {
-        case Type::DEFAULT:
-            config = std::move(internal::getPrimaryConfiguration());
-            break;
-        case Type::R_SUBMIX:
-            config = std::move(internal::getRSubmixConfiguration());
-            break;
-        case Type::STUB:
-            config = std::move(internal::getStubConfiguration());
-            break;
-        case Type::USB:
-            config = std::move(internal::getUsbConfiguration());
-            break;
-        case Type::BLUETOOTH:
-            config = std::move(internal::getBluetoothConfiguration());
-            break;
-    }
-    return config;
+std::unique_ptr<Module::Configuration> Module::initializeConfig() {
+    return internal::getConfiguration(getType());
 }
 
-internal::Configuration& Module::getConfig() {
+int32_t Module::getNominalLatencyMs(const AudioPortConfig&) {
+    // Arbitrary value. Implementations must override this method to provide their actual latency.
+    static constexpr int32_t kLatencyMs = 5;
+    return kLatencyMs;
+}
+
+std::vector<AudioRoute*> Module::getAudioRoutesForAudioPortImpl(int32_t portId) {
+    std::vector<AudioRoute*> result;
+    auto& routes = getConfig().routes;
+    for (auto& r : routes) {
+        const auto& srcs = r.sourcePortIds;
+        if (r.sinkPortId == portId || std::find(srcs.begin(), srcs.end(), portId) != srcs.end()) {
+            result.push_back(&r);
+        }
+    }
+    return result;
+}
+
+Module::Configuration& Module::getConfig() {
     if (!mConfig) {
         mConfig = std::move(initializeConfig());
     }
     return *mConfig;
 }
 
+std::set<int32_t> Module::getRoutableAudioPortIds(int32_t portId,
+                                                  std::vector<AudioRoute*>* routes) {
+    std::vector<AudioRoute*> routesStorage;
+    if (routes == nullptr) {
+        routesStorage = getAudioRoutesForAudioPortImpl(portId);
+        routes = &routesStorage;
+    }
+    std::set<int32_t> result;
+    for (AudioRoute* r : *routes) {
+        if (r->sinkPortId == portId) {
+            result.insert(r->sourcePortIds.begin(), r->sourcePortIds.end());
+        } else {
+            result.insert(r->sinkPortId);
+        }
+    }
+    return result;
+}
+
 void Module::registerPatch(const AudioPatch& patch) {
     auto& configs = getConfig().portConfigs;
     auto do_insert = [&](const std::vector<int32_t>& portConfigIds) {
@@ -340,21 +427,43 @@
 
 ndk::ScopedAStatus Module::updateStreamsConnectedState(const AudioPatch& oldPatch,
                                                        const AudioPatch& newPatch) {
-    // Streams from the old patch need to be disconnected, streams from the new
-    // patch need to be connected. If the stream belongs to both patches, no need
-    // to update it.
+    // Notify streams about the new set of devices they are connected to.
     auto maybeFailure = ndk::ScopedAStatus::ok();
-    std::set<int32_t> idsToDisconnect, idsToConnect, idsToDisconnectOnFailure;
-    idsToDisconnect.insert(oldPatch.sourcePortConfigIds.begin(),
-                           oldPatch.sourcePortConfigIds.end());
-    idsToDisconnect.insert(oldPatch.sinkPortConfigIds.begin(), oldPatch.sinkPortConfigIds.end());
-    idsToConnect.insert(newPatch.sourcePortConfigIds.begin(), newPatch.sourcePortConfigIds.end());
-    idsToConnect.insert(newPatch.sinkPortConfigIds.begin(), newPatch.sinkPortConfigIds.end());
-    std::for_each(idsToDisconnect.begin(), idsToDisconnect.end(), [&](const auto& portConfigId) {
-        if (idsToConnect.count(portConfigId) == 0 && mStreams.count(portConfigId) != 0) {
-            if (auto status = mStreams.setStreamConnectedDevices(portConfigId, {}); status.isOk()) {
+    using Connections =
+            std::map<int32_t /*mixPortConfigId*/, std::set<int32_t /*devicePortConfigId*/>>;
+    Connections oldConnections, newConnections;
+    auto fillConnectionsHelper = [&](Connections& connections,
+                                     const std::vector<int32_t>& mixPortCfgIds,
+                                     const std::vector<int32_t>& devicePortCfgIds) {
+        for (int32_t mixPortCfgId : mixPortCfgIds) {
+            connections[mixPortCfgId].insert(devicePortCfgIds.begin(), devicePortCfgIds.end());
+        }
+    };
+    auto fillConnections = [&](Connections& connections, const AudioPatch& patch) {
+        if (std::find_if(patch.sourcePortConfigIds.begin(), patch.sourcePortConfigIds.end(),
+                         [&](int32_t portConfigId) { return mStreams.count(portConfigId) > 0; }) !=
+            patch.sourcePortConfigIds.end()) {
+            // Sources are mix ports.
+            fillConnectionsHelper(connections, patch.sourcePortConfigIds, patch.sinkPortConfigIds);
+        } else if (std::find_if(patch.sinkPortConfigIds.begin(), patch.sinkPortConfigIds.end(),
+                                [&](int32_t portConfigId) {
+                                    return mStreams.count(portConfigId) > 0;
+                                }) != patch.sinkPortConfigIds.end()) {
+            // Sources are device ports.
+            fillConnectionsHelper(connections, patch.sinkPortConfigIds, patch.sourcePortConfigIds);
+        }  // Otherwise, there are no streams to notify.
+    };
+    fillConnections(oldConnections, oldPatch);
+    fillConnections(newConnections, newPatch);
+
+    std::for_each(oldConnections.begin(), oldConnections.end(), [&](const auto& connectionPair) {
+        const int32_t mixPortConfigId = connectionPair.first;
+        if (auto it = newConnections.find(mixPortConfigId);
+            it == newConnections.end() || it->second != connectionPair.second) {
+            if (auto status = mStreams.setStreamConnectedDevices(mixPortConfigId, {});
+                status.isOk()) {
                 LOG(DEBUG) << "updateStreamsConnectedState: The stream on port config id "
-                           << portConfigId << " has been disconnected";
+                           << mixPortConfigId << " has been disconnected";
             } else {
                 // Disconnection is tricky to roll back, just register a failure.
                 maybeFailure = std::move(status);
@@ -362,23 +471,26 @@
         }
     });
     if (!maybeFailure.isOk()) return maybeFailure;
-    std::for_each(idsToConnect.begin(), idsToConnect.end(), [&](const auto& portConfigId) {
-        if (idsToDisconnect.count(portConfigId) == 0 && mStreams.count(portConfigId) != 0) {
-            const auto connectedDevices = findConnectedDevices(portConfigId);
+    std::set<int32_t> idsToDisconnectOnFailure;
+    std::for_each(newConnections.begin(), newConnections.end(), [&](const auto& connectionPair) {
+        const int32_t mixPortConfigId = connectionPair.first;
+        if (auto it = oldConnections.find(mixPortConfigId);
+            it == oldConnections.end() || it->second != connectionPair.second) {
+            const auto connectedDevices = findConnectedDevices(mixPortConfigId);
             if (connectedDevices.empty()) {
                 // This is important as workers use the vector size to derive the connection status.
                 LOG(FATAL) << "updateStreamsConnectedState: No connected devices found for port "
                               "config id "
-                           << portConfigId;
+                           << mixPortConfigId;
             }
-            if (auto status = mStreams.setStreamConnectedDevices(portConfigId, connectedDevices);
+            if (auto status = mStreams.setStreamConnectedDevices(mixPortConfigId, connectedDevices);
                 status.isOk()) {
                 LOG(DEBUG) << "updateStreamsConnectedState: The stream on port config id "
-                           << portConfigId << " has been connected to: "
+                           << mixPortConfigId << " has been connected to: "
                            << ::android::internal::ToString(connectedDevices);
             } else {
                 maybeFailure = std::move(status);
-                idsToDisconnectOnFailure.insert(portConfigId);
+                idsToDisconnectOnFailure.insert(mixPortConfigId);
             }
         }
     });
@@ -485,37 +597,58 @@
         }
     }
 
-    if (connectedPort.profiles.empty()) {
-        if (!mDebug.simulateDeviceConnections) {
-            RETURN_STATUS_IF_ERROR(populateConnectedDevicePort(&connectedPort));
-        } else {
-            auto& connectedProfiles = getConfig().connectedProfiles;
-            if (auto connectedProfilesIt = connectedProfiles.find(templateId);
-                connectedProfilesIt != connectedProfiles.end()) {
-                connectedPort.profiles = connectedProfilesIt->second;
-            }
+    // Two main cases are considered with regard to the profiles of the connected device port:
+    //
+    //  1. If the template device port has dynamic profiles, and at least one routable mix
+    //     port also has dynamic profiles, it means that after connecting the device, the
+    //     connected device port must have profiles populated with actual capabilities of
+    //     the connected device, and dynamic of routable mix ports will be filled
+    //     according to these capabilities. An example of this case is connection of an
+    //     HDMI or USB device. For USB handled by ADSP, there can be mix ports with static
+    //     profiles, and one dedicated mix port for "hi-fi" playback. The latter is left with
+    //     dynamic profiles so that they can be populated with actual capabilities of
+    //     the connected device.
+    //
+    //  2. If the template device port has dynamic profiles, while all routable mix ports
+    //     have static profiles, it means that after connecting the device, the connected
+    //     device port can be left with dynamic profiles, and profiles of mix ports are
+    //     left untouched. An example of this case is connection of an analog wired
+    //     headset, it should be treated in the same way as a speaker.
+    //
+    //  Yet another possible case is when both the template device port and all routable
+    //  mix ports have static profiles. This is allowed and handled correctly, however, it
+    //  is not very practical, since these profiles are likely duplicates of each other.
+
+    std::vector<AudioRoute*> routesToMixPorts = getAudioRoutesForAudioPortImpl(templateId);
+    std::set<int32_t> routableMixPortIds = getRoutableAudioPortIds(templateId, &routesToMixPorts);
+    if (!mDebug.simulateDeviceConnections) {
+        // Even if the device port has static profiles, the HAL module might need to update
+        // them, or abort the connection process.
+        RETURN_STATUS_IF_ERROR(populateConnectedDevicePort(&connectedPort));
+    } else if (hasDynamicProfilesOnly(connectedPort.profiles)) {
+        auto& connectedProfiles = getConfig().connectedProfiles;
+        if (auto connectedProfilesIt = connectedProfiles.find(templateId);
+            connectedProfilesIt != connectedProfiles.end()) {
+            connectedPort.profiles = connectedProfilesIt->second;
         }
-        if (connectedPort.profiles.empty()) {
-            LOG(ERROR) << __func__
-                       << ": profiles of a connected port still empty after connecting external "
-                          "device "
-                       << connectedPort.toString();
+    }
+    if (hasDynamicProfilesOnly(connectedPort.profiles)) {
+        // Possible case 2. Check if all routable mix ports have static profiles.
+        if (auto dynamicMixPortIt = std::find_if(ports.begin(), ports.end(),
+                                                 [&routableMixPortIds](const auto& p) {
+                                                     return routableMixPortIds.count(p.id) > 0 &&
+                                                            hasDynamicProfilesOnly(p.profiles);
+                                                 });
+            dynamicMixPortIt != ports.end()) {
+            LOG(ERROR) << __func__ << ": connected port only has dynamic profiles after connecting "
+                       << "external device " << connectedPort.toString() << ", and there exist "
+                       << "a routable mix port with dynamic profiles: "
+                       << dynamicMixPortIt->toString();
             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
         }
     }
 
-    for (auto profile : connectedPort.profiles) {
-        if (profile.channelMasks.empty()) {
-            LOG(ERROR) << __func__ << ": the profile " << profile.name << " has no channel masks";
-            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-        }
-        if (profile.sampleRates.empty()) {
-            LOG(ERROR) << __func__ << ": the profile " << profile.name << " has no sample rates";
-            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-        }
-    }
-
-    connectedPort.id = ++getConfig().nextPortId;
+    connectedPort.id = getConfig().nextPortId++;
     auto [connectedPortsIt, _] =
             mConnectedDevicePorts.insert(std::pair(connectedPort.id, std::set<int32_t>()));
     LOG(DEBUG) << __func__ << ": template port " << templateId << " external device connected, "
@@ -523,44 +656,36 @@
     ports.push_back(connectedPort);
     onExternalDeviceConnectionChanged(connectedPort, true /*connected*/);
 
-    std::vector<int32_t> routablePortIds;
+    // For routes where the template port is a source, add the connected port to sources,
+    // otherwise, create a new route by copying from the route for the template port.
     std::vector<AudioRoute> newRoutes;
-    auto& routes = getConfig().routes;
-    for (auto& r : routes) {
-        if (r.sinkPortId == templateId) {
-            AudioRoute newRoute;
-            newRoute.sourcePortIds = r.sourcePortIds;
-            newRoute.sinkPortId = connectedPort.id;
-            newRoute.isExclusive = r.isExclusive;
-            newRoutes.push_back(std::move(newRoute));
-            routablePortIds.insert(routablePortIds.end(), r.sourcePortIds.begin(),
-                                   r.sourcePortIds.end());
+    for (AudioRoute* r : routesToMixPorts) {
+        if (r->sinkPortId == templateId) {
+            newRoutes.push_back(AudioRoute{.sourcePortIds = r->sourcePortIds,
+                                           .sinkPortId = connectedPort.id,
+                                           .isExclusive = r->isExclusive});
         } else {
-            auto& srcs = r.sourcePortIds;
-            if (std::find(srcs.begin(), srcs.end(), templateId) != srcs.end()) {
-                srcs.push_back(connectedPort.id);
-                routablePortIds.push_back(r.sinkPortId);
-            }
+            r->sourcePortIds.push_back(connectedPort.id);
         }
     }
+    auto& routes = getConfig().routes;
     routes.insert(routes.end(), newRoutes.begin(), newRoutes.end());
 
-    // Note: this is a simplistic approach assuming that a mix port can only be populated
-    // from a single device port. Implementing support for stuffing dynamic profiles with a superset
-    // of all profiles from all routable dynamic device ports would be more involved.
-    for (const auto mixPortId : routablePortIds) {
-        auto portsIt = findById<AudioPort>(ports, mixPortId);
-        if (portsIt != ports.end()) {
-            if (portsIt->profiles.empty()) {
-                portsIt->profiles = connectedPort.profiles;
-                connectedPortsIt->second.insert(portsIt->id);
+    if (!hasDynamicProfilesOnly(connectedPort.profiles) && !routableMixPortIds.empty()) {
+        // Note: this is a simplistic approach assuming that a mix port can only be populated
+        // from a single device port. Implementing support for stuffing dynamic profiles with
+        // a superset of all profiles from all routable dynamic device ports would be more involved.
+        for (auto& port : ports) {
+            if (routableMixPortIds.count(port.id) == 0) continue;
+            if (hasDynamicProfilesOnly(port.profiles)) {
+                port.profiles = connectedPort.profiles;
+                connectedPortsIt->second.insert(port.id);
             } else {
-                // Check if profiles are non empty because they were populated by
-                // a previous connection. Otherwise, it means that they are not empty because
-                // the mix port has static profiles.
-                for (const auto cp : mConnectedDevicePorts) {
-                    if (cp.second.count(portsIt->id) > 0) {
-                        connectedPortsIt->second.insert(portsIt->id);
+                // Check if profiles are not all dynamic because they were populated by
+                // a previous connection. Otherwise, it means that they are actually static.
+                for (const auto& cp : mConnectedDevicePorts) {
+                    if (cp.second.count(port.id) > 0) {
+                        connectedPortsIt->second.insert(port.id);
                         break;
                     }
                 }
@@ -680,13 +805,9 @@
         LOG(ERROR) << __func__ << ": port id " << in_portId << " not found";
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
-    auto& routes = getConfig().routes;
-    std::copy_if(routes.begin(), routes.end(), std::back_inserter(*_aidl_return),
-                 [&](const auto& r) {
-                     const auto& srcs = r.sourcePortIds;
-                     return r.sinkPortId == in_portId ||
-                            std::find(srcs.begin(), srcs.end(), in_portId) != srcs.end();
-                 });
+    std::vector<AudioRoute*> routes = getAudioRoutesForAudioPortImpl(in_portId);
+    std::transform(routes.begin(), routes.end(), std::back_inserter(*_aidl_return),
+                   [](auto rptr) { return *rptr; });
     return ndk::ScopedAStatus::ok();
 }
 
@@ -707,7 +828,7 @@
     context.fillDescriptor(&_aidl_return->desc);
     std::shared_ptr<StreamIn> stream;
     RETURN_STATUS_IF_ERROR(createInputStream(std::move(context), in_args.sinkMetadata,
-                                             mConfig->microphones, &stream));
+                                             getMicrophoneInfos(), &stream));
     StreamWrapper streamWrapper(stream);
     if (auto patchIt = mPatches.find(in_args.portConfigId); patchIt != mPatches.end()) {
         RETURN_STATUS_IF_ERROR(
@@ -854,17 +975,28 @@
             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
         }
     }
+    // Find the highest sample rate among mix port configs.
+    std::map<int32_t, AudioPortConfig*> sampleRates;
+    std::vector<AudioPortConfig*>& mixPortConfigs =
+            sources[0]->ext.getTag() == AudioPortExt::mix ? sources : sinks;
+    for (auto mix : mixPortConfigs) {
+        sampleRates.emplace(mix->sampleRate.value().value, mix);
+    }
     *_aidl_return = in_requested;
-    _aidl_return->minimumStreamBufferSizeFrames = kMinimumStreamBufferSizeFrames;
+    auto maxSampleRateIt = std::max_element(sampleRates.begin(), sampleRates.end());
+    const int32_t latencyMs = getNominalLatencyMs(*(maxSampleRateIt->second));
+    _aidl_return->minimumStreamBufferSizeFrames =
+            calculateBufferSizeFrames(latencyMs, maxSampleRateIt->first);
     _aidl_return->latenciesMs.clear();
     _aidl_return->latenciesMs.insert(_aidl_return->latenciesMs.end(),
-                                     _aidl_return->sinkPortConfigIds.size(), kLatencyMs);
+                                     _aidl_return->sinkPortConfigIds.size(), latencyMs);
     AudioPatch oldPatch{};
     if (existing == patches.end()) {
         _aidl_return->id = getConfig().nextPatchId++;
         patches.push_back(*_aidl_return);
     } else {
         oldPatch = *existing;
+        *existing = *_aidl_return;
     }
     patchesBackup = mPatches;
     registerPatch(*_aidl_return);
@@ -899,13 +1031,14 @@
 
     const int portId = existing != configs.end() ? existing->portId : in_requested.portId;
     if (portId == 0) {
-        LOG(ERROR) << __func__ << ": input port config does not specify portId";
+        LOG(ERROR) << __func__ << ": requested port config does not specify portId";
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
     auto& ports = getConfig().ports;
     auto portIt = findById<AudioPort>(ports, portId);
     if (portIt == ports.end()) {
-        LOG(ERROR) << __func__ << ": input port config points to non-existent portId " << portId;
+        LOG(ERROR) << __func__ << ": requested port config points to non-existent portId "
+                   << portId;
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
     if (existing != configs.end()) {
@@ -923,6 +1056,10 @@
     // or a new generated config. Now attempt to update it according to the specified
     // fields of 'in_requested'.
 
+    // Device ports with only dynamic profiles are used for devices that are connected via ADSP,
+    // which takes care of their actual configuration automatically.
+    const bool allowDynamicConfig = portIt->ext.getTag() == AudioPortExt::device &&
+                                    hasDynamicProfilesOnly(portIt->profiles);
     bool requestedIsValid = true, requestedIsFullySpecified = true;
 
     AudioIoFlags portFlags = portIt->flags;
@@ -940,17 +1077,19 @@
     AudioProfile portProfile;
     if (in_requested.format.has_value()) {
         const auto& format = in_requested.format.value();
-        if (findAudioProfile(*portIt, format, &portProfile)) {
+        if ((format == AudioFormatDescription{} && allowDynamicConfig) ||
+            findAudioProfile(*portIt, format, &portProfile)) {
             out_suggested->format = format;
         } else {
             LOG(WARNING) << __func__ << ": requested format " << format.toString()
-                         << " is not found in port's " << portId << " profiles";
+                         << " is not found in the profiles of port " << portId;
             requestedIsValid = false;
         }
     } else {
         requestedIsFullySpecified = false;
     }
-    if (!findAudioProfile(*portIt, out_suggested->format.value(), &portProfile)) {
+    if (!(out_suggested->format.value() == AudioFormatDescription{} && allowDynamicConfig) &&
+        !findAudioProfile(*portIt, out_suggested->format.value(), &portProfile)) {
         LOG(ERROR) << __func__ << ": port " << portId << " does not support format "
                    << out_suggested->format.value().toString() << " anymore";
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
@@ -958,8 +1097,9 @@
 
     if (in_requested.channelMask.has_value()) {
         const auto& channelMask = in_requested.channelMask.value();
-        if (find(portProfile.channelMasks.begin(), portProfile.channelMasks.end(), channelMask) !=
-            portProfile.channelMasks.end()) {
+        if ((channelMask == AudioChannelLayout{} && allowDynamicConfig) ||
+            find(portProfile.channelMasks.begin(), portProfile.channelMasks.end(), channelMask) !=
+                    portProfile.channelMasks.end()) {
             out_suggested->channelMask = channelMask;
         } else {
             LOG(WARNING) << __func__ << ": requested channel mask " << channelMask.toString()
@@ -973,7 +1113,8 @@
 
     if (in_requested.sampleRate.has_value()) {
         const auto& sampleRate = in_requested.sampleRate.value();
-        if (find(portProfile.sampleRates.begin(), portProfile.sampleRates.end(),
+        if ((sampleRate.value == 0 && allowDynamicConfig) ||
+            find(portProfile.sampleRates.begin(), portProfile.sampleRates.end(),
                  sampleRate.value) != portProfile.sampleRates.end()) {
             out_suggested->sampleRate = sampleRate;
         } else {
@@ -1090,7 +1231,7 @@
         // Reset master mute if it failed.
         onMasterMuteChanged(mMasterMute);
     }
-    return std::move(result);
+    return result;
 }
 
 ndk::ScopedAStatus Module::getMasterVolume(float* _aidl_return) {
@@ -1112,7 +1253,7 @@
                        << "), error=" << result;
             onMasterVolumeChanged(mMasterVolume);
         }
-        return std::move(result);
+        return result;
     }
     LOG(ERROR) << __func__ << ": invalid master volume value: " << in_volume;
     return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
@@ -1131,7 +1272,7 @@
 }
 
 ndk::ScopedAStatus Module::getMicrophones(std::vector<MicrophoneInfo>* _aidl_return) {
-    *_aidl_return = getConfig().microphones;
+    *_aidl_return = getMicrophoneInfos();
     LOG(DEBUG) << __func__ << ": returning " << ::android::internal::ToString(*_aidl_return);
     return ndk::ScopedAStatus::ok();
 }
@@ -1277,6 +1418,12 @@
             mmapSources.insert(port.id);
         }
     }
+    if (mmapSources.empty() && mmapSinks.empty()) {
+        AudioMMapPolicyInfo never;
+        never.mmapPolicy = AudioMMapPolicy::NEVER;
+        _aidl_return->push_back(never);
+        return ndk::ScopedAStatus::ok();
+    }
     for (const auto& route : getConfig().routes) {
         if (mmapSinks.count(route.sinkPortId) != 0) {
             // The sink is a mix port, add the sources if they are device ports.
@@ -1365,7 +1512,18 @@
     return mIsMmapSupported.value();
 }
 
-ndk::ScopedAStatus Module::populateConnectedDevicePort(AudioPort* audioPort __unused) {
+ndk::ScopedAStatus Module::populateConnectedDevicePort(AudioPort* audioPort) {
+    if (audioPort->ext.getTag() != AudioPortExt::device) {
+        LOG(ERROR) << __func__ << ": not a device port: " << audioPort->toString();
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    const auto& devicePort = audioPort->ext.get<AudioPortExt::device>();
+    if (!devicePort.device.type.connection.empty()) {
+        LOG(ERROR) << __func__
+                   << ": module implementation must override 'populateConnectedDevicePort' "
+                   << "to handle connection of external devices.";
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
     LOG(VERBOSE) << __func__ << ": do nothing and return ok";
     return ndk::ScopedAStatus::ok();
 }
@@ -1393,9 +1551,27 @@
     return ndk::ScopedAStatus::ok();
 }
 
-Module::BtProfileHandles Module::getBtProfileManagerHandles() {
-    return std::make_tuple(std::weak_ptr<IBluetooth>(), std::weak_ptr<IBluetoothA2dp>(),
-                           std::weak_ptr<IBluetoothLe>());
+std::vector<MicrophoneInfo> Module::getMicrophoneInfos() {
+    std::vector<MicrophoneInfo> result;
+    Configuration& config = getConfig();
+    for (const AudioPort& port : config.ports) {
+        if (port.ext.getTag() == AudioPortExt::Tag::device) {
+            const AudioDeviceType deviceType =
+                    port.ext.get<AudioPortExt::Tag::device>().device.type.type;
+            if (deviceType == AudioDeviceType::IN_MICROPHONE ||
+                deviceType == AudioDeviceType::IN_MICROPHONE_BACK) {
+                // Placeholder values. Vendor implementations must populate MicrophoneInfo
+                // accordingly based on their physical microphone parameters.
+                result.push_back(MicrophoneInfo{
+                        .id = port.name,
+                        .device = port.ext.get<AudioPortExt::Tag::device>().device,
+                        .group = 0,
+                        .indexInTheGroup = 0,
+                });
+            }
+        }
+    }
+    return result;
 }
 
 ndk::ScopedAStatus Module::bluetoothParametersUpdated() {
diff --git a/audio/aidl/default/ModulePrimary.cpp b/audio/aidl/default/ModulePrimary.cpp
index 9919c7f..3da6d48 100644
--- a/audio/aidl/default/ModulePrimary.cpp
+++ b/audio/aidl/default/ModulePrimary.cpp
@@ -58,4 +58,12 @@
                                                   offloadInfo);
 }
 
+int32_t ModulePrimary::getNominalLatencyMs(const AudioPortConfig&) {
+    // 85 ms is chosen considering 4096 frames @ 48 kHz. This is the value which allows
+    // the virtual Android device implementation to pass CTS. Hardware implementations
+    // should have significantly lower latency.
+    static constexpr int32_t kLatencyMs = 85;
+    return kLatencyMs;
+}
+
 }  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/SoundDose.cpp b/audio/aidl/default/SoundDose.cpp
index f12ce5d..1c9e081 100644
--- a/audio/aidl/default/SoundDose.cpp
+++ b/audio/aidl/default/SoundDose.cpp
@@ -18,7 +18,15 @@
 
 #include "core-impl/SoundDose.h"
 
+#include <aidl/android/hardware/audio/core/sounddose/ISoundDose.h>
 #include <android-base/logging.h>
+#include <media/AidlConversionCppNdk.h>
+#include <utils/Timers.h>
+
+using aidl::android::hardware::audio::core::sounddose::ISoundDose;
+using aidl::android::media::audio::common::AudioDevice;
+using aidl::android::media::audio::common::AudioDeviceDescription;
+using aidl::android::media::audio::common::AudioFormatDescription;
 
 namespace aidl::android::hardware::audio::core::sounddose {
 
@@ -28,11 +36,16 @@
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
 
+    ::android::audio_utils::lock_guard l(mMutex);
     mRs2Value = in_rs2ValueDbA;
+    if (mMelProcessor != nullptr) {
+        mMelProcessor->setOutputRs2UpperBound(in_rs2ValueDbA);
+    }
     return ndk::ScopedAStatus::ok();
 }
 
 ndk::ScopedAStatus SoundDose::getOutputRs2UpperBound(float* _aidl_return) {
+    ::android::audio_utils::lock_guard l(mMutex);
     *_aidl_return = mRs2Value;
     LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
     return ndk::ScopedAStatus::ok();
@@ -44,6 +57,8 @@
         LOG(ERROR) << __func__ << ": Callback is nullptr";
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
+
+    ::android::audio_utils::lock_guard l(mCbMutex);
     if (mCallback != nullptr) {
         LOG(ERROR) << __func__ << ": Sound dose callback was already registered";
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
@@ -51,7 +66,81 @@
 
     mCallback = in_callback;
     LOG(DEBUG) << __func__ << ": Registered sound dose callback ";
+
     return ndk::ScopedAStatus::ok();
 }
 
+void SoundDose::setAudioDevice(const AudioDevice& audioDevice) {
+    ::android::audio_utils::lock_guard l(mCbMutex);
+    mAudioDevice = audioDevice;
+}
+
+void SoundDose::startDataProcessor(uint32_t sampleRate, uint32_t channelCount,
+                                   const AudioFormatDescription& aidlFormat) {
+    ::android::audio_utils::lock_guard l(mMutex);
+    const auto result = aidl2legacy_AudioFormatDescription_audio_format_t(aidlFormat);
+    const audio_format_t format = result.value_or(AUDIO_FORMAT_INVALID);
+
+    if (mMelProcessor == nullptr) {
+        // we don't have the deviceId concept on the vendor side so just pass 0
+        mMelProcessor = ::android::sp<::android::audio_utils::MelProcessor>::make(
+                sampleRate, channelCount, format, mMelCallback, /*deviceId=*/0, mRs2Value);
+    } else {
+        mMelProcessor->updateAudioFormat(sampleRate, channelCount, format);
+    }
+}
+
+void SoundDose::process(const void* buffer, size_t bytes) {
+    ::android::audio_utils::lock_guard l(mMutex);
+    if (mMelProcessor != nullptr) {
+        mMelProcessor->process(buffer, bytes);
+    }
+}
+
+void SoundDose::onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length,
+                               audio_port_handle_t deviceId __attribute__((__unused__))) const {
+    ::android::audio_utils::lock_guard l(mCbMutex);
+    if (!mAudioDevice.has_value()) {
+        LOG(WARNING) << __func__ << ": New mel values without a registered device";
+        return;
+    }
+    if (mCallback == nullptr) {
+        LOG(ERROR) << __func__ << ": New mel values without a registered callback";
+        return;
+    }
+
+    ISoundDose::IHalSoundDoseCallback::MelRecord melRecord;
+    melRecord.timestamp = nanoseconds_to_seconds(systemTime());
+    melRecord.melValues = std::vector<float>(mels.begin() + offset, mels.begin() + offset + length);
+
+    mCallback->onNewMelValues(melRecord, mAudioDevice.value());
+}
+
+void SoundDose::MelCallback::onNewMelValues(const std::vector<float>& mels, size_t offset,
+                                            size_t length,
+                                            audio_port_handle_t deviceId
+                                            __attribute__((__unused__))) const {
+    mSoundDose.onNewMelValues(mels, offset, length, deviceId);
+}
+
+void SoundDose::onMomentaryExposure(float currentMel, audio_port_handle_t deviceId
+                                    __attribute__((__unused__))) const {
+    ::android::audio_utils::lock_guard l(mCbMutex);
+    if (!mAudioDevice.has_value()) {
+        LOG(WARNING) << __func__ << ": Momentary exposure without a registered device";
+        return;
+    }
+    if (mCallback == nullptr) {
+        LOG(ERROR) << __func__ << ": Momentary exposure without a registered callback";
+        return;
+    }
+
+    mCallback->onMomentaryExposureWarning(currentMel, mAudioDevice.value());
+}
+
+void SoundDose::MelCallback::onMomentaryExposure(float currentMel, audio_port_handle_t deviceId
+                                                 __attribute__((__unused__))) const {
+    mSoundDose.onMomentaryExposure(currentMel, deviceId);
+}
+
 }  // namespace aidl::android::hardware::audio::core::sounddose
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index f7298c0..e8c1693 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -90,6 +90,14 @@
     return true;
 }
 
+void StreamContext::startStreamDataProcessor() {
+    auto streamDataProcessor = mStreamDataProcessor.lock();
+    if (streamDataProcessor != nullptr) {
+        streamDataProcessor->startDataProcessor(mSampleRate, getChannelCount(mChannelLayout),
+                                                mFormat);
+    }
+}
+
 void StreamContext::reset() {
     mCommandMQ.reset();
     mReplyMQ.reset();
@@ -130,7 +138,7 @@
     reply->status = STATUS_OK;
     if (isConnected) {
         reply->observable.frames = mContext->getFrameCount();
-        reply->observable.timeNs = ::android::elapsedRealtimeNano();
+        reply->observable.timeNs = ::android::uptimeNanos();
         if (auto status = mDriver->refinePosition(&reply->observable); status == ::android::OK) {
             return;
         }
@@ -307,7 +315,7 @@
     const size_t frameSize = mContext->getFrameSize();
     size_t actualFrameCount = 0;
     bool fatal = false;
-    int32_t latency = Module::kLatencyMs;
+    int32_t latency = mContext->getNominalLatencyMs();
     if (isConnected) {
         if (::android::status_t status = mDriver->transfer(mDataBuffer.get(), byteCount / frameSize,
                                                            &actualFrameCount, &latency);
@@ -573,7 +581,7 @@
     const size_t readByteCount = dataMQ->availableToRead();
     const size_t frameSize = mContext->getFrameSize();
     bool fatal = false;
-    int32_t latency = Module::kLatencyMs;
+    int32_t latency = mContext->getNominalLatencyMs();
     if (bool success = readByteCount > 0 ? dataMQ->read(&mDataBuffer[0], readByteCount) : true) {
         const bool isConnected = mIsConnected;
         LOG(VERBOSE) << __func__ << ": reading of " << readByteCount << " bytes from data MQ"
@@ -593,6 +601,10 @@
                 fatal = true;
                 LOG(ERROR) << __func__ << ": write failed: " << status;
             }
+            auto streamDataProcessor = mContext->getStreamDataProcessor().lock();
+            if (streamDataProcessor != nullptr) {
+                streamDataProcessor->process(mDataBuffer.get(), actualFrameCount * frameSize);
+            }
         } else {
             if (mContext->getAsyncCallback() == nullptr) {
                 usleep(3000);  // Simulate blocking transfer delay.
@@ -836,7 +848,7 @@
 }
 
 StreamInHwGainHelper::StreamInHwGainHelper(const StreamContext* context)
-    : mChannelCount(getChannelCount(context->getChannelLayout())) {}
+    : mChannelCount(getChannelCount(context->getChannelLayout())), mHwGains(mChannelCount, 0.0f) {}
 
 ndk::ScopedAStatus StreamInHwGainHelper::getHwGainImpl(std::vector<float>* _aidl_return) {
     *_aidl_return = mHwGains;
@@ -967,7 +979,8 @@
 }
 
 StreamOutHwVolumeHelper::StreamOutHwVolumeHelper(const StreamContext* context)
-    : mChannelCount(getChannelCount(context->getChannelLayout())) {}
+    : mChannelCount(getChannelCount(context->getChannelLayout())),
+      mHwVolumes(mChannelCount, 0.0f) {}
 
 ndk::ScopedAStatus StreamOutHwVolumeHelper::getHwVolumeImpl(std::vector<float>* _aidl_return) {
     *_aidl_return = mHwVolumes;
diff --git a/audio/aidl/default/Telephony.cpp b/audio/aidl/default/Telephony.cpp
index bf05a8d..d9da39f 100644
--- a/audio/aidl/default/Telephony.cpp
+++ b/audio/aidl/default/Telephony.cpp
@@ -16,9 +16,9 @@
 
 #define LOG_TAG "AHAL_Telephony"
 #include <android-base/logging.h>
+#include <android/binder_to_string.h>
 
 #include <Utils.h>
-#include <android/binder_to_string.h>
 
 #include "core-impl/Telephony.h"
 
diff --git a/audio/aidl/default/XsdcConversion.cpp b/audio/aidl/default/XsdcConversion.cpp
new file mode 100644
index 0000000..9e30347
--- /dev/null
+++ b/audio/aidl/default/XsdcConversion.cpp
@@ -0,0 +1,448 @@
+#include <inttypes.h>
+
+#include <unordered_set>
+
+#define LOG_TAG "AHAL_Config"
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include <aidl/android/media/audio/common/AudioPort.h>
+#include <aidl/android/media/audio/common/AudioPortConfig.h>
+#include <media/AidlConversionCppNdk.h>
+#include <media/TypeConverter.h>
+
+#include "core-impl/XmlConverter.h"
+#include "core-impl/XsdcConversion.h"
+
+using aidl::android::media::audio::common::AudioChannelLayout;
+using aidl::android::media::audio::common::AudioDevice;
+using aidl::android::media::audio::common::AudioDeviceAddress;
+using aidl::android::media::audio::common::AudioDeviceDescription;
+using aidl::android::media::audio::common::AudioDeviceType;
+using aidl::android::media::audio::common::AudioFormatDescription;
+using aidl::android::media::audio::common::AudioFormatType;
+using aidl::android::media::audio::common::AudioGain;
+using aidl::android::media::audio::common::AudioHalCapCriterion;
+using aidl::android::media::audio::common::AudioHalCapCriterionType;
+using aidl::android::media::audio::common::AudioHalVolumeCurve;
+using aidl::android::media::audio::common::AudioIoFlags;
+using aidl::android::media::audio::common::AudioPort;
+using aidl::android::media::audio::common::AudioPortConfig;
+using aidl::android::media::audio::common::AudioPortDeviceExt;
+using aidl::android::media::audio::common::AudioPortExt;
+using aidl::android::media::audio::common::AudioPortMixExt;
+using aidl::android::media::audio::common::AudioProfile;
+using ::android::BAD_VALUE;
+using ::android::base::unexpected;
+
+namespace ap_xsd = android::audio::policy::configuration;
+namespace eng_xsd = android::audio::policy::engine::configuration;
+
+namespace aidl::android::hardware::audio::core::internal {
+
+inline ConversionResult<std::string> assertNonEmpty(const std::string& s) {
+    if (s.empty()) {
+        LOG(ERROR) << __func__ << " Review Audio Policy config: "
+                   << " empty string is not valid.";
+        return unexpected(BAD_VALUE);
+    }
+    return s;
+}
+
+#define NON_EMPTY_STRING_OR_FATAL(s) VALUE_OR_FATAL(assertNonEmpty(s))
+
+ConversionResult<AudioFormatDescription> convertAudioFormatToAidl(const std::string& xsdcFormat) {
+    audio_format_t legacyFormat = ::android::formatFromString(xsdcFormat, AUDIO_FORMAT_DEFAULT);
+    ConversionResult<AudioFormatDescription> result =
+            legacy2aidl_audio_format_t_AudioFormatDescription(legacyFormat);
+    if ((legacyFormat == AUDIO_FORMAT_DEFAULT && xsdcFormat.compare("AUDIO_FORMAT_DEFAULT") != 0) ||
+        !result.ok()) {
+        LOG(ERROR) << __func__ << " Review Audio Policy config: " << xsdcFormat
+                   << " is not a valid audio format.";
+        return unexpected(BAD_VALUE);
+    }
+    return result;
+}
+
+std::unordered_set<std::string> getAttachedDevices(const ap_xsd::Modules::Module& moduleConfig) {
+    std::unordered_set<std::string> attachedDeviceSet;
+    if (moduleConfig.hasAttachedDevices()) {
+        for (const ap_xsd::AttachedDevices& attachedDevices : moduleConfig.getAttachedDevices()) {
+            if (attachedDevices.hasItem()) {
+                attachedDeviceSet.insert(attachedDevices.getItem().begin(),
+                                         attachedDevices.getItem().end());
+            }
+        }
+    }
+    return attachedDeviceSet;
+}
+
+ConversionResult<AudioDeviceDescription> convertDeviceTypeToAidl(const std::string& xType) {
+    audio_devices_t legacyDeviceType = AUDIO_DEVICE_NONE;
+    ::android::DeviceConverter::fromString(xType, legacyDeviceType);
+    ConversionResult<AudioDeviceDescription> result =
+            legacy2aidl_audio_devices_t_AudioDeviceDescription(legacyDeviceType);
+    if ((legacyDeviceType == AUDIO_DEVICE_NONE) || !result.ok()) {
+        LOG(ERROR) << __func__ << " Review Audio Policy config: " << xType
+                   << " is not a valid device type.";
+        return unexpected(BAD_VALUE);
+    }
+    return result;
+}
+
+ConversionResult<AudioDevice> createAudioDevice(
+        const ap_xsd::DevicePorts::DevicePort& xDevicePort) {
+    AudioDevice device = {
+            .type = VALUE_OR_FATAL(convertDeviceTypeToAidl(xDevicePort.getType())),
+            .address = xDevicePort.hasAddress()
+                               ? AudioDeviceAddress::make<AudioDeviceAddress::Tag::id>(
+                                         xDevicePort.getAddress())
+                               : AudioDeviceAddress{}};
+    if (device.type.type == AudioDeviceType::IN_MICROPHONE && device.type.connection.empty()) {
+        device.address = "bottom";
+    } else if (device.type.type == AudioDeviceType::IN_MICROPHONE_BACK &&
+               device.type.connection.empty()) {
+        device.address = "back";
+    }
+    return device;
+}
+
+ConversionResult<AudioPortExt> createAudioPortExt(
+        const ap_xsd::DevicePorts::DevicePort& xDevicePort,
+        const std::string& xDefaultOutputDevice) {
+    AudioPortDeviceExt deviceExt = {
+            .device = VALUE_OR_FATAL(createAudioDevice(xDevicePort)),
+            .flags = (xDevicePort.getTagName() == xDefaultOutputDevice)
+                             ? 1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE
+                             : 0,
+            .encodedFormats =
+                    xDevicePort.hasEncodedFormats()
+                            ? VALUE_OR_FATAL(
+                                      (convertCollectionToAidl<std::string, AudioFormatDescription>(
+                                              xDevicePort.getEncodedFormats(),
+                                              &convertAudioFormatToAidl)))
+                            : std::vector<AudioFormatDescription>{},
+    };
+    return AudioPortExt::make<AudioPortExt::Tag::device>(deviceExt);
+}
+
+ConversionResult<AudioPortExt> createAudioPortExt(const ap_xsd::MixPorts::MixPort& xMixPort) {
+    AudioPortMixExt mixExt = {
+            .maxOpenStreamCount =
+                    xMixPort.hasMaxOpenCount() ? static_cast<int>(xMixPort.getMaxOpenCount()) : 0,
+            .maxActiveStreamCount = xMixPort.hasMaxActiveCount()
+                                            ? static_cast<int>(xMixPort.getMaxActiveCount())
+                                            : 1,
+            .recommendedMuteDurationMs =
+                    xMixPort.hasRecommendedMuteDurationMs()
+                            ? static_cast<int>(xMixPort.getRecommendedMuteDurationMs())
+                            : 0};
+    return AudioPortExt::make<AudioPortExt::Tag::mix>(mixExt);
+}
+
+ConversionResult<int> convertGainModeToAidl(const std::vector<ap_xsd::AudioGainMode>& gainModeVec) {
+    int gainModeMask = 0;
+    for (const ap_xsd::AudioGainMode& gainMode : gainModeVec) {
+        audio_gain_mode_t legacyGainMode;
+        if (::android::GainModeConverter::fromString(ap_xsd::toString(gainMode), legacyGainMode)) {
+            gainModeMask |= static_cast<int>(legacyGainMode);
+        }
+    }
+    return gainModeMask;
+}
+
+ConversionResult<AudioChannelLayout> convertChannelMaskToAidl(
+        const ap_xsd::AudioChannelMask& xChannelMask) {
+    std::string xChannelMaskLiteral = ap_xsd::toString(xChannelMask);
+    audio_channel_mask_t legacyChannelMask = ::android::channelMaskFromString(xChannelMaskLiteral);
+    ConversionResult<AudioChannelLayout> result =
+            legacy2aidl_audio_channel_mask_t_AudioChannelLayout(
+                    legacyChannelMask,
+                    /* isInput= */ xChannelMaskLiteral.find("AUDIO_CHANNEL_IN_") == 0);
+    if ((legacyChannelMask == AUDIO_CHANNEL_INVALID) || !result.ok()) {
+        LOG(ERROR) << __func__ << " Review Audio Policy config: " << xChannelMaskLiteral
+                   << " is not a valid audio channel mask.";
+        return unexpected(BAD_VALUE);
+    }
+    return result;
+}
+
+ConversionResult<AudioGain> convertGainToAidl(const ap_xsd::Gains::Gain& xGain) {
+    return AudioGain{
+            .mode = VALUE_OR_FATAL(convertGainModeToAidl(xGain.getMode())),
+            .channelMask =
+                    xGain.hasChannel_mask()
+                            ? VALUE_OR_FATAL(convertChannelMaskToAidl(xGain.getChannel_mask()))
+                            : AudioChannelLayout{},
+            .minValue = xGain.hasMinValueMB() ? xGain.getMinValueMB() : 0,
+            .maxValue = xGain.hasMaxValueMB() ? xGain.getMaxValueMB() : 0,
+            .defaultValue = xGain.hasDefaultValueMB() ? xGain.getDefaultValueMB() : 0,
+            .stepValue = xGain.hasStepValueMB() ? xGain.getStepValueMB() : 0,
+            .minRampMs = xGain.hasMinRampMs() ? xGain.getMinRampMs() : 0,
+            .maxRampMs = xGain.hasMaxRampMs() ? xGain.getMaxRampMs() : 0,
+            .useForVolume = xGain.hasUseForVolume() ? xGain.getUseForVolume() : false,
+    };
+}
+
+ConversionResult<AudioProfile> convertAudioProfileToAidl(const ap_xsd::Profile& xProfile) {
+    return AudioProfile{
+            .format = xProfile.hasFormat()
+                              ? VALUE_OR_FATAL(convertAudioFormatToAidl(xProfile.getFormat()))
+                              : AudioFormatDescription{},
+            .channelMasks =
+                    xProfile.hasChannelMasks()
+                            ? VALUE_OR_FATAL((convertCollectionToAidl<ap_xsd::AudioChannelMask,
+                                                                      AudioChannelLayout>(
+                                      xProfile.getChannelMasks(), &convertChannelMaskToAidl)))
+                            : std::vector<AudioChannelLayout>{},
+            .sampleRates = xProfile.hasSamplingRates()
+                                   ? VALUE_OR_FATAL((convertCollectionToAidl<int64_t, int>(
+                                             xProfile.getSamplingRates(),
+                                             [](const int64_t x) -> int { return x; })))
+                                   : std::vector<int>{}};
+}
+
+ConversionResult<AudioIoFlags> convertIoFlagsToAidl(
+        const std::vector<ap_xsd::AudioInOutFlag>& flags, const ap_xsd::Role role,
+        bool flagsForMixPort) {
+    int flagMask = 0;
+    if ((role == ap_xsd::Role::sink && flagsForMixPort) ||
+        (role == ap_xsd::Role::source && !flagsForMixPort)) {
+        for (const ap_xsd::AudioInOutFlag& flag : flags) {
+            audio_input_flags_t legacyFlag;
+            if (::android::InputFlagConverter::fromString(ap_xsd::toString(flag), legacyFlag)) {
+                flagMask |= static_cast<int>(legacyFlag);
+            }
+        }
+        return AudioIoFlags::make<AudioIoFlags::Tag::input>(flagMask);
+    } else {
+        for (const ap_xsd::AudioInOutFlag& flag : flags) {
+            audio_output_flags_t legacyFlag;
+            if (::android::OutputFlagConverter::fromString(ap_xsd::toString(flag), legacyFlag)) {
+                flagMask |= static_cast<int>(legacyFlag);
+            }
+        }
+        return AudioIoFlags::make<AudioIoFlags::Tag::output>(flagMask);
+    }
+}
+
+ConversionResult<AudioPort> convertDevicePortToAidl(
+        const ap_xsd::DevicePorts::DevicePort& xDevicePort, const std::string& xDefaultOutputDevice,
+        int32_t& nextPortId) {
+    return AudioPort{
+            .id = nextPortId++,
+            .name = NON_EMPTY_STRING_OR_FATAL(xDevicePort.getTagName()),
+            .profiles = VALUE_OR_FATAL((convertCollectionToAidl<ap_xsd::Profile, AudioProfile>(
+                    xDevicePort.getProfile(), convertAudioProfileToAidl))),
+            .flags = VALUE_OR_FATAL(convertIoFlagsToAidl({}, xDevicePort.getRole(), false)),
+            .gains = VALUE_OR_FATAL(
+                    (convertWrappedCollectionToAidl<ap_xsd::Gains, ap_xsd::Gains::Gain, AudioGain>(
+                            xDevicePort.getGains(), &ap_xsd::Gains::getGain, convertGainToAidl))),
+
+            .ext = VALUE_OR_FATAL(createAudioPortExt(xDevicePort, xDefaultOutputDevice))};
+}
+
+ConversionResult<std::vector<AudioPort>> convertDevicePortsInModuleToAidl(
+        const ap_xsd::Modules::Module& xModuleConfig, int32_t& nextPortId) {
+    std::vector<AudioPort> audioPortVec;
+    std::vector<ap_xsd::DevicePorts> xDevicePortsVec = xModuleConfig.getDevicePorts();
+    if (xDevicePortsVec.size() > 1) {
+        LOG(ERROR) << __func__ << "Having multiple '<devicePorts>' elements is not allowed, found: "
+                   << xDevicePortsVec.size();
+        return unexpected(BAD_VALUE);
+    }
+    if (!xDevicePortsVec.empty()) {
+        const std::string xDefaultOutputDevice = xModuleConfig.hasDefaultOutputDevice()
+                                                         ? xModuleConfig.getDefaultOutputDevice()
+                                                         : "";
+        audioPortVec.reserve(xDevicePortsVec[0].getDevicePort().size());
+        for (const ap_xsd::DevicePorts& xDevicePortsType : xDevicePortsVec) {
+            for (const ap_xsd::DevicePorts::DevicePort& xDevicePort :
+                 xDevicePortsType.getDevicePort()) {
+                audioPortVec.push_back(VALUE_OR_FATAL(
+                        convertDevicePortToAidl(xDevicePort, xDefaultOutputDevice, nextPortId)));
+            }
+        }
+    }
+    const std::unordered_set<std::string> xAttachedDeviceSet = getAttachedDevices(xModuleConfig);
+    for (const auto& port : audioPortVec) {
+        const auto& devicePort = port.ext.get<AudioPortExt::device>();
+        if (xAttachedDeviceSet.count(port.name) != devicePort.device.type.connection.empty()) {
+            LOG(ERROR) << __func__ << ": Review Audio Policy config: <attachedDevices> "
+                       << "list is incorrect or devicePort \"" << port.name
+                       << "\" type= " << devicePort.device.type.toString() << " is incorrect.";
+            return unexpected(BAD_VALUE);
+        }
+    }
+    return audioPortVec;
+}
+
+ConversionResult<AudioPort> convertMixPortToAidl(const ap_xsd::MixPorts::MixPort& xMixPort,
+                                                 int32_t& nextPortId) {
+    return AudioPort{
+            .id = nextPortId++,
+            .name = NON_EMPTY_STRING_OR_FATAL(xMixPort.getName()),
+            .profiles = VALUE_OR_FATAL((convertCollectionToAidl<ap_xsd::Profile, AudioProfile>(
+                    xMixPort.getProfile(), convertAudioProfileToAidl))),
+            .flags = xMixPort.hasFlags()
+                             ? VALUE_OR_FATAL(convertIoFlagsToAidl(xMixPort.getFlags(),
+                                                                   xMixPort.getRole(), true))
+                             : VALUE_OR_FATAL(convertIoFlagsToAidl({}, xMixPort.getRole(), true)),
+            .gains = VALUE_OR_FATAL(
+                    (convertWrappedCollectionToAidl<ap_xsd::Gains, ap_xsd::Gains::Gain, AudioGain>(
+                            xMixPort.getGains(), &ap_xsd::Gains::getGain, &convertGainToAidl))),
+            .ext = VALUE_OR_FATAL(createAudioPortExt(xMixPort)),
+    };
+}
+
+ConversionResult<std::vector<AudioPort>> convertMixPortsInModuleToAidl(
+        const ap_xsd::Modules::Module& xModuleConfig, int32_t& nextPortId) {
+    std::vector<AudioPort> audioPortVec;
+    std::vector<ap_xsd::MixPorts> xMixPortsVec = xModuleConfig.getMixPorts();
+    if (xMixPortsVec.size() > 1) {
+        LOG(ERROR) << __func__ << "Having multiple '<mixPorts>' elements is not allowed, found: "
+                   << xMixPortsVec.size();
+        return unexpected(BAD_VALUE);
+    }
+    if (!xMixPortsVec.empty()) {
+        audioPortVec.reserve(xMixPortsVec[0].getMixPort().size());
+        for (const ap_xsd::MixPorts& xMixPortsType : xMixPortsVec) {
+            for (const ap_xsd::MixPorts::MixPort& xMixPort : xMixPortsType.getMixPort()) {
+                audioPortVec.push_back(VALUE_OR_FATAL(convertMixPortToAidl(xMixPort, nextPortId)));
+            }
+        }
+    }
+    return audioPortVec;
+}
+
+ConversionResult<int32_t> getSinkPortId(const ap_xsd::Routes::Route& xRoute,
+                                        const std::unordered_map<std::string, int32_t>& portMap) {
+    auto portMapIter = portMap.find(xRoute.getSink());
+    if (portMapIter == portMap.end()) {
+        LOG(ERROR) << __func__ << " Review Audio Policy config: audio route"
+                   << "has sink: " << xRoute.getSink()
+                   << " which is neither a device port nor mix port.";
+        return unexpected(BAD_VALUE);
+    }
+    return portMapIter->second;
+}
+
+ConversionResult<std::vector<int32_t>> getSourcePortIds(
+        const ap_xsd::Routes::Route& xRoute,
+        const std::unordered_map<std::string, int32_t>& portMap) {
+    std::vector<int32_t> sourcePortIds;
+    for (const std::string& rawSource : ::android::base::Split(xRoute.getSources(), ",")) {
+        const std::string source = ::android::base::Trim(rawSource);
+        auto portMapIter = portMap.find(source);
+        if (portMapIter == portMap.end()) {
+            LOG(ERROR) << __func__ << " Review Audio Policy config: audio route"
+                       << "has source \"" << source
+                       << "\" which is neither a device port nor mix port.";
+            return unexpected(BAD_VALUE);
+        }
+        sourcePortIds.push_back(portMapIter->second);
+    }
+    return sourcePortIds;
+}
+
+ConversionResult<AudioRoute> convertRouteToAidl(const ap_xsd::Routes::Route& xRoute,
+                                                const std::vector<AudioPort>& aidlAudioPorts) {
+    std::unordered_map<std::string, int32_t> portMap;
+    for (const AudioPort& port : aidlAudioPorts) {
+        portMap.insert({port.name, port.id});
+    }
+    return AudioRoute{.sourcePortIds = VALUE_OR_FATAL(getSourcePortIds(xRoute, portMap)),
+                      .sinkPortId = VALUE_OR_FATAL(getSinkPortId(xRoute, portMap)),
+                      .isExclusive = (xRoute.getType() == ap_xsd::MixType::mux)};
+}
+
+ConversionResult<std::vector<AudioRoute>> convertRoutesInModuleToAidl(
+        const ap_xsd::Modules::Module& xModuleConfig,
+        const std::vector<AudioPort>& aidlAudioPorts) {
+    std::vector<AudioRoute> audioRouteVec;
+    std::vector<ap_xsd::Routes> xRoutesVec = xModuleConfig.getRoutes();
+    if (!xRoutesVec.empty()) {
+        /*
+         * xRoutesVec likely only contains one element; that is, it's
+         * likely that all ap_xsd::Routes::MixPort types that we need to convert
+         * are inside of xRoutesVec[0].
+         */
+        audioRouteVec.reserve(xRoutesVec[0].getRoute().size());
+        for (const ap_xsd::Routes& xRoutesType : xRoutesVec) {
+            for (const ap_xsd::Routes::Route& xRoute : xRoutesType.getRoute()) {
+                audioRouteVec.push_back(VALUE_OR_FATAL(convertRouteToAidl(xRoute, aidlAudioPorts)));
+            }
+        }
+    }
+    return audioRouteVec;
+}
+
+ConversionResult<std::unique_ptr<Module::Configuration>> convertModuleConfigToAidl(
+        const ap_xsd::Modules::Module& xModuleConfig) {
+    auto result = std::make_unique<Module::Configuration>();
+    auto& aidlModuleConfig = *result;
+    std::vector<AudioPort> devicePorts = VALUE_OR_FATAL(
+            convertDevicePortsInModuleToAidl(xModuleConfig, aidlModuleConfig.nextPortId));
+
+    // The XML config does not specify the default input device.
+    // Assign the first attached input device as the default.
+    for (auto& port : devicePorts) {
+        if (port.flags.getTag() != AudioIoFlags::input) continue;
+        auto& deviceExt = port.ext.get<AudioPortExt::device>();
+        if (!deviceExt.device.type.connection.empty()) continue;
+        deviceExt.flags |= 1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE;
+        break;
+    }
+
+    std::vector<AudioPort> mixPorts = VALUE_OR_FATAL(
+            convertMixPortsInModuleToAidl(xModuleConfig, aidlModuleConfig.nextPortId));
+    aidlModuleConfig.ports.reserve(devicePorts.size() + mixPorts.size());
+    aidlModuleConfig.ports.insert(aidlModuleConfig.ports.end(), devicePorts.begin(),
+                                  devicePorts.end());
+    aidlModuleConfig.ports.insert(aidlModuleConfig.ports.end(), mixPorts.begin(), mixPorts.end());
+
+    aidlModuleConfig.routes =
+            VALUE_OR_FATAL(convertRoutesInModuleToAidl(xModuleConfig, aidlModuleConfig.ports));
+    return result;
+}
+
+ConversionResult<AudioHalCapCriterion> convertCapCriterionToAidl(
+        const eng_xsd::CriterionType& xsdcCriterion) {
+    AudioHalCapCriterion aidlCapCriterion;
+    aidlCapCriterion.name = xsdcCriterion.getName();
+    aidlCapCriterion.criterionTypeName = xsdcCriterion.getType();
+    aidlCapCriterion.defaultLiteralValue = xsdcCriterion.get_default();
+    return aidlCapCriterion;
+}
+
+ConversionResult<std::string> convertCriterionTypeValueToAidl(
+        const eng_xsd::ValueType& xsdcCriterionTypeValue) {
+    return xsdcCriterionTypeValue.getLiteral();
+}
+
+ConversionResult<AudioHalCapCriterionType> convertCapCriterionTypeToAidl(
+        const eng_xsd::CriterionTypeType& xsdcCriterionType) {
+    AudioHalCapCriterionType aidlCapCriterionType;
+    aidlCapCriterionType.name = xsdcCriterionType.getName();
+    aidlCapCriterionType.isInclusive = !(static_cast<bool>(xsdcCriterionType.getType()));
+    aidlCapCriterionType.values = VALUE_OR_RETURN(
+            (convertWrappedCollectionToAidl<eng_xsd::ValuesType, eng_xsd::ValueType, std::string>(
+                    xsdcCriterionType.getValues(), &eng_xsd::ValuesType::getValue,
+                    &convertCriterionTypeValueToAidl)));
+    return aidlCapCriterionType;
+}
+
+ConversionResult<AudioHalVolumeCurve::CurvePoint> convertCurvePointToAidl(
+        const std::string& xsdcCurvePoint) {
+    AudioHalVolumeCurve::CurvePoint aidlCurvePoint{};
+    if ((sscanf(xsdcCurvePoint.c_str(), "%" SCNd8 ",%d", &aidlCurvePoint.index,
+                &aidlCurvePoint.attenuationMb) != 2) ||
+        (aidlCurvePoint.index < AudioHalVolumeCurve::CurvePoint::MIN_INDEX) ||
+        (aidlCurvePoint.index > AudioHalVolumeCurve::CurvePoint::MAX_INDEX)) {
+        LOG(ERROR) << __func__ << " Review Audio Policy config: volume curve point:"
+                   << "\"" << xsdcCurvePoint << "\" is invalid";
+        return unexpected(BAD_VALUE);
+    }
+    return aidlCurvePoint;
+}
+}  // namespace aidl::android::hardware::audio::core::internal
diff --git a/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp b/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp
index 63a014a..8727232 100644
--- a/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp
+++ b/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp
@@ -74,7 +74,7 @@
         .common = {.id = {.type = getEffectTypeUuidAcousticEchoCanceler(),
                           .uuid = getEffectImplUuidAcousticEchoCancelerSw(),
                           .proxy = std::nullopt},
-                   .flags = {.type = Flags::Type::INSERT,
+                   .flags = {.type = Flags::Type::PRE_PROC,
                              .insert = Flags::Insert::FIRST,
                              .volume = Flags::Volume::CTRL},
                    .name = AcousticEchoCancelerSw::kEffectName,
diff --git a/audio/aidl/default/alsa/Mixer.cpp b/audio/aidl/default/alsa/Mixer.cpp
index 126c033..e72502b 100644
--- a/audio/aidl/default/alsa/Mixer.cpp
+++ b/audio/aidl/default/alsa/Mixer.cpp
@@ -20,9 +20,24 @@
 #define LOG_TAG "AHAL_AlsaMixer"
 #include <android-base/logging.h>
 #include <android/binder_status.h>
+#include <error/expected_utils.h>
 
 #include "Mixer.h"
 
+namespace ndk {
+
+// This enables use of 'error/expected_utils' for ScopedAStatus.
+
+inline bool errorIsOk(const ScopedAStatus& s) {
+    return s.isOk();
+}
+
+inline std::string errorToString(const ScopedAStatus& s) {
+    return s.getDescription();
+}
+
+}  // namespace ndk
+
 namespace aidl::android::hardware::audio::core::alsa {
 
 // static
@@ -93,6 +108,36 @@
     }
 }
 
+ndk::ScopedAStatus Mixer::getMasterMute(bool* muted) {
+    return getMixerControlMute(MASTER_SWITCH, muted);
+}
+
+ndk::ScopedAStatus Mixer::getMasterVolume(float* volume) {
+    return getMixerControlVolume(MASTER_VOLUME, volume);
+}
+
+ndk::ScopedAStatus Mixer::getMicGain(float* gain) {
+    return getMixerControlVolume(MIC_GAIN, gain);
+}
+
+ndk::ScopedAStatus Mixer::getMicMute(bool* muted) {
+    return getMixerControlMute(MIC_SWITCH, muted);
+}
+
+ndk::ScopedAStatus Mixer::getVolumes(std::vector<float>* volumes) {
+    struct mixer_ctl* mctl;
+    RETURN_STATUS_IF_ERROR(findControl(Mixer::HW_VOLUME, &mctl));
+    std::vector<int> percents;
+    std::lock_guard l(mMixerAccess);
+    if (int err = getMixerControlPercent(mctl, &percents); err != 0) {
+        LOG(ERROR) << __func__ << ": failed to get volume, err=" << err;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    std::transform(percents.begin(), percents.end(), std::back_inserter(*volumes),
+                   [](int percent) -> float { return std::clamp(percent / 100.0f, 0.0f, 1.0f); });
+    return ndk::ScopedAStatus::ok();
+}
+
 ndk::ScopedAStatus Mixer::setMasterMute(bool muted) {
     return setMixerControlMute(MASTER_SWITCH, muted);
 }
@@ -110,35 +155,70 @@
 }
 
 ndk::ScopedAStatus Mixer::setVolumes(const std::vector<float>& volumes) {
-    if (!isValid()) {
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-    }
-    auto it = mMixerControls.find(Mixer::HW_VOLUME);
-    if (it == mMixerControls.end()) {
-        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
-    }
+    struct mixer_ctl* mctl;
+    RETURN_STATUS_IF_ERROR(findControl(Mixer::HW_VOLUME, &mctl));
     std::vector<int> percents;
     std::transform(
             volumes.begin(), volumes.end(), std::back_inserter(percents),
             [](float volume) -> int { return std::floor(std::clamp(volume, 0.0f, 1.0f) * 100); });
     std::lock_guard l(mMixerAccess);
-    if (int err = setMixerControlPercent(it->second, percents); err != 0) {
+    if (int err = setMixerControlPercent(mctl, percents); err != 0) {
         LOG(ERROR) << __func__ << ": failed to set volume, err=" << err;
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
     return ndk::ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus Mixer::setMixerControlMute(Mixer::Control ctl, bool muted) {
+ndk::ScopedAStatus Mixer::findControl(Control ctl, struct mixer_ctl** result) {
     if (!isValid()) {
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
-    auto it = mMixerControls.find(ctl);
-    if (it == mMixerControls.end()) {
-        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    if (auto it = mMixerControls.find(ctl); it != mMixerControls.end()) {
+        *result = it->second;
+        return ndk::ScopedAStatus::ok();
     }
+    return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus Mixer::getMixerControlMute(Control ctl, bool* muted) {
+    struct mixer_ctl* mctl;
+    RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl));
     std::lock_guard l(mMixerAccess);
-    if (int err = setMixerControlValue(it->second, muted ? 0 : 1); err != 0) {
+    std::vector<int> mutedValues;
+    if (int err = getMixerControlValues(mctl, &mutedValues); err != 0) {
+        LOG(ERROR) << __func__ << ": failed to get " << ctl << ", err=" << err;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    if (mutedValues.empty()) {
+        LOG(ERROR) << __func__ << ": got no values for " << ctl;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    *muted = mutedValues[0] != 0;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Mixer::getMixerControlVolume(Control ctl, float* volume) {
+    struct mixer_ctl* mctl;
+    RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl));
+    std::lock_guard l(mMixerAccess);
+    std::vector<int> percents;
+    if (int err = getMixerControlPercent(mctl, &percents); err != 0) {
+        LOG(ERROR) << __func__ << ": failed to get " << ctl << ", err=" << err;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    if (percents.empty()) {
+        LOG(ERROR) << __func__ << ": got no values for " << ctl;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    *volume = std::clamp(percents[0] / 100.0f, 0.0f, 1.0f);
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Mixer::setMixerControlMute(Control ctl, bool muted) {
+    struct mixer_ctl* mctl;
+    RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl));
+    std::lock_guard l(mMixerAccess);
+    if (int err = setMixerControlValue(mctl, muted ? 0 : 1); err != 0) {
         LOG(ERROR) << __func__ << ": failed to set " << ctl << " to " << muted << ", err=" << err;
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
@@ -146,54 +226,72 @@
 }
 
 ndk::ScopedAStatus Mixer::setMixerControlVolume(Control ctl, float volume) {
-    if (!isValid()) {
-        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-    }
-    auto it = mMixerControls.find(ctl);
-    if (it == mMixerControls.end()) {
-        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
-    }
+    struct mixer_ctl* mctl;
+    RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl));
     volume = std::clamp(volume, 0.0f, 1.0f);
     std::lock_guard l(mMixerAccess);
-    if (int err = setMixerControlPercent(it->second, std::floor(volume * 100)); err != 0) {
+    if (int err = setMixerControlPercent(mctl, std::floor(volume * 100)); err != 0) {
         LOG(ERROR) << __func__ << ": failed to set " << ctl << " to " << volume << ", err=" << err;
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
     return ndk::ScopedAStatus::ok();
 }
 
+int Mixer::getMixerControlPercent(struct mixer_ctl* ctl, std::vector<int>* percents) {
+    const unsigned int n = mixer_ctl_get_num_values(ctl);
+    percents->resize(n);
+    for (unsigned int id = 0; id < n; id++) {
+        if (int valueOrError = mixer_ctl_get_percent(ctl, id); valueOrError >= 0) {
+            (*percents)[id] = valueOrError;
+        } else {
+            return valueOrError;
+        }
+    }
+    return 0;
+}
+
+int Mixer::getMixerControlValues(struct mixer_ctl* ctl, std::vector<int>* values) {
+    const unsigned int n = mixer_ctl_get_num_values(ctl);
+    values->resize(n);
+    for (unsigned int id = 0; id < n; id++) {
+        if (int valueOrError = mixer_ctl_get_value(ctl, id); valueOrError >= 0) {
+            (*values)[id] = valueOrError;
+        } else {
+            return valueOrError;
+        }
+    }
+    return 0;
+}
+
 int Mixer::setMixerControlPercent(struct mixer_ctl* ctl, int percent) {
-    int ret = 0;
     const unsigned int n = mixer_ctl_get_num_values(ctl);
     for (unsigned int id = 0; id < n; id++) {
         if (int error = mixer_ctl_set_percent(ctl, id, percent); error != 0) {
-            ret = error;
+            return error;
         }
     }
-    return ret;
+    return 0;
 }
 
 int Mixer::setMixerControlPercent(struct mixer_ctl* ctl, const std::vector<int>& percents) {
-    int ret = 0;
     const unsigned int n = mixer_ctl_get_num_values(ctl);
     for (unsigned int id = 0; id < n; id++) {
         if (int error = mixer_ctl_set_percent(ctl, id, id < percents.size() ? percents[id] : 0);
             error != 0) {
-            ret = error;
+            return error;
         }
     }
-    return ret;
+    return 0;
 }
 
 int Mixer::setMixerControlValue(struct mixer_ctl* ctl, int value) {
-    int ret = 0;
     const unsigned int n = mixer_ctl_get_num_values(ctl);
     for (unsigned int id = 0; id < n; id++) {
         if (int error = mixer_ctl_set_value(ctl, id, value); error != 0) {
-            ret = error;
+            return error;
         }
     }
-    return ret;
+    return 0;
 }
 
 }  // namespace aidl::android::hardware::audio::core::alsa
diff --git a/audio/aidl/default/alsa/Mixer.h b/audio/aidl/default/alsa/Mixer.h
index 8fba1e0..41f19a8 100644
--- a/audio/aidl/default/alsa/Mixer.h
+++ b/audio/aidl/default/alsa/Mixer.h
@@ -39,6 +39,11 @@
 
     bool isValid() const { return mMixer != nullptr; }
 
+    ndk::ScopedAStatus getMasterMute(bool* muted);
+    ndk::ScopedAStatus getMasterVolume(float* volume);
+    ndk::ScopedAStatus getMicGain(float* gain);
+    ndk::ScopedAStatus getMicMute(bool* muted);
+    ndk::ScopedAStatus getVolumes(std::vector<float>* volumes);
     ndk::ScopedAStatus setMasterMute(bool muted);
     ndk::ScopedAStatus setMasterVolume(float volume);
     ndk::ScopedAStatus setMicGain(float gain);
@@ -60,9 +65,16 @@
     static const std::map<Control, std::vector<ControlNamesAndExpectedCtlType>> kPossibleControls;
     static Controls initializeMixerControls(struct mixer* mixer);
 
+    ndk::ScopedAStatus findControl(Control ctl, struct mixer_ctl** result);
+    ndk::ScopedAStatus getMixerControlMute(Control ctl, bool* muted);
+    ndk::ScopedAStatus getMixerControlVolume(Control ctl, float* volume);
     ndk::ScopedAStatus setMixerControlMute(Control ctl, bool muted);
     ndk::ScopedAStatus setMixerControlVolume(Control ctl, float volume);
 
+    int getMixerControlPercent(struct mixer_ctl* ctl, std::vector<int>* percents)
+            REQUIRES(mMixerAccess);
+    int getMixerControlValues(struct mixer_ctl* ctl, std::vector<int>* values)
+            REQUIRES(mMixerAccess);
     int setMixerControlPercent(struct mixer_ctl* ctl, int percent) REQUIRES(mMixerAccess);
     int setMixerControlPercent(struct mixer_ctl* ctl, const std::vector<int>& percents)
             REQUIRES(mMixerAccess);
diff --git a/audio/aidl/default/alsa/ModuleAlsa.cpp b/audio/aidl/default/alsa/ModuleAlsa.cpp
index 8e75d56..8512631 100644
--- a/audio/aidl/default/alsa/ModuleAlsa.cpp
+++ b/audio/aidl/default/alsa/ModuleAlsa.cpp
@@ -39,13 +39,14 @@
     if (!deviceProfile.has_value()) {
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
-    auto profile = alsa::readAlsaDeviceInfo(*deviceProfile);
-    if (!profile.has_value()) {
+    auto proxy = alsa::readAlsaDeviceInfo(*deviceProfile);
+    if (proxy.get() == nullptr) {
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
     }
 
-    std::vector<AudioChannelLayout> channels = alsa::getChannelMasksFromProfile(&profile.value());
-    std::vector<int> sampleRates = alsa::getSampleRatesFromProfile(&profile.value());
+    alsa_device_profile* profile = proxy.getProfile();
+    std::vector<AudioChannelLayout> channels = alsa::getChannelMasksFromProfile(profile);
+    std::vector<int> sampleRates = alsa::getSampleRatesFromProfile(profile);
 
     for (size_t i = 0; i < std::min(MAX_PROFILE_FORMATS, AUDIO_PORT_MAX_AUDIO_PROFILES) &&
                        profile->formats[i] != PCM_FORMAT_INVALID;
diff --git a/audio/aidl/default/alsa/StreamAlsa.cpp b/audio/aidl/default/alsa/StreamAlsa.cpp
index 0605d6f..e57d538 100644
--- a/audio/aidl/default/alsa/StreamAlsa.cpp
+++ b/audio/aidl/default/alsa/StreamAlsa.cpp
@@ -83,7 +83,7 @@
             proxy = alsa::openProxyForAttachedDevice(
                     device, const_cast<struct pcm_config*>(&mConfig.value()), mBufferSizeFrames);
         }
-        if (!proxy) {
+        if (proxy.get() == nullptr) {
             return ::android::NO_INIT;
         }
         alsaDeviceProxies.push_back(std::move(proxy));
@@ -119,7 +119,7 @@
 
 ::android::status_t StreamAlsa::refinePosition(StreamDescriptor::Position* position) {
     if (mAlsaDeviceProxies.empty()) {
-        LOG(FATAL) << __func__ << ": no opened devices";
+        LOG(WARNING) << __func__ << ": no opened devices";
         return ::android::NO_INIT;
     }
     // Since the proxy can only count transferred frames since its creation,
diff --git a/audio/aidl/default/alsa/Utils.cpp b/audio/aidl/default/alsa/Utils.cpp
index 9dcd024..c08836c 100644
--- a/audio/aidl/default/alsa/Utils.cpp
+++ b/audio/aidl/default/alsa/Utils.cpp
@@ -37,6 +37,23 @@
 
 namespace aidl::android::hardware::audio::core::alsa {
 
+DeviceProxy::DeviceProxy() : mProfile(nullptr), mProxy(nullptr, alsaProxyDeleter) {}
+
+DeviceProxy::DeviceProxy(const DeviceProfile& deviceProfile)
+    : mProfile(new alsa_device_profile), mProxy(new alsa_device_proxy, alsaProxyDeleter) {
+    profile_init(mProfile.get(), deviceProfile.direction);
+    mProfile->card = deviceProfile.card;
+    mProfile->device = deviceProfile.device;
+    memset(mProxy.get(), 0, sizeof(alsa_device_proxy));
+}
+
+void DeviceProxy::alsaProxyDeleter(alsa_device_proxy* proxy) {
+    if (proxy != nullptr) {
+        proxy_close(proxy);
+        delete proxy;
+    }
+}
+
 namespace {
 
 using AudioChannelCountToMaskMap = std::map<unsigned int, AudioChannelLayout>;
@@ -261,39 +278,24 @@
     return sampleRates;
 }
 
-DeviceProxy makeDeviceProxy() {
-    DeviceProxy proxy(new alsa_device_proxy, [](alsa_device_proxy* proxy) {
-        if (proxy != nullptr) {
-            proxy_close(proxy);
-            delete proxy;
-        }
-    });
-    memset(proxy.get(), 0, sizeof(alsa_device_proxy));
-    return proxy;
-}
-
 DeviceProxy openProxyForAttachedDevice(const DeviceProfile& deviceProfile,
                                        struct pcm_config* pcmConfig, size_t bufferFrameCount) {
     if (deviceProfile.isExternal) {
         LOG(FATAL) << __func__ << ": called for an external device, address=" << deviceProfile;
     }
-    alsa_device_profile profile;
-    profile_init(&profile, deviceProfile.direction);
-    profile.card = deviceProfile.card;
-    profile.device = deviceProfile.device;
-    if (!profile_fill_builtin_device_info(&profile, pcmConfig, bufferFrameCount)) {
+    DeviceProxy proxy(deviceProfile);
+    if (!profile_fill_builtin_device_info(proxy.getProfile(), pcmConfig, bufferFrameCount)) {
         LOG(FATAL) << __func__ << ": failed to init for built-in device, address=" << deviceProfile;
     }
-    auto proxy = makeDeviceProxy();
-    if (int err = proxy_prepare_from_default_config(proxy.get(), &profile); err != 0) {
+    if (int err = proxy_prepare_from_default_config(proxy.get(), proxy.getProfile()); err != 0) {
         LOG(FATAL) << __func__ << ": fail to prepare for device address=" << deviceProfile
                    << " error=" << err;
-        return nullptr;
+        return DeviceProxy();
     }
     if (int err = proxy_open(proxy.get()); err != 0) {
         LOG(ERROR) << __func__ << ": failed to open device, address=" << deviceProfile
                    << " error=" << err;
-        return nullptr;
+        return DeviceProxy();
     }
     return proxy;
 }
@@ -303,42 +305,36 @@
     if (!deviceProfile.isExternal) {
         LOG(FATAL) << __func__ << ": called for an attached device, address=" << deviceProfile;
     }
-    auto profile = readAlsaDeviceInfo(deviceProfile);
-    if (!profile.has_value()) {
-        LOG(ERROR) << __func__ << ": unable to read device info, device address=" << deviceProfile;
-        return nullptr;
+    auto proxy = readAlsaDeviceInfo(deviceProfile);
+    if (proxy.get() == nullptr) {
+        return proxy;
     }
-    auto proxy = makeDeviceProxy();
-    if (int err = proxy_prepare(proxy.get(), &profile.value(), pcmConfig, requireExactMatch);
+    if (int err = proxy_prepare(proxy.get(), proxy.getProfile(), pcmConfig, requireExactMatch);
         err != 0) {
         LOG(ERROR) << __func__ << ": fail to prepare for device address=" << deviceProfile
                    << " error=" << err;
-        return nullptr;
+        return DeviceProxy();
     }
     if (int err = proxy_open(proxy.get()); err != 0) {
         LOG(ERROR) << __func__ << ": failed to open device, address=" << deviceProfile
                    << " error=" << err;
-        return nullptr;
+        return DeviceProxy();
     }
     return proxy;
 }
 
-std::optional<alsa_device_profile> readAlsaDeviceInfo(const DeviceProfile& deviceProfile) {
-    alsa_device_profile profile;
-    profile_init(&profile, deviceProfile.direction);
-    profile.card = deviceProfile.card;
-    profile.device = deviceProfile.device;
-    if (!profile_read_device_info(&profile)) {
-        LOG(ERROR) << __func__ << ": failed to read device info, card=" << profile.card
-                   << ", device=" << profile.device;
-        return std::nullopt;
+DeviceProxy readAlsaDeviceInfo(const DeviceProfile& deviceProfile) {
+    DeviceProxy proxy(deviceProfile);
+    if (!profile_read_device_info(proxy.getProfile())) {
+        LOG(ERROR) << __func__ << ": unable to read device info, device address=" << deviceProfile;
+        return DeviceProxy();
     }
-    return profile;
+    return proxy;
 }
 
 void resetTransferredFrames(DeviceProxy& proxy, uint64_t frames) {
-    if (proxy != nullptr) {
-        proxy->transferred = frames;
+    if (proxy.get() != nullptr) {
+        proxy.get()->transferred = frames;
     }
 }
 
diff --git a/audio/aidl/default/alsa/Utils.h b/audio/aidl/default/alsa/Utils.h
index 37414b3..980f685 100644
--- a/audio/aidl/default/alsa/Utils.h
+++ b/audio/aidl/default/alsa/Utils.h
@@ -43,8 +43,21 @@
     bool isExternal;
 };
 std::ostream& operator<<(std::ostream& os, const DeviceProfile& device);
-using DeviceProxyDeleter = std::function<void(alsa_device_proxy*)>;
-using DeviceProxy = std::unique_ptr<alsa_device_proxy, DeviceProxyDeleter>;
+
+class DeviceProxy {
+  public:
+    DeviceProxy();  // Constructs a "null" proxy.
+    explicit DeviceProxy(const DeviceProfile& deviceProfile);
+    alsa_device_profile* getProfile() { return mProfile.get(); }
+    alsa_device_proxy* get() { return mProxy.get(); }
+
+  private:
+    static void alsaProxyDeleter(alsa_device_proxy* proxy);
+    using AlsaProxy = std::unique_ptr<alsa_device_proxy, decltype(alsaProxyDeleter)*>;
+
+    std::unique_ptr<alsa_device_profile> mProfile;
+    AlsaProxy mProxy;
+};
 
 ::aidl::android::media::audio::common::AudioChannelLayout getChannelLayoutMaskFromChannelCount(
         unsigned int channelCount, int isInput);
@@ -60,12 +73,11 @@
         const ::aidl::android::media::audio::common::AudioPort& audioPort);
 std::optional<struct pcm_config> getPcmConfig(const StreamContext& context, bool isInput);
 std::vector<int> getSampleRatesFromProfile(const alsa_device_profile* profile);
-DeviceProxy makeDeviceProxy();
 DeviceProxy openProxyForAttachedDevice(const DeviceProfile& deviceProfile,
                                        struct pcm_config* pcmConfig, size_t bufferFrameCount);
 DeviceProxy openProxyForExternalDevice(const DeviceProfile& deviceProfile,
                                        struct pcm_config* pcmConfig, bool requireExactMatch);
-std::optional<alsa_device_profile> readAlsaDeviceInfo(const DeviceProfile& deviceProfile);
+DeviceProxy readAlsaDeviceInfo(const DeviceProfile& deviceProfile);
 void resetTransferredFrames(DeviceProxy& proxy, uint64_t frames);
 
 ::aidl::android::media::audio::common::AudioFormatDescription
diff --git a/audio/aidl/default/android.hardware.audio.effect.service-aidl.xml b/audio/aidl/default/android.hardware.audio.effect.service-aidl.xml
index fdc53a3..05a825d 100644
--- a/audio/aidl/default/android.hardware.audio.effect.service-aidl.xml
+++ b/audio/aidl/default/android.hardware.audio.effect.service-aidl.xml
@@ -1,7 +1,7 @@
 <manifest version="1.0" type="device">
   <hal format="aidl">
     <name>android.hardware.audio.effect</name>
-    <version>1</version>
+    <version>2</version>
     <fqname>IFactory/default</fqname>
   </hal>
 </manifest>
diff --git a/audio/aidl/default/android.hardware.audio.service-aidl.xml b/audio/aidl/default/android.hardware.audio.service-aidl.xml
index 9db6061..2a51876 100644
--- a/audio/aidl/default/android.hardware.audio.service-aidl.xml
+++ b/audio/aidl/default/android.hardware.audio.service-aidl.xml
@@ -1,16 +1,27 @@
 <manifest version="1.0" type="device">
   <hal format="aidl">
     <name>android.hardware.audio.core</name>
-    <version>1</version>
+    <version>2</version>
     <fqname>IModule/default</fqname>
   </hal>
   <hal format="aidl">
     <name>android.hardware.audio.core</name>
-    <version>1</version>
+    <version>2</version>
     <fqname>IModule/r_submix</fqname>
   </hal>
   <hal format="aidl">
     <name>android.hardware.audio.core</name>
+    <version>2</version>
+    <fqname>IModule/bluetooth</fqname>
+  </hal>
+  <hal format="aidl">
+    <name>android.hardware.audio.core</name>
+    <version>2</version>
+    <fqname>IConfig/default</fqname>
+  </hal>
+  <!-- Uncomment when these modules present in the configuration
+  <hal format="aidl">
+    <name>android.hardware.audio.core</name>
     <version>1</version>
     <fqname>IModule/stub</fqname>
   </hal>
@@ -19,14 +30,5 @@
     <version>1</version>
     <fqname>IModule/usb</fqname>
   </hal>
-  <hal format="aidl">
-    <name>android.hardware.audio.core</name>
-    <version>1</version>
-    <fqname>IModule/bluetooth</fqname>
-  </hal>
-  <hal format="aidl">
-    <name>android.hardware.audio.core</name>
-    <version>1</version>
-    <fqname>IConfig/default</fqname>
-  </hal>
+  -->
 </manifest>
diff --git a/audio/aidl/default/bluetooth/DevicePortProxy.cpp b/audio/aidl/default/bluetooth/DevicePortProxy.cpp
index 12e204a..1be0875 100644
--- a/audio/aidl/default/bluetooth/DevicePortProxy.cpp
+++ b/audio/aidl/default/bluetooth/DevicePortProxy.cpp
@@ -19,11 +19,25 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <audio_utils/primitives.h>
-#include <inttypes.h>
 #include <log/log.h>
 
+#include "BluetoothAudioSessionControl.h"
 #include "core-impl/DevicePortProxy.h"
 
+using aidl::android::hardware::audio::common::SinkMetadata;
+using aidl::android::hardware::audio::common::SourceMetadata;
+using aidl::android::hardware::bluetooth::audio::AudioConfiguration;
+using aidl::android::hardware::bluetooth::audio::BluetoothAudioSessionControl;
+using aidl::android::hardware::bluetooth::audio::BluetoothAudioStatus;
+using aidl::android::hardware::bluetooth::audio::ChannelMode;
+using aidl::android::hardware::bluetooth::audio::PcmConfiguration;
+using aidl::android::hardware::bluetooth::audio::PortStatusCallbacks;
+using aidl::android::hardware::bluetooth::audio::PresentationPosition;
+using aidl::android::hardware::bluetooth::audio::SessionType;
+using aidl::android::media::audio::common::AudioDeviceDescription;
+using aidl::android::media::audio::common::AudioDeviceType;
+using android::base::StringPrintf;
+
 namespace android::bluetooth::audio::aidl {
 
 namespace {
@@ -33,20 +47,6 @@
 
 }  // namespace
 
-using ::aidl::android::hardware::audio::common::SinkMetadata;
-using ::aidl::android::hardware::audio::common::SourceMetadata;
-using ::aidl::android::hardware::bluetooth::audio::AudioConfiguration;
-using ::aidl::android::hardware::bluetooth::audio::BluetoothAudioSessionControl;
-using ::aidl::android::hardware::bluetooth::audio::BluetoothAudioStatus;
-using ::aidl::android::hardware::bluetooth::audio::ChannelMode;
-using ::aidl::android::hardware::bluetooth::audio::PcmConfiguration;
-using ::aidl::android::hardware::bluetooth::audio::PortStatusCallbacks;
-using ::aidl::android::hardware::bluetooth::audio::PresentationPosition;
-using ::aidl::android::hardware::bluetooth::audio::SessionType;
-using ::aidl::android::media::audio::common::AudioDeviceDescription;
-using ::aidl::android::media::audio::common::AudioDeviceType;
-using ::android::base::StringPrintf;
-
 std::ostream& operator<<(std::ostream& os, const BluetoothStreamState& state) {
     switch (state) {
         case BluetoothStreamState::DISABLED:
diff --git a/audio/aidl/default/bluetooth/ModuleBluetooth.cpp b/audio/aidl/default/bluetooth/ModuleBluetooth.cpp
index bfe7ca0..502b153 100644
--- a/audio/aidl/default/bluetooth/ModuleBluetooth.cpp
+++ b/audio/aidl/default/bluetooth/ModuleBluetooth.cpp
@@ -18,42 +18,56 @@
 
 #include <android-base/logging.h>
 
+#include "BluetoothAudioSession.h"
 #include "core-impl/ModuleBluetooth.h"
 #include "core-impl/StreamBluetooth.h"
 
-namespace aidl::android::hardware::audio::core {
-
 using aidl::android::hardware::audio::common::SinkMetadata;
 using aidl::android::hardware::audio::common::SourceMetadata;
+using aidl::android::media::audio::common::AudioDeviceDescription;
+using aidl::android::media::audio::common::AudioDeviceType;
 using aidl::android::media::audio::common::AudioOffloadInfo;
+using aidl::android::media::audio::common::AudioPort;
+using aidl::android::media::audio::common::AudioPortExt;
 using aidl::android::media::audio::common::MicrophoneInfo;
+using android::bluetooth::audio::aidl::BluetoothAudioPortAidl;
+using android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut;
+
+namespace aidl::android::hardware::audio::core {
 
 ndk::ScopedAStatus ModuleBluetooth::getBluetoothA2dp(
         std::shared_ptr<IBluetoothA2dp>* _aidl_return) {
-    if (!mBluetoothA2dp) {
-        auto handle = ndk::SharedRefBase::make<BluetoothA2dp>();
-        handle->registerHandler(std::bind(&ModuleBluetooth::bluetoothParametersUpdated, this));
-        mBluetoothA2dp = handle;
-    }
-    *_aidl_return = mBluetoothA2dp.getInstance();
+    *_aidl_return = getBtA2dp().getInstance();
     LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: " << _aidl_return->get();
     return ndk::ScopedAStatus::ok();
 }
 
 ndk::ScopedAStatus ModuleBluetooth::getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) {
+    *_aidl_return = getBtLe().getInstance();
+    LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get();
+    return ndk::ScopedAStatus::ok();
+}
+
+ChildInterface<BluetoothA2dp>& ModuleBluetooth::getBtA2dp() {
+    if (!mBluetoothA2dp) {
+        auto handle = ndk::SharedRefBase::make<BluetoothA2dp>();
+        handle->registerHandler(std::bind(&ModuleBluetooth::bluetoothParametersUpdated, this));
+        mBluetoothA2dp = handle;
+    }
+    return mBluetoothA2dp;
+}
+
+ChildInterface<BluetoothLe>& ModuleBluetooth::getBtLe() {
     if (!mBluetoothLe) {
         auto handle = ndk::SharedRefBase::make<BluetoothLe>();
         handle->registerHandler(std::bind(&ModuleBluetooth::bluetoothParametersUpdated, this));
         mBluetoothLe = handle;
     }
-    *_aidl_return = mBluetoothLe.getInstance();
-    LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get();
-    return ndk::ScopedAStatus::ok();
+    return mBluetoothLe;
 }
 
-Module::BtProfileHandles ModuleBluetooth::getBtProfileManagerHandles() {
-    return std::make_tuple(std::weak_ptr<IBluetooth>(), mBluetoothA2dp.getInstance(),
-                           mBluetoothLe.getInstance());
+ModuleBluetooth::BtProfileHandles ModuleBluetooth::getBtProfileManagerHandles() {
+    return std::make_tuple(std::weak_ptr<IBluetooth>(), getBtA2dp().getPtr(), getBtLe().getPtr());
 }
 
 ndk::ScopedAStatus ModuleBluetooth::getMicMute(bool* _aidl_return __unused) {
@@ -80,6 +94,54 @@
                                                     offloadInfo, getBtProfileManagerHandles());
 }
 
+ndk::ScopedAStatus ModuleBluetooth::populateConnectedDevicePort(AudioPort* audioPort) {
+    if (audioPort->ext.getTag() != AudioPortExt::device) {
+        LOG(ERROR) << __func__ << ": not a device port: " << audioPort->toString();
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    const auto& devicePort = audioPort->ext.get<AudioPortExt::device>();
+    const auto& description = devicePort.device.type;
+    // Since the configuration of the BT module is static, there is nothing to populate here.
+    // However, this method must return an error when the device can not be connected,
+    // this is determined by the status of BT profiles.
+    if (description.connection == AudioDeviceDescription::CONNECTION_BT_A2DP) {
+        bool isA2dpEnabled = false;
+        if (!!mBluetoothA2dp) {
+            RETURN_STATUS_IF_ERROR((*mBluetoothA2dp).isEnabled(&isA2dpEnabled));
+        }
+        LOG(DEBUG) << __func__ << ": isA2dpEnabled: " << isA2dpEnabled;
+        return isA2dpEnabled ? ndk::ScopedAStatus::ok()
+                             : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    } else if (description.connection == AudioDeviceDescription::CONNECTION_BT_LE) {
+        bool isLeEnabled = false;
+        if (!!mBluetoothLe) {
+            RETURN_STATUS_IF_ERROR((*mBluetoothLe).isEnabled(&isLeEnabled));
+        }
+        LOG(DEBUG) << __func__ << ": isLeEnabled: " << isLeEnabled;
+        return isLeEnabled ? ndk::ScopedAStatus::ok()
+                           : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    } else if (description.connection == AudioDeviceDescription::CONNECTION_WIRELESS &&
+               description.type == AudioDeviceType::OUT_HEARING_AID) {
+        // Hearing aids can use a number of profiles, thus the only way to check
+        // connectivity is to try to talk to the BT HAL.
+        if (!::aidl::android::hardware::bluetooth::audio::BluetoothAudioSession::
+                    IsAidlAvailable()) {
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+        }
+        std::shared_ptr<BluetoothAudioPortAidl> proxy = std::shared_ptr<BluetoothAudioPortAidl>(
+                std::make_shared<BluetoothAudioPortAidlOut>());
+        if (proxy->registerPort(description)) {
+            LOG(DEBUG) << __func__ << ": registered hearing aid port";
+            proxy->unregisterPort();
+            return ndk::ScopedAStatus::ok();
+        }
+        LOG(DEBUG) << __func__ << ": failed to register hearing aid port";
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    LOG(ERROR) << __func__ << ": unsupported device type: " << audioPort->toString();
+    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+}
+
 ndk::ScopedAStatus ModuleBluetooth::onMasterMuteChanged(bool) {
     LOG(DEBUG) << __func__ << ": is not supported";
     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
diff --git a/audio/aidl/default/bluetooth/StreamBluetooth.cpp b/audio/aidl/default/bluetooth/StreamBluetooth.cpp
index 91a33c2..0cee7f4 100644
--- a/audio/aidl/default/bluetooth/StreamBluetooth.cpp
+++ b/audio/aidl/default/bluetooth/StreamBluetooth.cpp
@@ -20,52 +20,49 @@
 #include <android-base/logging.h>
 #include <audio_utils/clock.h>
 
-#include "BluetoothAudioSessionControl.h"
+#include "BluetoothAudioSession.h"
 #include "core-impl/StreamBluetooth.h"
 
-namespace aidl::android::hardware::audio::core {
+using aidl::android::hardware::audio::common::frameCountFromDurationUs;
+using aidl::android::hardware::audio::common::SinkMetadata;
+using aidl::android::hardware::audio::common::SourceMetadata;
+using aidl::android::hardware::audio::core::VendorParameter;
+using aidl::android::hardware::bluetooth::audio::ChannelMode;
+using aidl::android::hardware::bluetooth::audio::PcmConfiguration;
+using aidl::android::hardware::bluetooth::audio::PresentationPosition;
+using aidl::android::media::audio::common::AudioChannelLayout;
+using aidl::android::media::audio::common::AudioDevice;
+using aidl::android::media::audio::common::AudioDeviceAddress;
+using aidl::android::media::audio::common::AudioFormatDescription;
+using aidl::android::media::audio::common::AudioFormatType;
+using aidl::android::media::audio::common::AudioOffloadInfo;
+using aidl::android::media::audio::common::MicrophoneDynamicInfo;
+using aidl::android::media::audio::common::MicrophoneInfo;
+using android::bluetooth::audio::aidl::BluetoothAudioPortAidl;
+using android::bluetooth::audio::aidl::BluetoothAudioPortAidlIn;
+using android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut;
+using android::bluetooth::audio::aidl::BluetoothStreamState;
 
-using ::aidl::android::hardware::audio::common::SinkMetadata;
-using ::aidl::android::hardware::audio::common::SourceMetadata;
-using ::aidl::android::hardware::audio::core::VendorParameter;
-using ::aidl::android::hardware::bluetooth::audio::ChannelMode;
-using ::aidl::android::hardware::bluetooth::audio::PcmConfiguration;
-using ::aidl::android::hardware::bluetooth::audio::PresentationPosition;
-using ::aidl::android::media::audio::common::AudioChannelLayout;
-using ::aidl::android::media::audio::common::AudioDevice;
-using ::aidl::android::media::audio::common::AudioDeviceAddress;
-using ::aidl::android::media::audio::common::AudioFormatDescription;
-using ::aidl::android::media::audio::common::AudioFormatType;
-using ::aidl::android::media::audio::common::AudioOffloadInfo;
-using ::aidl::android::media::audio::common::MicrophoneDynamicInfo;
-using ::aidl::android::media::audio::common::MicrophoneInfo;
-using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidl;
-using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidlIn;
-using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut;
-using ::android::bluetooth::audio::aidl::BluetoothStreamState;
+namespace aidl::android::hardware::audio::core {
 
 constexpr int kBluetoothDefaultInputBufferMs = 20;
 constexpr int kBluetoothDefaultOutputBufferMs = 10;
 // constexpr int kBluetoothSpatializerOutputBufferMs = 10;
 
-size_t getFrameCount(uint64_t durationUs, uint32_t sampleRate) {
-    return (durationUs * sampleRate) / 1000000;
-}
-
 // pcm configuration params are not really used by the module
 StreamBluetooth::StreamBluetooth(StreamContext* context, const Metadata& metadata,
-                                 Module::BtProfileHandles&& btHandles)
+                                 ModuleBluetooth::BtProfileHandles&& btHandles)
     : StreamCommonImpl(context, metadata),
       mSampleRate(getContext().getSampleRate()),
       mChannelLayout(getContext().getChannelLayout()),
       mFormat(getContext().getFormat()),
       mFrameSizeBytes(getContext().getFrameSize()),
       mIsInput(isInput(metadata)),
-      mBluetoothA2dp(std::move(std::get<Module::BtInterface::BTA2DP>(btHandles))),
-      mBluetoothLe(std::move(std::get<Module::BtInterface::BTLE>(btHandles))) {
+      mBluetoothA2dp(std::move(std::get<ModuleBluetooth::BtInterface::BTA2DP>(btHandles))),
+      mBluetoothLe(std::move(std::get<ModuleBluetooth::BtInterface::BTLE>(btHandles))) {
     mPreferredDataIntervalUs =
-            mIsInput ? kBluetoothDefaultInputBufferMs : kBluetoothDefaultOutputBufferMs;
-    mPreferredFrameCount = getFrameCount(mPreferredDataIntervalUs, mSampleRate);
+            (mIsInput ? kBluetoothDefaultInputBufferMs : kBluetoothDefaultOutputBufferMs) * 1000;
+    mPreferredFrameCount = frameCountFromDurationUs(mPreferredDataIntervalUs, mSampleRate);
     mIsInitialized = false;
     mIsReadyToClose = false;
 }
@@ -226,7 +223,7 @@
     if (config.dataIntervalUs > 0) {
         mPreferredDataIntervalUs =
                 std::min((int32_t)mPreferredDataIntervalUs, config.dataIntervalUs);
-        mPreferredFrameCount = getFrameCount(mPreferredDataIntervalUs, mSampleRate);
+        mPreferredFrameCount = frameCountFromDurationUs(mPreferredDataIntervalUs, mSampleRate);
     }
     return true;
 }
@@ -318,7 +315,7 @@
 
 StreamInBluetooth::StreamInBluetooth(StreamContext&& context, const SinkMetadata& sinkMetadata,
                                      const std::vector<MicrophoneInfo>& microphones,
-                                     Module::BtProfileHandles&& btProfileHandles)
+                                     ModuleBluetooth::BtProfileHandles&& btProfileHandles)
     : StreamIn(std::move(context), microphones),
       StreamBluetooth(&mContextInstance, sinkMetadata, std::move(btProfileHandles)) {}
 
@@ -331,7 +328,7 @@
 StreamOutBluetooth::StreamOutBluetooth(StreamContext&& context,
                                        const SourceMetadata& sourceMetadata,
                                        const std::optional<AudioOffloadInfo>& offloadInfo,
-                                       Module::BtProfileHandles&& btProfileHandles)
+                                       ModuleBluetooth::BtProfileHandles&& btProfileHandles)
     : StreamOut(std::move(context), offloadInfo),
       StreamBluetooth(&mContextInstance, sourceMetadata, std::move(btProfileHandles)) {}
 
diff --git a/audio/aidl/default/config/audioPolicy/api/current.txt b/audio/aidl/default/config/audioPolicy/api/current.txt
index e2bc833..3547f54 100644
--- a/audio/aidl/default/config/audioPolicy/api/current.txt
+++ b/audio/aidl/default/config/audioPolicy/api/current.txt
@@ -85,16 +85,6 @@
     enum_constant public static final android.audio.policy.configuration.AudioChannelMask AUDIO_CHANNEL_OUT_TRI_BACK;
   }
 
-  public enum AudioContentType {
-    method @NonNull public String getRawName();
-    enum_constant public static final android.audio.policy.configuration.AudioContentType AUDIO_CONTENT_TYPE_MOVIE;
-    enum_constant public static final android.audio.policy.configuration.AudioContentType AUDIO_CONTENT_TYPE_MUSIC;
-    enum_constant public static final android.audio.policy.configuration.AudioContentType AUDIO_CONTENT_TYPE_SONIFICATION;
-    enum_constant public static final android.audio.policy.configuration.AudioContentType AUDIO_CONTENT_TYPE_SPEECH;
-    enum_constant public static final android.audio.policy.configuration.AudioContentType AUDIO_CONTENT_TYPE_ULTRASOUND;
-    enum_constant public static final android.audio.policy.configuration.AudioContentType AUDIO_CONTENT_TYPE_UNKNOWN;
-  }
-
   public enum AudioDevice {
     method @NonNull public String getRawName();
     enum_constant public static final android.audio.policy.configuration.AudioDevice AUDIO_DEVICE_IN_AMBIENT;
@@ -168,13 +158,6 @@
     enum_constant public static final android.audio.policy.configuration.AudioDevice AUDIO_DEVICE_OUT_WIRED_HEADSET;
   }
 
-  public enum AudioEncapsulationType {
-    method @NonNull public String getRawName();
-    enum_constant public static final android.audio.policy.configuration.AudioEncapsulationType AUDIO_ENCAPSULATION_TYPE_IEC61937;
-    enum_constant public static final android.audio.policy.configuration.AudioEncapsulationType AUDIO_ENCAPSULATION_TYPE_NONE;
-    enum_constant public static final android.audio.policy.configuration.AudioEncapsulationType AUDIO_ENCAPSULATION_TYPE_PCM;
-  }
-
   public enum AudioFormat {
     method @NonNull public String getRawName();
     enum_constant public static final android.audio.policy.configuration.AudioFormat AUDIO_FORMAT_AAC;
@@ -359,29 +342,6 @@
     enum_constant public static final android.audio.policy.configuration.AudioStreamType AUDIO_STREAM_VOICE_CALL;
   }
 
-  public enum AudioUsage {
-    method @NonNull public String getRawName();
-    enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_ALARM;
-    enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_ANNOUNCEMENT;
-    enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY;
-    enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
-    enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_ASSISTANCE_SONIFICATION;
-    enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_ASSISTANT;
-    enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_CALL_ASSISTANT;
-    enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_EMERGENCY;
-    enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_GAME;
-    enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_MEDIA;
-    enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_NOTIFICATION;
-    enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_NOTIFICATION_EVENT;
-    enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE;
-    enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_SAFETY;
-    enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_UNKNOWN;
-    enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_VEHICLE_STATUS;
-    enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_VIRTUAL_SOURCE;
-    enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_VOICE_COMMUNICATION;
-    enum_constant public static final android.audio.policy.configuration.AudioUsage AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
-  }
-
   public enum DeviceCategory {
     method @NonNull public String getRawName();
     enum_constant public static final android.audio.policy.configuration.DeviceCategory DEVICE_CATEGORY_EARPIECE;
@@ -435,7 +395,6 @@
     method @Nullable public int getMinRampMs();
     method @Nullable public int getMinValueMB();
     method @Nullable public java.util.List<android.audio.policy.configuration.AudioGainMode> getMode();
-    method @Nullable public String getName();
     method @Nullable public int getStepValueMB();
     method @Nullable public boolean getUseForVolume();
     method public void setChannel_mask(@Nullable android.audio.policy.configuration.AudioChannelMask);
@@ -445,7 +404,6 @@
     method public void setMinRampMs(@Nullable int);
     method public void setMinValueMB(@Nullable int);
     method public void setMode(@Nullable java.util.List<android.audio.policy.configuration.AudioGainMode>);
-    method public void setName(@Nullable String);
     method public void setStepValueMB(@Nullable int);
     method public void setUseForVolume(@Nullable boolean);
   }
@@ -478,7 +436,6 @@
     method @Nullable public long getMaxActiveCount();
     method @Nullable public long getMaxOpenCount();
     method @Nullable public String getName();
-    method @Nullable public java.util.List<android.audio.policy.configuration.AudioUsage> getPreferredUsage();
     method @Nullable public java.util.List<android.audio.policy.configuration.Profile> getProfile();
     method @Nullable public long getRecommendedMuteDurationMs();
     method @Nullable public android.audio.policy.configuration.Role getRole();
@@ -487,7 +444,6 @@
     method public void setMaxActiveCount(@Nullable long);
     method public void setMaxOpenCount(@Nullable long);
     method public void setName(@Nullable String);
-    method public void setPreferredUsage(@Nullable java.util.List<android.audio.policy.configuration.AudioUsage>);
     method public void setRecommendedMuteDurationMs(@Nullable long);
     method public void setRole(@Nullable android.audio.policy.configuration.Role);
   }
@@ -524,14 +480,10 @@
   public class Profile {
     ctor public Profile();
     method @Nullable public java.util.List<android.audio.policy.configuration.AudioChannelMask> getChannelMasks();
-    method @Nullable public android.audio.policy.configuration.AudioEncapsulationType getEncapsulationType();
     method @Nullable public String getFormat();
-    method @Nullable public String getName();
     method @Nullable public java.util.List<java.math.BigInteger> getSamplingRates();
     method public void setChannelMasks(@Nullable java.util.List<android.audio.policy.configuration.AudioChannelMask>);
-    method public void setEncapsulationType(@Nullable android.audio.policy.configuration.AudioEncapsulationType);
     method public void setFormat(@Nullable String);
-    method public void setName(@Nullable String);
     method public void setSamplingRates(@Nullable java.util.List<java.math.BigInteger>);
   }
 
diff --git a/audio/aidl/default/config/audioPolicy/audio_policy_configuration.xsd b/audio/aidl/default/config/audioPolicy/audio_policy_configuration.xsd
index 9a3a447..d93f697 100644
--- a/audio/aidl/default/config/audioPolicy/audio_policy_configuration.xsd
+++ b/audio/aidl/default/config/audioPolicy/audio_policy_configuration.xsd
@@ -217,20 +217,6 @@
                     <xs:attribute name="flags" type="audioInOutFlags"/>
                     <xs:attribute name="maxOpenCount" type="xs:unsignedInt"/>
                     <xs:attribute name="maxActiveCount" type="xs:unsignedInt"/>
-                    <xs:attribute name="preferredUsage" type="audioUsageList">
-                        <xs:annotation>
-                            <xs:documentation xml:lang="en">
-                                When choosing the mixPort of an audio track, the audioPolicy
-                                first considers the mixPorts with a preferredUsage including
-                                the track AudioUsage preferred .
-                                If non support the track format, the other mixPorts are considered.
-                                Eg: a <mixPort preferredUsage="AUDIO_USAGE_MEDIA" /> will receive
-                                    the audio of all apps playing with a MEDIA usage.
-                                    It may receive audio from ALARM if there are no audio compatible
-                                    <mixPort preferredUsage="AUDIO_USAGE_ALARM" />.
-                             </xs:documentation>
-                        </xs:annotation>
-                    </xs:attribute>
                     <xs:attribute name="recommendedMuteDurationMs" type="xs:unsignedInt"/>
                 </xs:complexType>
                 <xs:unique name="mixPortProfileUniqueness">
@@ -434,56 +420,6 @@
     <xs:simpleType name="extendableAudioFormat">
         <xs:union memberTypes="audioFormat vendorExtension"/>
     </xs:simpleType>
-    <xs:simpleType name="audioUsage">
-        <xs:annotation>
-            <xs:documentation xml:lang="en">
-                Audio usage specifies the intended use case for the sound being played.
-                Please consult frameworks/base/media/java/android/media/AudioAttributes.java
-                for the description of each value.
-            </xs:documentation>
-        </xs:annotation>
-        <xs:restriction base="xs:string">
-            <xs:enumeration value="AUDIO_USAGE_UNKNOWN" />
-            <xs:enumeration value="AUDIO_USAGE_MEDIA" />
-            <xs:enumeration value="AUDIO_USAGE_VOICE_COMMUNICATION" />
-            <xs:enumeration value="AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING" />
-            <xs:enumeration value="AUDIO_USAGE_ALARM" />
-            <xs:enumeration value="AUDIO_USAGE_NOTIFICATION" />
-            <xs:enumeration value="AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE" />
-            <xs:enumeration value="AUDIO_USAGE_NOTIFICATION_EVENT" />
-            <xs:enumeration value="AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY" />
-            <xs:enumeration value="AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE" />
-            <xs:enumeration value="AUDIO_USAGE_ASSISTANCE_SONIFICATION" />
-            <xs:enumeration value="AUDIO_USAGE_GAME" />
-            <xs:enumeration value="AUDIO_USAGE_VIRTUAL_SOURCE" />
-            <xs:enumeration value="AUDIO_USAGE_ASSISTANT" />
-            <xs:enumeration value="AUDIO_USAGE_CALL_ASSISTANT" />
-            <xs:enumeration value="AUDIO_USAGE_EMERGENCY" />
-            <xs:enumeration value="AUDIO_USAGE_SAFETY" />
-            <xs:enumeration value="AUDIO_USAGE_VEHICLE_STATUS" />
-            <xs:enumeration value="AUDIO_USAGE_ANNOUNCEMENT" />
-        </xs:restriction>
-    </xs:simpleType>
-    <xs:simpleType name="audioUsageList">
-        <xs:list itemType="audioUsage"/>
-    </xs:simpleType>
-    <xs:simpleType name="audioContentType">
-        <xs:annotation>
-            <xs:documentation xml:lang="en">
-                Audio content type expresses the general category of the content.
-                Please consult frameworks/base/media/java/android/media/AudioAttributes.java
-                for the description of each value.
-            </xs:documentation>
-        </xs:annotation>
-        <xs:restriction base="xs:string">
-            <xs:enumeration value="AUDIO_CONTENT_TYPE_UNKNOWN"/>
-            <xs:enumeration value="AUDIO_CONTENT_TYPE_SPEECH"/>
-            <xs:enumeration value="AUDIO_CONTENT_TYPE_MUSIC"/>
-            <xs:enumeration value="AUDIO_CONTENT_TYPE_MOVIE"/>
-            <xs:enumeration value="AUDIO_CONTENT_TYPE_SONIFICATION"/>
-            <xs:enumeration value="AUDIO_CONTENT_TYPE_ULTRASOUND"/>
-        </xs:restriction>
-    </xs:simpleType>
     <xs:simpleType name="samplingRates">
         <xs:list itemType="xs:nonNegativeInteger" />
     </xs:simpleType>
@@ -579,19 +515,10 @@
     <xs:simpleType name="channelMasks">
         <xs:list itemType="audioChannelMask" />
     </xs:simpleType>
-    <xs:simpleType name="audioEncapsulationType">
-        <xs:restriction base="xs:string">
-            <xs:enumeration value="AUDIO_ENCAPSULATION_TYPE_NONE"/>
-            <xs:enumeration value="AUDIO_ENCAPSULATION_TYPE_IEC61937"/>
-            <xs:enumeration value="AUDIO_ENCAPSULATION_TYPE_PCM"/>
-        </xs:restriction>
-    </xs:simpleType>
     <xs:complexType name="profile">
-        <xs:attribute name="name" type="xs:token" use="optional"/>
         <xs:attribute name="format" type="extendableAudioFormat" use="optional"/>
         <xs:attribute name="samplingRates" type="samplingRates" use="optional"/>
         <xs:attribute name="channelMasks" type="channelMasks" use="optional"/>
-        <xs:attribute name="encapsulationType" type="audioEncapsulationType" use="optional"/>
     </xs:complexType>
     <xs:simpleType name="audioGainMode">
         <xs:restriction base="xs:string">
@@ -612,7 +539,6 @@
         <xs:sequence>
             <xs:element name="gain" minOccurs="0" maxOccurs="unbounded">
                 <xs:complexType>
-                    <xs:attribute name="name" type="xs:token" use="required"/>
                     <xs:attribute name="mode" type="audioGainModeMask" use="required"/>
                     <xs:attribute name="channel_mask" type="audioChannelMask" use="optional"/>
                     <xs:attribute name="minValueMB" type="xs:int" use="optional"/>
diff --git a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp
index ed6cfa0..e8f90b2 100644
--- a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp
+++ b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp
@@ -93,7 +93,7 @@
         .common = {.id = {.type = getEffectTypeUuidDynamicsProcessing(),
                           .uuid = getEffectImplUuidDynamicsProcessingSw(),
                           .proxy = std::nullopt},
-                   .flags = {.type = Flags::Type::INSERT,
+                   .flags = {.type = Flags::Type::POST_PROC,
                              .insert = Flags::Insert::FIRST,
                              .volume = Flags::Volume::CTRL},
                    .name = DynamicsProcessingSw::kEffectName,
diff --git a/audio/aidl/default/include/core-impl/AudioPolicyConfigXmlConverter.h b/audio/aidl/default/include/core-impl/AudioPolicyConfigXmlConverter.h
index 090d585..bff4b4a 100644
--- a/audio/aidl/default/include/core-impl/AudioPolicyConfigXmlConverter.h
+++ b/audio/aidl/default/include/core-impl/AudioPolicyConfigXmlConverter.h
@@ -16,27 +16,42 @@
 
 #pragma once
 
+#include <memory>
+#include <optional>
 #include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
 
 #include <aidl/android/hardware/audio/core/SurroundSoundConfig.h>
 #include <aidl/android/media/audio/common/AudioHalEngineConfig.h>
 #include <android_audio_policy_configuration.h>
 #include <android_audio_policy_configuration_enums.h>
+#include <media/AidlConversionUtil.h>
 
+#include "core-impl/Module.h"
 #include "core-impl/XmlConverter.h"
 
 namespace aidl::android::hardware::audio::core::internal {
 
 class AudioPolicyConfigXmlConverter {
   public:
+    using ModuleConfiguration = std::pair<std::string, std::unique_ptr<Module::Configuration>>;
+    using ModuleConfigs = std::vector<ModuleConfiguration>;
+
     explicit AudioPolicyConfigXmlConverter(const std::string& configFilePath)
-        : mConverter(configFilePath, &::android::audio::policy::configuration::read) {}
+        : mConverter(configFilePath, &::android::audio::policy::configuration::read) {
+        if (mConverter.getXsdcConfig()) {
+            init();
+        }
+    }
 
     std::string getError() const { return mConverter.getError(); }
     ::android::status_t getStatus() const { return mConverter.getStatus(); }
 
     const ::aidl::android::media::audio::common::AudioHalEngineConfig& getAidlEngineConfig();
     const SurroundSoundConfig& getSurroundSoundConfig();
+    std::unique_ptr<ModuleConfigs> releaseModuleConfigs();
 
     // Public for testing purposes.
     static const SurroundSoundConfig& getDefaultSurroundSoundConfig();
@@ -47,13 +62,13 @@
         return mConverter.getXsdcConfig();
     }
     void addVolumeGroupstoEngineConfig();
+    void init();
     void mapStreamToVolumeCurve(
             const ::android::audio::policy::configuration::Volume& xsdcVolumeCurve);
     void mapStreamsToVolumeCurves();
     void parseVolumes();
-    ::aidl::android::media::audio::common::AudioHalVolumeCurve::CurvePoint convertCurvePointToAidl(
-            const std::string& xsdcCurvePoint);
-    ::aidl::android::media::audio::common::AudioHalVolumeCurve convertVolumeCurveToAidl(
+    ConversionResult<::aidl::android::media::audio::common::AudioHalVolumeCurve>
+    convertVolumeCurveToAidl(
             const ::android::audio::policy::configuration::Volume& xsdcVolumeCurve);
 
     ::aidl::android::media::audio::common::AudioHalEngineConfig mAidlEngineConfig;
@@ -63,6 +78,7 @@
     std::unordered_map<::android::audio::policy::configuration::AudioStreamType,
                        std::vector<::aidl::android::media::audio::common::AudioHalVolumeCurve>>
             mStreamToVolumeCurvesMap;
+    std::unique_ptr<ModuleConfigs> mModuleConfigurations = std::make_unique<ModuleConfigs>();
 };
 
 }  // namespace aidl::android::hardware::audio::core::internal
diff --git a/audio/aidl/default/include/core-impl/Bluetooth.h b/audio/aidl/default/include/core-impl/Bluetooth.h
index 44899bc..002cb19 100644
--- a/audio/aidl/default/include/core-impl/Bluetooth.h
+++ b/audio/aidl/default/include/core-impl/Bluetooth.h
@@ -46,9 +46,9 @@
 class BluetoothA2dp : public BnBluetoothA2dp, public ParamChangeHandler {
   public:
     BluetoothA2dp() = default;
+    ndk::ScopedAStatus isEnabled(bool* _aidl_return) override;
 
   private:
-    ndk::ScopedAStatus isEnabled(bool* _aidl_return) override;
     ndk::ScopedAStatus setEnabled(bool in_enabled) override;
     ndk::ScopedAStatus supportsOffloadReconfiguration(bool* _aidl_return) override;
     ndk::ScopedAStatus reconfigureOffload(
@@ -61,9 +61,9 @@
 class BluetoothLe : public BnBluetoothLe, public ParamChangeHandler {
   public:
     BluetoothLe() = default;
+    ndk::ScopedAStatus isEnabled(bool* _aidl_return) override;
 
   private:
-    ndk::ScopedAStatus isEnabled(bool* _aidl_return) override;
     ndk::ScopedAStatus setEnabled(bool in_enabled) override;
     ndk::ScopedAStatus supportsOffloadReconfiguration(bool* _aidl_return) override;
     ndk::ScopedAStatus reconfigureOffload(
diff --git a/audio/aidl/default/include/core-impl/ChildInterface.h b/audio/aidl/default/include/core-impl/ChildInterface.h
index 2421b59..f5f1855 100644
--- a/audio/aidl/default/include/core-impl/ChildInterface.h
+++ b/audio/aidl/default/include/core-impl/ChildInterface.h
@@ -40,14 +40,19 @@
     explicit operator bool() const { return !!this->first; }
     C& operator*() const { return *(this->first); }
     C* operator->() const { return this->first; }
+    std::shared_ptr<C> getPtr() { return this->first; }
     // Use 'getInstance' when returning the interface instance.
     std::shared_ptr<C> getInstance() {
+        (void)getBinder();
+        return this->first;
+    }
+    AIBinder* getBinder() {
         if (this->second.get() == nullptr) {
             this->second = this->first->asBinder();
             AIBinder_setMinSchedulerPolicy(this->second.get(), SCHED_NORMAL,
                                            ANDROID_PRIORITY_AUDIO);
         }
-        return this->first;
+        return this->second.get();
     }
 };
 
diff --git a/audio/aidl/default/include/core-impl/Config.h b/audio/aidl/default/include/core-impl/Config.h
index 96a6cb9..63d4b3d 100644
--- a/audio/aidl/default/include/core-impl/Config.h
+++ b/audio/aidl/default/include/core-impl/Config.h
@@ -26,11 +26,16 @@
 static const std::string kEngineConfigFileName = "audio_policy_engine_configuration.xml";
 
 class Config : public BnConfig {
+  public:
+    explicit Config(internal::AudioPolicyConfigXmlConverter& apConverter)
+        : mAudioPolicyConverter(apConverter) {}
+
+  private:
     ndk::ScopedAStatus getSurroundSoundConfig(SurroundSoundConfig* _aidl_return) override;
     ndk::ScopedAStatus getEngineConfig(
             aidl::android::media::audio::common::AudioHalEngineConfig* _aidl_return) override;
-    internal::AudioPolicyConfigXmlConverter mAudioPolicyConverter{
-            ::android::audio_get_audio_policy_config_file()};
+
+    internal::AudioPolicyConfigXmlConverter& mAudioPolicyConverter;
     internal::EngineConfigXmlConverter mEngConfigConverter{
             ::android::audio_find_readable_configuration_file(kEngineConfigFileName.c_str())};
 };
diff --git a/audio/aidl/default/include/core-impl/Configuration.h b/audio/aidl/default/include/core-impl/Configuration.h
index 6277c38..a56c8c9 100644
--- a/audio/aidl/default/include/core-impl/Configuration.h
+++ b/audio/aidl/default/include/core-impl/Configuration.h
@@ -16,37 +16,14 @@
 
 #pragma once
 
-#include <map>
 #include <memory>
-#include <vector>
 
-#include <aidl/android/hardware/audio/core/AudioPatch.h>
-#include <aidl/android/hardware/audio/core/AudioRoute.h>
-#include <aidl/android/media/audio/common/AudioPort.h>
-#include <aidl/android/media/audio/common/AudioPortConfig.h>
-#include <aidl/android/media/audio/common/MicrophoneInfo.h>
+#include "Module.h"
 
 namespace aidl::android::hardware::audio::core::internal {
 
-struct Configuration {
-    std::vector<::aidl::android::media::audio::common::MicrophoneInfo> microphones;
-    std::vector<::aidl::android::media::audio::common::AudioPort> ports;
-    std::vector<::aidl::android::media::audio::common::AudioPortConfig> portConfigs;
-    std::vector<::aidl::android::media::audio::common::AudioPortConfig> initialConfigs;
-    // Port id -> List of profiles to use when the device port state is set to 'connected'
-    // in connection simulation mode.
-    std::map<int32_t, std::vector<::aidl::android::media::audio::common::AudioProfile>>
-            connectedProfiles;
-    std::vector<AudioRoute> routes;
-    std::vector<AudioPatch> patches;
-    int32_t nextPortId = 1;
-    int32_t nextPatchId = 1;
-};
-
-std::unique_ptr<Configuration> getPrimaryConfiguration();
-std::unique_ptr<Configuration> getRSubmixConfiguration();
-std::unique_ptr<Configuration> getStubConfiguration();
-std::unique_ptr<Configuration> getUsbConfiguration();
-std::unique_ptr<Configuration> getBluetoothConfiguration();
+std::unique_ptr<Module::Configuration> getConfiguration(Module::Type moduleType);
+std::vector<aidl::android::media::audio::common::AudioProfile>
+getStandard16And24BitPcmAudioProfiles();
 
 }  // namespace aidl::android::hardware::audio::core::internal
diff --git a/audio/aidl/default/include/core-impl/DevicePortProxy.h b/audio/aidl/default/include/core-impl/DevicePortProxy.h
index 13b8c91..17a8cf3 100644
--- a/audio/aidl/default/include/core-impl/DevicePortProxy.h
+++ b/audio/aidl/default/include/core-impl/DevicePortProxy.h
@@ -25,11 +25,10 @@
 #include <aidl/android/hardware/audio/common/SourceMetadata.h>
 #include <aidl/android/hardware/bluetooth/audio/BluetoothAudioStatus.h>
 #include <aidl/android/hardware/bluetooth/audio/PcmConfiguration.h>
+#include <aidl/android/hardware/bluetooth/audio/PresentationPosition.h>
 #include <aidl/android/hardware/bluetooth/audio/SessionType.h>
 #include <aidl/android/media/audio/common/AudioDeviceDescription.h>
 
-#include "BluetoothAudioSessionControl.h"
-
 namespace android::bluetooth::audio::aidl {
 
 enum class BluetoothStreamState : uint8_t {
@@ -239,4 +238,4 @@
     size_t readData(void* buffer, size_t bytes) const override;
 };
 
-}  // namespace android::bluetooth::audio::aidl
\ No newline at end of file
+}  // namespace android::bluetooth::audio::aidl
diff --git a/audio/aidl/default/include/core-impl/EngineConfigXmlConverter.h b/audio/aidl/default/include/core-impl/EngineConfigXmlConverter.h
index b34441d..22ac8cb 100644
--- a/audio/aidl/default/include/core-impl/EngineConfigXmlConverter.h
+++ b/audio/aidl/default/include/core-impl/EngineConfigXmlConverter.h
@@ -19,10 +19,9 @@
 #include <string>
 #include <unordered_map>
 
-#include <utils/Errors.h>
-
 #include <android_audio_policy_engine_configuration.h>
 #include <android_audio_policy_engine_configuration_enums.h>
+#include <media/AidlConversionUtil.h>
 
 #include "core-impl/XmlConverter.h"
 
@@ -49,29 +48,24 @@
     }
     void init();
     void initProductStrategyMap();
-    ::aidl::android::media::audio::common::AudioAttributes convertAudioAttributesToAidl(
+    ConversionResult<::aidl::android::media::audio::common::AudioAttributes>
+    convertAudioAttributesToAidl(
             const ::android::audio::policy::engine::configuration::AttributesType&
                     xsdcAudioAttributes);
-    ::aidl::android::media::audio::common::AudioHalAttributesGroup convertAttributesGroupToAidl(
+    ConversionResult<::aidl::android::media::audio::common::AudioHalAttributesGroup>
+    convertAttributesGroupToAidl(
             const ::android::audio::policy::engine::configuration::AttributesGroup&
                     xsdcAttributesGroup);
-    ::aidl::android::media::audio::common::AudioHalCapCriterion convertCapCriterionToAidl(
-            const ::android::audio::policy::engine::configuration::CriterionType& xsdcCriterion);
-    ::aidl::android::media::audio::common::AudioHalCapCriterionType convertCapCriterionTypeToAidl(
-            const ::android::audio::policy::engine::configuration::CriterionTypeType&
-                    xsdcCriterionType);
-    std::string convertCriterionTypeValueToAidl(
-            const ::android::audio::policy::engine::configuration::ValueType&
-                    xsdcCriterionTypeValue);
-    ::aidl::android::media::audio::common::AudioHalVolumeCurve::CurvePoint convertCurvePointToAidl(
-            const std::string& xsdcCurvePoint);
-    ::aidl::android::media::audio::common::AudioHalProductStrategy convertProductStrategyToAidl(
-            const ::android::audio::policy::engine::configuration::ProductStrategies::
-                    ProductStrategy& xsdcProductStrategy);
-    int convertProductStrategyNameToAidl(const std::string& xsdcProductStrategyName);
-    ::aidl::android::media::audio::common::AudioHalVolumeCurve convertVolumeCurveToAidl(
+    ConversionResult<::aidl::android::media::audio::common::AudioHalProductStrategy>
+    convertProductStrategyToAidl(const ::android::audio::policy::engine::configuration::
+                                         ProductStrategies::ProductStrategy& xsdcProductStrategy);
+    ConversionResult<int> convertProductStrategyNameToAidl(
+            const std::string& xsdcProductStrategyName);
+    ConversionResult<::aidl::android::media::audio::common::AudioHalVolumeCurve>
+    convertVolumeCurveToAidl(
             const ::android::audio::policy::engine::configuration::Volume& xsdcVolumeCurve);
-    ::aidl::android::media::audio::common::AudioHalVolumeGroup convertVolumeGroupToAidl(
+    ConversionResult<::aidl::android::media::audio::common::AudioHalVolumeGroup>
+    convertVolumeGroupToAidl(
             const ::android::audio::policy::engine::configuration::VolumeGroupsType::VolumeGroup&
                     xsdcVolumeGroup);
 
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index bfdab51..caf43f1 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -19,30 +19,42 @@
 #include <iostream>
 #include <map>
 #include <memory>
+#include <optional>
 #include <set>
 
+#include <Utils.h>
 #include <aidl/android/hardware/audio/core/BnModule.h>
 
 #include "core-impl/ChildInterface.h"
-#include "core-impl/Configuration.h"
 #include "core-impl/Stream.h"
 
 namespace aidl::android::hardware::audio::core {
 
 class Module : public BnModule {
   public:
-    // This value is used for all AudioPatches and reported by all streams.
-    static constexpr int32_t kLatencyMs = 10;
+    struct Configuration {
+        std::vector<::aidl::android::media::audio::common::AudioPort> ports;
+        std::vector<::aidl::android::media::audio::common::AudioPortConfig> portConfigs;
+        std::vector<::aidl::android::media::audio::common::AudioPortConfig> initialConfigs;
+        // Port id -> List of profiles to use when the device port state is set to 'connected'
+        // in connection simulation mode.
+        std::map<int32_t, std::vector<::aidl::android::media::audio::common::AudioProfile>>
+                connectedProfiles;
+        std::vector<AudioRoute> routes;
+        std::vector<AudioPatch> patches;
+        int32_t nextPortId = 1;
+        int32_t nextPatchId = 1;
+    };
     enum Type : int { DEFAULT, R_SUBMIX, STUB, USB, BLUETOOTH };
-    enum BtInterface : int { BTCONF, BTA2DP, BTLE };
 
-    static std::shared_ptr<Module> createInstance(Type type);
+    static std::shared_ptr<Module> createInstance(Type type) {
+        return createInstance(type, std::make_unique<Configuration>());
+    }
+    static std::shared_ptr<Module> createInstance(Type type,
+                                                  std::unique_ptr<Configuration>&& config);
+    static std::optional<Type> typeFromString(const std::string& type);
 
-    explicit Module(Type type) : mType(type) {}
-
-    typedef std::tuple<std::weak_ptr<IBluetooth>, std::weak_ptr<IBluetoothA2dp>,
-                       std::weak_ptr<IBluetoothLe>>
-            BtProfileHandles;
+    Module(Type type, std::unique_ptr<Configuration>&& config);
 
   protected:
     // The vendor extension done via inheritance can override interface methods and augment
@@ -127,8 +139,6 @@
     ndk::ScopedAStatus getAAudioMixerBurstCount(int32_t* _aidl_return) override;
     ndk::ScopedAStatus getAAudioHardwareBurstMinUsec(int32_t* _aidl_return) override;
 
-    // This value is used for all AudioPatches.
-    static constexpr int32_t kMinimumStreamBufferSizeFrames = 256;
     // The maximum stream buffer size is 1 GiB = 2 ** 30 bytes;
     static constexpr int32_t kMaximumStreamBufferSizeBytes = 1 << 30;
 
@@ -148,7 +158,7 @@
     using Patches = std::multimap<int32_t, int32_t>;
 
     const Type mType;
-    std::unique_ptr<internal::Configuration> mConfig;
+    std::unique_ptr<Configuration> mConfig;
     ModuleDebug mDebug;
     VendorDebug mVendorDebug;
     ConnectedDevicePorts mConnectedDevicePorts;
@@ -157,7 +167,7 @@
     bool mMicMute = false;
     bool mMasterMute = false;
     float mMasterVolume = 1.0f;
-    ChildInterface<sounddose::ISoundDose> mSoundDose;
+    ChildInterface<sounddose::SoundDose> mSoundDose;
     std::optional<bool> mIsMmapSupported;
 
   protected:
@@ -187,9 +197,21 @@
             const ::aidl::android::media::audio::common::AudioPort& audioPort, bool connected);
     virtual ndk::ScopedAStatus onMasterMuteChanged(bool mute);
     virtual ndk::ScopedAStatus onMasterVolumeChanged(float volume);
-    virtual std::unique_ptr<internal::Configuration> initializeConfig();
+    virtual std::vector<::aidl::android::media::audio::common::MicrophoneInfo> getMicrophoneInfos();
+    virtual std::unique_ptr<Configuration> initializeConfig();
+    virtual int32_t getNominalLatencyMs(
+            const ::aidl::android::media::audio::common::AudioPortConfig& portConfig);
 
     // Utility and helper functions accessible to subclasses.
+    static int32_t calculateBufferSizeFrames(int32_t latencyMs, int32_t sampleRateHz) {
+        const int32_t rawSizeFrames =
+                aidl::android::hardware::audio::common::frameCountFromDurationMs(latencyMs,
+                                                                                 sampleRateHz);
+        int32_t powerOf2 = 1;
+        while (powerOf2 < rawSizeFrames) powerOf2 <<= 1;
+        return powerOf2;
+    }
+
     ndk::ScopedAStatus bluetoothParametersUpdated();
     void cleanUpPatch(int32_t patchId);
     ndk::ScopedAStatus createStreamContext(
@@ -202,16 +224,19 @@
     std::set<int32_t> findConnectedPortConfigIds(int32_t portConfigId);
     ndk::ScopedAStatus findPortIdForNewStream(
             int32_t in_portConfigId, ::aidl::android::media::audio::common::AudioPort** port);
-    virtual BtProfileHandles getBtProfileManagerHandles();
-    internal::Configuration& getConfig();
+    std::vector<AudioRoute*> getAudioRoutesForAudioPortImpl(int32_t portId);
+    Configuration& getConfig();
     const ConnectedDevicePorts& getConnectedDevicePorts() const { return mConnectedDevicePorts; }
     bool getMasterMute() const { return mMasterMute; }
     bool getMasterVolume() const { return mMasterVolume; }
     bool getMicMute() const { return mMicMute; }
     const Patches& getPatches() const { return mPatches; }
+    std::set<int32_t> getRoutableAudioPortIds(int32_t portId,
+                                              std::vector<AudioRoute*>* routes = nullptr);
     const Streams& getStreams() const { return mStreams; }
     Type getType() const { return mType; }
     bool isMmapSupported();
+    void populateConnectedProfiles();
     template <typename C>
     std::set<int32_t> portIdsFromPortConfigIds(C portConfigIds);
     void registerPatch(const AudioPatch& patch);
diff --git a/audio/aidl/default/include/core-impl/ModuleAlsa.h b/audio/aidl/default/include/core-impl/ModuleAlsa.h
index 5815961..2774fe5 100644
--- a/audio/aidl/default/include/core-impl/ModuleAlsa.h
+++ b/audio/aidl/default/include/core-impl/ModuleAlsa.h
@@ -27,7 +27,8 @@
 // provide necessary overrides for all interface methods omitted here.
 class ModuleAlsa : public Module {
   public:
-    explicit ModuleAlsa(Module::Type type) : Module(type) {}
+    ModuleAlsa(Type type, std::unique_ptr<Configuration>&& config)
+        : Module(type, std::move(config)) {}
 
   protected:
     // Extension methods of 'Module'.
diff --git a/audio/aidl/default/include/core-impl/ModuleBluetooth.h b/audio/aidl/default/include/core-impl/ModuleBluetooth.h
index 68b4e6b..a58798b 100644
--- a/audio/aidl/default/include/core-impl/ModuleBluetooth.h
+++ b/audio/aidl/default/include/core-impl/ModuleBluetooth.h
@@ -23,10 +23,18 @@
 
 class ModuleBluetooth final : public Module {
   public:
-    ModuleBluetooth() : Module(Type::BLUETOOTH) {}
+    enum BtInterface : int { BTSCO, BTA2DP, BTLE };
+    typedef std::tuple<std::weak_ptr<IBluetooth>, std::weak_ptr<IBluetoothA2dp>,
+                       std::weak_ptr<IBluetoothLe>>
+            BtProfileHandles;
+
+    ModuleBluetooth(std::unique_ptr<Configuration>&& config)
+        : Module(Type::BLUETOOTH, std::move(config)) {}
 
   private:
-    BtProfileHandles getBtProfileManagerHandles() override;
+    ChildInterface<BluetoothA2dp>& getBtA2dp();
+    ChildInterface<BluetoothLe>& getBtLe();
+    BtProfileHandles getBtProfileManagerHandles();
 
     ndk::ScopedAStatus getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) override;
     ndk::ScopedAStatus getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) override;
@@ -44,11 +52,13 @@
             const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
                     offloadInfo,
             std::shared_ptr<StreamOut>* result) override;
+    ndk::ScopedAStatus populateConnectedDevicePort(
+            ::aidl::android::media::audio::common::AudioPort* audioPort) override;
     ndk::ScopedAStatus onMasterMuteChanged(bool mute) override;
     ndk::ScopedAStatus onMasterVolumeChanged(float volume) override;
 
-    ChildInterface<IBluetoothA2dp> mBluetoothA2dp;
-    ChildInterface<IBluetoothLe> mBluetoothLe;
+    ChildInterface<BluetoothA2dp> mBluetoothA2dp;
+    ChildInterface<BluetoothLe> mBluetoothLe;
 };
 
-}  // namespace aidl::android::hardware::audio::core
\ No newline at end of file
+}  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/ModulePrimary.h b/audio/aidl/default/include/core-impl/ModulePrimary.h
index 6264237..82c8a03 100644
--- a/audio/aidl/default/include/core-impl/ModulePrimary.h
+++ b/audio/aidl/default/include/core-impl/ModulePrimary.h
@@ -22,7 +22,8 @@
 
 class ModulePrimary final : public Module {
   public:
-    ModulePrimary() : Module(Type::DEFAULT) {}
+    ModulePrimary(std::unique_ptr<Configuration>&& config)
+        : Module(Type::DEFAULT, std::move(config)) {}
 
   protected:
     ndk::ScopedAStatus getTelephony(std::shared_ptr<ITelephony>* _aidl_return) override;
@@ -38,6 +39,8 @@
             const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
                     offloadInfo,
             std::shared_ptr<StreamOut>* result) override;
+    int32_t getNominalLatencyMs(
+            const ::aidl::android::media::audio::common::AudioPortConfig& portConfig) override;
 
   private:
     ChildInterface<ITelephony> mTelephony;
diff --git a/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h b/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h
index c4bf7b9..9f8acc9 100644
--- a/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h
+++ b/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h
@@ -22,7 +22,8 @@
 
 class ModuleRemoteSubmix : public Module {
   public:
-    ModuleRemoteSubmix() : Module(Type::R_SUBMIX) {}
+    ModuleRemoteSubmix(std::unique_ptr<Configuration>&& config)
+        : Module(Type::R_SUBMIX, std::move(config)) {}
 
   private:
     // IModule interfaces
@@ -49,6 +50,9 @@
             override;
     ndk::ScopedAStatus onMasterMuteChanged(bool mute) override;
     ndk::ScopedAStatus onMasterVolumeChanged(float volume) override;
+    int32_t getNominalLatencyMs(
+            const ::aidl::android::media::audio::common::AudioPortConfig& portConfig) override;
+    // TODO(b/307586684): Report proper minimum stream buffer size by overriding 'setAudioPatch'.
 };
 
 }  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/ModuleStub.h b/audio/aidl/default/include/core-impl/ModuleStub.h
index 4f77161..e9b7db4 100644
--- a/audio/aidl/default/include/core-impl/ModuleStub.h
+++ b/audio/aidl/default/include/core-impl/ModuleStub.h
@@ -22,7 +22,7 @@
 
 class ModuleStub final : public Module {
   public:
-    ModuleStub() : Module(Type::STUB) {}
+    ModuleStub(std::unique_ptr<Configuration>&& config) : Module(Type::STUB, std::move(config)) {}
 
   protected:
     ndk::ScopedAStatus getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) override;
diff --git a/audio/aidl/default/include/core-impl/ModuleUsb.h b/audio/aidl/default/include/core-impl/ModuleUsb.h
index a296b8c..6ee8f8a 100644
--- a/audio/aidl/default/include/core-impl/ModuleUsb.h
+++ b/audio/aidl/default/include/core-impl/ModuleUsb.h
@@ -22,7 +22,7 @@
 
 class ModuleUsb final : public ModuleAlsa {
   public:
-    ModuleUsb() : ModuleAlsa(Type::USB) {}
+    ModuleUsb(std::unique_ptr<Configuration>&& config) : ModuleAlsa(Type::USB, std::move(config)) {}
 
   private:
     // IModule interfaces
diff --git a/audio/aidl/default/include/core-impl/SoundDose.h b/audio/aidl/default/include/core-impl/SoundDose.h
index 2a069d9..82c1077 100644
--- a/audio/aidl/default/include/core-impl/SoundDose.h
+++ b/audio/aidl/default/include/core-impl/SoundDose.h
@@ -20,23 +20,68 @@
 
 #include <aidl/android/hardware/audio/core/sounddose/BnSoundDose.h>
 #include <aidl/android/media/audio/common/AudioDevice.h>
-
-using aidl::android::media::audio::common::AudioDevice;
+#include <aidl/android/media/audio/common/AudioFormatDescription.h>
+#include <audio_utils/MelProcessor.h>
+#include <audio_utils/mutex.h>
 
 namespace aidl::android::hardware::audio::core::sounddose {
 
-class SoundDose : public BnSoundDose {
+// Interface used for processing the data received by a stream.
+class StreamDataProcessorInterface {
   public:
-    SoundDose() : mRs2Value(DEFAULT_MAX_RS2){};
+    virtual ~StreamDataProcessorInterface() = default;
 
+    virtual void startDataProcessor(
+            uint32_t samplerate, uint32_t channelCount,
+            const ::aidl::android::media::audio::common::AudioFormatDescription& format) = 0;
+    virtual void setAudioDevice(
+            const ::aidl::android::media::audio::common::AudioDevice& audioDevice) = 0;
+    virtual void process(const void* buffer, size_t size) = 0;
+};
+
+class SoundDose final : public BnSoundDose, public StreamDataProcessorInterface {
+  public:
+    SoundDose() : mMelCallback(::android::sp<MelCallback>::make(this)){};
+
+    // -------------------------------------- BnSoundDose ------------------------------------------
     ndk::ScopedAStatus setOutputRs2UpperBound(float in_rs2ValueDbA) override;
     ndk::ScopedAStatus getOutputRs2UpperBound(float* _aidl_return) override;
     ndk::ScopedAStatus registerSoundDoseCallback(
             const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& in_callback) override;
 
+    // ----------------------------- StreamDataProcessorInterface ----------------------------------
+    void setAudioDevice(
+            const ::aidl::android::media::audio::common::AudioDevice& audioDevice) override;
+    void startDataProcessor(
+            uint32_t samplerate, uint32_t channelCount,
+            const ::aidl::android::media::audio::common::AudioFormatDescription& format) override;
+    void process(const void* buffer, size_t size) override;
+
   private:
-    std::shared_ptr<ISoundDose::IHalSoundDoseCallback> mCallback;
-    float mRs2Value;
+    class MelCallback : public ::android::audio_utils::MelProcessor::MelCallback {
+      public:
+        explicit MelCallback(SoundDose* soundDose) : mSoundDose(*soundDose) {}
+
+        // ------------------------------------ MelCallback ----------------------------------------
+        void onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length,
+                            audio_port_handle_t deviceId) const override;
+        void onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const override;
+
+        SoundDose& mSoundDose;  // must outlive MelCallback, not owning
+    };
+
+    void onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length,
+                        audio_port_handle_t deviceId) const;
+    void onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const;
+
+    mutable ::android::audio_utils::mutex mCbMutex;
+    std::shared_ptr<ISoundDose::IHalSoundDoseCallback> mCallback GUARDED_BY(mCbMutex);
+    std::optional<::aidl::android::media::audio::common::AudioDevice> mAudioDevice
+            GUARDED_BY(mCbMutex);
+    mutable ::android::audio_utils::mutex mMutex;
+    float mRs2Value GUARDED_BY(mMutex) = DEFAULT_MAX_RS2;
+    ::android::sp<::android::audio_utils::MelProcessor> mMelProcessor GUARDED_BY(mMutex);
+    ::android::sp<MelCallback> mMelCallback GUARDED_BY(mMutex);
 };
 
 }  // namespace aidl::android::hardware::audio::core::sounddose
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index 88fddec..aa9fb19 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -44,6 +44,7 @@
 #include <utils/Errors.h>
 
 #include "core-impl/ChildInterface.h"
+#include "core-impl/SoundDose.h"
 #include "core-impl/utils.h"
 
 namespace aidl::android::hardware::audio::core {
@@ -80,59 +81,28 @@
 
     StreamContext() = default;
     StreamContext(std::unique_ptr<CommandMQ> commandMQ, std::unique_ptr<ReplyMQ> replyMQ,
-                  int portId,
                   const ::aidl::android::media::audio::common::AudioFormatDescription& format,
                   const ::aidl::android::media::audio::common::AudioChannelLayout& channelLayout,
                   int sampleRate, const ::aidl::android::media::audio::common::AudioIoFlags& flags,
-                  int32_t mixPortHandle, std::unique_ptr<DataMQ> dataMQ,
+                  int32_t nominalLatencyMs, int32_t mixPortHandle, std::unique_ptr<DataMQ> dataMQ,
                   std::shared_ptr<IStreamCallback> asyncCallback,
                   std::shared_ptr<IStreamOutEventCallback> outEventCallback,
+                  std::weak_ptr<sounddose::StreamDataProcessorInterface> streamDataProcessor,
                   DebugParameters debugParameters)
         : mCommandMQ(std::move(commandMQ)),
           mInternalCommandCookie(std::rand()),
           mReplyMQ(std::move(replyMQ)),
-          mPortId(portId),
           mFormat(format),
           mChannelLayout(channelLayout),
           mSampleRate(sampleRate),
           mFlags(flags),
+          mNominalLatencyMs(nominalLatencyMs),
           mMixPortHandle(mixPortHandle),
           mDataMQ(std::move(dataMQ)),
           mAsyncCallback(asyncCallback),
           mOutEventCallback(outEventCallback),
+          mStreamDataProcessor(streamDataProcessor),
           mDebugParameters(debugParameters) {}
-    StreamContext(StreamContext&& other)
-        : mCommandMQ(std::move(other.mCommandMQ)),
-          mInternalCommandCookie(other.mInternalCommandCookie),
-          mReplyMQ(std::move(other.mReplyMQ)),
-          mPortId(other.mPortId),
-          mFormat(other.mFormat),
-          mChannelLayout(other.mChannelLayout),
-          mSampleRate(other.mSampleRate),
-          mFlags(std::move(other.mFlags)),
-          mMixPortHandle(other.mMixPortHandle),
-          mDataMQ(std::move(other.mDataMQ)),
-          mAsyncCallback(std::move(other.mAsyncCallback)),
-          mOutEventCallback(std::move(other.mOutEventCallback)),
-          mDebugParameters(std::move(other.mDebugParameters)),
-          mFrameCount(other.mFrameCount) {}
-    StreamContext& operator=(StreamContext&& other) {
-        mCommandMQ = std::move(other.mCommandMQ);
-        mInternalCommandCookie = other.mInternalCommandCookie;
-        mReplyMQ = std::move(other.mReplyMQ);
-        mPortId = std::move(other.mPortId);
-        mFormat = std::move(other.mFormat);
-        mChannelLayout = std::move(other.mChannelLayout);
-        mSampleRate = other.mSampleRate;
-        mFlags = std::move(other.mFlags);
-        mMixPortHandle = other.mMixPortHandle;
-        mDataMQ = std::move(other.mDataMQ);
-        mAsyncCallback = std::move(other.mAsyncCallback);
-        mOutEventCallback = std::move(other.mOutEventCallback);
-        mDebugParameters = std::move(other.mDebugParameters);
-        mFrameCount = other.mFrameCount;
-        return *this;
-    }
 
     void fillDescriptor(StreamDescriptor* desc);
     std::shared_ptr<IStreamCallback> getAsyncCallback() const { return mAsyncCallback; }
@@ -151,10 +121,14 @@
     size_t getFrameSize() const;
     int getInternalCommandCookie() const { return mInternalCommandCookie; }
     int32_t getMixPortHandle() const { return mMixPortHandle; }
+    int32_t getNominalLatencyMs() const { return mNominalLatencyMs; }
     std::shared_ptr<IStreamOutEventCallback> getOutEventCallback() const {
         return mOutEventCallback;
     }
-    int getPortId() const { return mPortId; }
+    std::weak_ptr<sounddose::StreamDataProcessorInterface> getStreamDataProcessor() const {
+        return mStreamDataProcessor;
+    }
+    void startStreamDataProcessor();
     ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
     int getTransientStateDelayMs() const { return mDebugParameters.transientStateDelayMs; }
     int getSampleRate() const { return mSampleRate; }
@@ -167,18 +141,20 @@
     long getFrameCount() const { return mFrameCount; }
 
   private:
+    // Fields are non const to allow move assignment.
     std::unique_ptr<CommandMQ> mCommandMQ;
     int mInternalCommandCookie;  // The value used to confirm that the command was posted internally
     std::unique_ptr<ReplyMQ> mReplyMQ;
-    int mPortId;
     ::aidl::android::media::audio::common::AudioFormatDescription mFormat;
     ::aidl::android::media::audio::common::AudioChannelLayout mChannelLayout;
     int mSampleRate;
     ::aidl::android::media::audio::common::AudioIoFlags mFlags;
+    int32_t mNominalLatencyMs;
     int32_t mMixPortHandle;
     std::unique_ptr<DataMQ> mDataMQ;
     std::shared_ptr<IStreamCallback> mAsyncCallback;
     std::shared_ptr<IStreamOutEventCallback> mOutEventCallback;  // Only used by output streams
+    std::weak_ptr<sounddose::StreamDataProcessorInterface> mStreamDataProcessor;
     DebugParameters mDebugParameters;
     long mFrameCount = 0;
 };
diff --git a/audio/aidl/default/include/core-impl/StreamBluetooth.h b/audio/aidl/default/include/core-impl/StreamBluetooth.h
index c2f8c1d..1258d38 100644
--- a/audio/aidl/default/include/core-impl/StreamBluetooth.h
+++ b/audio/aidl/default/include/core-impl/StreamBluetooth.h
@@ -24,7 +24,7 @@
 #include <aidl/android/hardware/audio/core/IBluetoothLe.h>
 
 #include "core-impl/DevicePortProxy.h"
-#include "core-impl/Module.h"
+#include "core-impl/ModuleBluetooth.h"
 #include "core-impl/Stream.h"
 
 namespace aidl::android::hardware::audio::core {
@@ -32,7 +32,7 @@
 class StreamBluetooth : public StreamCommonImpl {
   public:
     StreamBluetooth(StreamContext* context, const Metadata& metadata,
-                    Module::BtProfileHandles&& btHandles);
+                    ModuleBluetooth::BtProfileHandles&& btHandles);
     // Methods of 'DriverInterface'.
     ::android::status_t init() override;
     ::android::status_t drain(StreamDescriptor::DrainMode) override;
@@ -80,7 +80,7 @@
             StreamContext&& context,
             const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
             const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
-            Module::BtProfileHandles&& btHandles);
+            ModuleBluetooth::BtProfileHandles&& btHandles);
 
   private:
     void onClose(StreamDescriptor::State) override { defaultOnClose(); }
@@ -97,7 +97,7 @@
             const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
             const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
                     offloadInfo,
-            Module::BtProfileHandles&& btHandles);
+            ModuleBluetooth::BtProfileHandles&& btHandles);
 
   private:
     void onClose(StreamDescriptor::State) override { defaultOnClose(); }
diff --git a/audio/aidl/default/include/core-impl/StreamPrimary.h b/audio/aidl/default/include/core-impl/StreamPrimary.h
index b3ddd0b..abc119c 100644
--- a/audio/aidl/default/include/core-impl/StreamPrimary.h
+++ b/audio/aidl/default/include/core-impl/StreamPrimary.h
@@ -27,10 +27,13 @@
   public:
     StreamPrimary(StreamContext* context, const Metadata& metadata);
 
+    ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
+                                 int32_t* latencyMs) override;
+
   protected:
     std::vector<alsa::DeviceProfile> getDeviceProfiles() override;
 
-    const bool mIsInput;
+    const bool mIsAsynchronous;
 };
 
 class StreamInPrimary final : public StreamIn, public StreamSwitcher, public StreamInHwGainHelper {
@@ -79,6 +82,10 @@
 
     ndk::ScopedAStatus getHwVolume(std::vector<float>* _aidl_return) override;
     ndk::ScopedAStatus setHwVolume(const std::vector<float>& in_channelVolumes) override;
+
+    ndk::ScopedAStatus setConnectedDevices(
+            const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
+            override;
 };
 
 }  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
index 94404a1..21592b3 100644
--- a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
+++ b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
@@ -46,7 +46,7 @@
     ndk::ScopedAStatus prepareToClose() override;
 
   private:
-    size_t getPipeSizeInFrames();
+    long getDelayInUsForFrameCount(size_t frameCount);
     size_t getStreamPipeSizeInFrames();
     ::android::status_t outWrite(void* buffer, size_t frameCount, size_t* actualFrameCount);
     ::android::status_t inRead(void* buffer, size_t frameCount, size_t* actualFrameCount);
diff --git a/audio/aidl/default/include/core-impl/XmlConverter.h b/audio/aidl/default/include/core-impl/XmlConverter.h
index 383ea24..68e6b8e 100644
--- a/audio/aidl/default/include/core-impl/XmlConverter.h
+++ b/audio/aidl/default/include/core-impl/XmlConverter.h
@@ -22,7 +22,6 @@
 
 #include <media/AidlConversionUtil.h>
 #include <system/audio_config.h>
-#include <utils/Errors.h>
 
 namespace aidl::android::hardware::audio::core::internal {
 
@@ -85,10 +84,10 @@
  *     </Modules>
  */
 template <typename W, typename X, typename A>
-std::vector<A> convertWrappedCollectionToAidlUnchecked(
+static ConversionResult<std::vector<A>> convertWrappedCollectionToAidl(
         const std::vector<W>& xsdcWrapperTypeVec,
         std::function<const std::vector<X>&(const W&)> getInnerTypeVec,
-        std::function<A(const X&)> convertToAidl) {
+        std::function<ConversionResult<A>(const X&)> convertToAidl) {
     std::vector<A> resultAidlTypeVec;
     if (!xsdcWrapperTypeVec.empty()) {
         /*
@@ -98,21 +97,23 @@
          */
         resultAidlTypeVec.reserve(getInnerTypeVec(xsdcWrapperTypeVec[0]).size());
         for (const W& xsdcWrapperType : xsdcWrapperTypeVec) {
-            std::transform(getInnerTypeVec(xsdcWrapperType).begin(),
-                           getInnerTypeVec(xsdcWrapperType).end(),
-                           std::back_inserter(resultAidlTypeVec), convertToAidl);
+            for (const X& xsdcType : getInnerTypeVec(xsdcWrapperType)) {
+                resultAidlTypeVec.push_back(VALUE_OR_FATAL(convertToAidl(xsdcType)));
+            }
         }
     }
     return resultAidlTypeVec;
 }
 
 template <typename X, typename A>
-std::vector<A> convertCollectionToAidlUnchecked(const std::vector<X>& xsdcTypeVec,
-                                                std::function<A(const X&)> itemConversion) {
+static ConversionResult<std::vector<A>> convertCollectionToAidl(
+        const std::vector<X>& xsdcTypeVec,
+        std::function<ConversionResult<A>(const X&)> convertToAidl) {
     std::vector<A> resultAidlTypeVec;
     resultAidlTypeVec.reserve(xsdcTypeVec.size());
-    std::transform(xsdcTypeVec.begin(), xsdcTypeVec.end(), std::back_inserter(resultAidlTypeVec),
-                   itemConversion);
+    for (const X& xsdcType : xsdcTypeVec) {
+        resultAidlTypeVec.push_back(VALUE_OR_FATAL(convertToAidl(xsdcType)));
+    }
     return resultAidlTypeVec;
 }
 
diff --git a/audio/aidl/default/include/core-impl/XsdcConversion.h b/audio/aidl/default/include/core-impl/XsdcConversion.h
new file mode 100644
index 0000000..30dc8b6
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/XsdcConversion.h
@@ -0,0 +1,29 @@
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <aidl/android/media/audio/common/AudioHalCapCriterion.h>
+#include <aidl/android/media/audio/common/AudioHalCapCriterionType.h>
+#include <aidl/android/media/audio/common/AudioHalVolumeCurve.h>
+#include <aidl/android/media/audio/common/AudioPort.h>
+#include <android_audio_policy_configuration.h>
+#include <android_audio_policy_configuration_enums.h>
+#include <android_audio_policy_engine_configuration.h>
+#include <media/AidlConversionUtil.h>
+
+#include "core-impl/Module.h"
+
+namespace aidl::android::hardware::audio::core::internal {
+
+ConversionResult<::aidl::android::media::audio::common::AudioHalCapCriterion>
+convertCapCriterionToAidl(
+        const ::android::audio::policy::engine::configuration::CriterionType& xsdcCriterion);
+ConversionResult<::aidl::android::media::audio::common::AudioHalCapCriterionType>
+convertCapCriterionTypeToAidl(
+        const ::android::audio::policy::engine::configuration::CriterionTypeType&
+                xsdcCriterionType);
+ConversionResult<::aidl::android::media::audio::common::AudioHalVolumeCurve::CurvePoint>
+convertCurvePointToAidl(const std::string& xsdcCurvePoint);
+ConversionResult<std::unique_ptr<Module::Configuration>> convertModuleConfigToAidl(
+        const ::android::audio::policy::configuration::Modules::Module& moduleConfig);
+}  // namespace aidl::android::hardware::audio::core::internal
diff --git a/audio/aidl/default/main.cpp b/audio/aidl/default/main.cpp
index a0c0fab..6ab747d 100644
--- a/audio/aidl/default/main.cpp
+++ b/audio/aidl/default/main.cpp
@@ -16,21 +16,51 @@
 
 #include <cstdlib>
 #include <ctime>
-#include <sstream>
 #include <utility>
 #include <vector>
 
+#define LOG_TAG "AHAL_Main"
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android/binder_ibinder_platform.h>
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
 
+#include "core-impl/AudioPolicyConfigXmlConverter.h"
+#include "core-impl/ChildInterface.h"
 #include "core-impl/Config.h"
 #include "core-impl/Module.h"
 
+using aidl::android::hardware::audio::core::ChildInterface;
 using aidl::android::hardware::audio::core::Config;
 using aidl::android::hardware::audio::core::Module;
+using aidl::android::hardware::audio::core::internal::AudioPolicyConfigXmlConverter;
+
+namespace {
+
+ChildInterface<Module> createModule(const std::string& name,
+                                    std::unique_ptr<Module::Configuration>&& config) {
+    ChildInterface<Module> result;
+    {
+        auto moduleType = Module::typeFromString(name);
+        if (!moduleType.has_value()) {
+            LOG(ERROR) << __func__ << ": module type \"" << name << "\" is not supported";
+            return result;
+        }
+        auto module = Module::createInstance(*moduleType, std::move(config));
+        if (module == nullptr) return result;
+        result = std::move(module);
+    }
+    const std::string moduleFqn = std::string().append(Module::descriptor).append("/").append(name);
+    binder_status_t status = AServiceManager_addService(result.getBinder(), moduleFqn.c_str());
+    if (status != STATUS_OK) {
+        LOG(ERROR) << __func__ << ": failed to register service for \"" << moduleFqn << "\"";
+        return ChildInterface<Module>();
+    }
+    return result;
+};
+
+}  // namespace
 
 int main() {
     // Random values are used in the implementation.
@@ -45,29 +75,27 @@
     // Guaranteed log for b/210919187 and logd_integration_test
     LOG(INFO) << "Init for Audio AIDL HAL";
 
+    AudioPolicyConfigXmlConverter audioPolicyConverter{
+            ::android::audio_get_audio_policy_config_file()};
+
     // Make the default config service
-    auto config = ndk::SharedRefBase::make<Config>();
-    const std::string configName = std::string() + Config::descriptor + "/default";
+    auto config = ndk::SharedRefBase::make<Config>(audioPolicyConverter);
+    const std::string configFqn = std::string().append(Config::descriptor).append("/default");
     binder_status_t status =
-            AServiceManager_addService(config->asBinder().get(), configName.c_str());
-    CHECK_EQ(STATUS_OK, status);
+            AServiceManager_addService(config->asBinder().get(), configFqn.c_str());
+    if (status != STATUS_OK) {
+        LOG(ERROR) << "failed to register service for \"" << configFqn << "\"";
+    }
 
     // Make modules
-    auto createModule = [](Module::Type type) {
-        auto module = Module::createInstance(type);
-        ndk::SpAIBinder moduleBinder = module->asBinder();
-        std::stringstream moduleName;
-        moduleName << Module::descriptor << "/" << type;
-        AIBinder_setMinSchedulerPolicy(moduleBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
-        binder_status_t status =
-                AServiceManager_addService(moduleBinder.get(), moduleName.str().c_str());
-        CHECK_EQ(STATUS_OK, status);
-        return std::make_pair(module, moduleBinder);
-    };
-    auto modules = {createModule(Module::Type::DEFAULT), createModule(Module::Type::R_SUBMIX),
-                    createModule(Module::Type::USB), createModule(Module::Type::STUB),
-                    createModule(Module::Type::BLUETOOTH)};
-    (void)modules;
+    std::vector<ChildInterface<Module>> moduleInstances;
+    auto configs(audioPolicyConverter.releaseModuleConfigs());
+    for (std::pair<std::string, std::unique_ptr<Module::Configuration>>& configPair : *configs) {
+        std::string name = configPair.first;
+        if (auto instance = createModule(name, std::move(configPair.second)); instance) {
+            moduleInstances.push_back(std::move(instance));
+        }
+    }
 
     ABinderProcess_joinThreadPool();
     return EXIT_FAILURE;  // should not reach
diff --git a/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.cpp b/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.cpp
index 9b2cb7c..99f2caf 100644
--- a/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.cpp
+++ b/audio/aidl/default/noiseSuppression/NoiseSuppressionSw.cpp
@@ -65,7 +65,7 @@
         .common = {.id = {.type = getEffectTypeUuidNoiseSuppression(),
                           .uuid = getEffectImplUuidNoiseSuppressionSw(),
                           .proxy = std::nullopt},
-                   .flags = {.type = Flags::Type::INSERT,
+                   .flags = {.type = Flags::Type::PRE_PROC,
                              .insert = Flags::Insert::FIRST,
                              .volume = Flags::Volume::CTRL},
                    .name = NoiseSuppressionSw::kEffectName,
diff --git a/audio/aidl/default/primary/StreamPrimary.cpp b/audio/aidl/default/primary/StreamPrimary.cpp
index e01be8a..7e3bdd4 100644
--- a/audio/aidl/default/primary/StreamPrimary.cpp
+++ b/audio/aidl/default/primary/StreamPrimary.cpp
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-#include <limits>
+#include <chrono>
 
 #define LOG_TAG "AHAL_StreamPrimary"
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <audio_utils/clock.h>
 #include <error/expected_utils.h>
 
 #include "PrimaryMixer.h"
@@ -37,7 +38,33 @@
 namespace aidl::android::hardware::audio::core {
 
 StreamPrimary::StreamPrimary(StreamContext* context, const Metadata& metadata)
-    : StreamAlsa(context, metadata, 3 /*readWriteRetries*/), mIsInput(isInput(metadata)) {}
+    : StreamAlsa(context, metadata, 3 /*readWriteRetries*/),
+      mIsAsynchronous(!!getContext().getAsyncCallback()) {
+    context->startStreamDataProcessor();
+}
+
+::android::status_t StreamPrimary::transfer(void* buffer, size_t frameCount,
+                                            size_t* actualFrameCount, int32_t* latencyMs) {
+    auto start = std::chrono::steady_clock::now();
+    if (auto status = StreamAlsa::transfer(buffer, frameCount, actualFrameCount, latencyMs);
+        status != ::android::OK) {
+        return status;
+    }
+    // This is a workaround for the emulator implementation which has a host-side buffer
+    // and this can result in reading faster than real time.
+    if (mIsInput && !mIsAsynchronous) {
+        auto recordDurationUs = std::chrono::duration_cast<std::chrono::microseconds>(
+                std::chrono::steady_clock::now() - start);
+        const long projectedVsObservedOffsetUs =
+                *actualFrameCount * MICROS_PER_SECOND / mContext.getSampleRate() -
+                recordDurationUs.count();
+        if (projectedVsObservedOffsetUs > 0) {
+            LOG(VERBOSE) << __func__ << ": sleeping for " << projectedVsObservedOffsetUs << " us";
+            usleep(projectedVsObservedOffsetUs);
+        }
+    }
+    return ::android::OK;
+}
 
 std::vector<alsa::DeviceProfile> StreamPrimary::getDeviceProfiles() {
     static const std::vector<alsa::DeviceProfile> kBuiltInSource{
@@ -64,7 +91,8 @@
             GetBoolProperty("ro.boot.audio.tinyalsa.simulate_input", false);
     return kSimulateInput || device.type.type == AudioDeviceType::IN_TELEPHONY_RX ||
            device.type.type == AudioDeviceType::IN_FM_TUNER ||
-           device.type.connection == AudioDeviceDescription::CONNECTION_BUS;
+           device.type.connection == AudioDeviceDescription::CONNECTION_BUS ||
+           (device.type.type == AudioDeviceType::IN_DEVICE && device.type.connection.empty());
 }
 
 StreamSwitcher::DeviceSwitchBehavior StreamInPrimary::switchCurrentStream(
@@ -99,6 +127,11 @@
     if (isStubStream()) {
         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
     }
+    float gain;
+    RETURN_STATUS_IF_ERROR(primary::PrimaryMixer::getInstance().getMicGain(&gain));
+    _aidl_return->resize(0);
+    _aidl_return->resize(mChannelCount, gain);
+    RETURN_STATUS_IF_ERROR(setHwGainImpl(*_aidl_return));
     return getHwGainImpl(_aidl_return);
 }
 
@@ -130,7 +163,8 @@
     static const bool kSimulateOutput =
             GetBoolProperty("ro.boot.audio.tinyalsa.ignore_output", false);
     return kSimulateOutput || device.type.type == AudioDeviceType::OUT_TELEPHONY_TX ||
-           device.type.connection == AudioDeviceDescription::CONNECTION_BUS;
+           device.type.connection == AudioDeviceDescription::CONNECTION_BUS ||
+           (device.type.type == AudioDeviceType::OUT_DEVICE && device.type.connection.empty());
 }
 
 StreamSwitcher::DeviceSwitchBehavior StreamOutPrimary::switchCurrentStream(
@@ -165,6 +199,9 @@
     if (isStubStream()) {
         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
     }
+    RETURN_STATUS_IF_ERROR(primary::PrimaryMixer::getInstance().getVolumes(_aidl_return));
+    _aidl_return->resize(mChannelCount);
+    RETURN_STATUS_IF_ERROR(setHwVolumeImpl(*_aidl_return));
     return getHwVolumeImpl(_aidl_return);
 }
 
@@ -183,4 +220,15 @@
     return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus StreamOutPrimary::setConnectedDevices(
+        const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
+    if (!devices.empty()) {
+        auto streamDataProcessor = mContextInstance.getStreamDataProcessor().lock();
+        if (streamDataProcessor != nullptr) {
+            streamDataProcessor->setAudioDevice(devices[0]);
+        }
+    }
+    return StreamSwitcher::setConnectedDevices(devices);
+}
+
 }  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp b/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp
index adea877..3e8dd7c 100644
--- a/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp
+++ b/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp
@@ -21,6 +21,7 @@
 #include <android-base/logging.h>
 #include <error/expected_utils.h>
 
+#include "SubmixRoute.h"
 #include "core-impl/ModuleRemoteSubmix.h"
 #include "core-impl/StreamRemoteSubmix.h"
 
@@ -59,23 +60,22 @@
 
 ndk::ScopedAStatus ModuleRemoteSubmix::populateConnectedDevicePort(AudioPort* audioPort) {
     // Find the corresponding mix port and copy its profiles.
-    std::vector<AudioRoute> routes;
     // At this moment, the port has the same ID as the template port, see connectExternalDevice.
-    RETURN_STATUS_IF_ERROR(getAudioRoutesForAudioPort(audioPort->id, &routes));
+    std::vector<AudioRoute*> routes = getAudioRoutesForAudioPortImpl(audioPort->id);
     if (routes.empty()) {
         LOG(ERROR) << __func__ << ": no routes found for the port " << audioPort->toString();
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     }
     const auto& route = *routes.begin();
     AudioPort mixPort;
-    if (route.sinkPortId == audioPort->id) {
-        if (route.sourcePortIds.empty()) {
-            LOG(ERROR) << __func__ << ": invalid route " << route.toString();
+    if (route->sinkPortId == audioPort->id) {
+        if (route->sourcePortIds.empty()) {
+            LOG(ERROR) << __func__ << ": invalid route " << route->toString();
             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
         }
-        RETURN_STATUS_IF_ERROR(getAudioPort(*route.sourcePortIds.begin(), &mixPort));
+        RETURN_STATUS_IF_ERROR(getAudioPort(*route->sourcePortIds.begin(), &mixPort));
     } else {
-        RETURN_STATUS_IF_ERROR(getAudioPort(route.sinkPortId, &mixPort));
+        RETURN_STATUS_IF_ERROR(getAudioPort(route->sinkPortId, &mixPort));
     }
     audioPort->profiles = mixPort.profiles;
     return ndk::ScopedAStatus::ok();
@@ -107,4 +107,12 @@
     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 }
 
+int32_t ModuleRemoteSubmix::getNominalLatencyMs(const AudioPortConfig&) {
+    // See the note on kDefaultPipePeriodCount.
+    static constexpr int32_t kMaxLatencyMs =
+            (r_submix::kDefaultPipeSizeInFrames * 1000) / r_submix::kDefaultSampleRateHz;
+    static constexpr int32_t kMinLatencyMs = kMaxLatencyMs / r_submix::kDefaultPipePeriodCount;
+    return (kMaxLatencyMs + kMinLatencyMs) / 2;
+}
+
 }  // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
index 9c9c08b..fc61dcb 100644
--- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
+++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
@@ -17,8 +17,6 @@
 #define LOG_TAG "AHAL_StreamRemoteSubmix"
 #include <android-base/logging.h>
 
-#include <cmath>
-
 #include "core-impl/StreamRemoteSubmix.h"
 
 using aidl::android::hardware::audio::common::SinkMetadata;
@@ -133,6 +131,8 @@
             LOG(DEBUG) << __func__ << ": shutting down MonoPipe sink";
 
             sink->shutdown(true);
+            // The client already considers this stream as closed, release the output end.
+            route->closeStream(mIsInput);
         } else {
             LOG(DEBUG) << __func__ << ": stream already closed.";
             ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
@@ -158,27 +158,8 @@
 
 ::android::status_t StreamRemoteSubmix::transfer(void* buffer, size_t frameCount,
                                                  size_t* actualFrameCount, int32_t* latencyMs) {
-    *latencyMs = (getStreamPipeSizeInFrames() * MILLIS_PER_SECOND) / mStreamConfig.sampleRate;
+    *latencyMs = getDelayInUsForFrameCount(getStreamPipeSizeInFrames()) / 1000;
     LOG(VERBOSE) << __func__ << ": Latency " << *latencyMs << "ms";
-
-    sp<MonoPipe> sink = mCurrentRoute->getSink();
-    if (sink != nullptr) {
-        if (sink->isShutdown()) {
-            sink.clear();
-            LOG(VERBOSE) << __func__ << ": pipe shutdown, ignoring the transfer.";
-            // the pipe has already been shutdown, this buffer will be lost but we must simulate
-            // timing so we don't drain the output faster than realtime
-            const size_t delayUs = static_cast<size_t>(
-                    std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate));
-            usleep(delayUs);
-
-            *actualFrameCount = frameCount;
-            return ::android::OK;
-        }
-    } else {
-        LOG(ERROR) << __func__ << ": transfer without a pipe!";
-        return ::android::UNEXPECTED_NULL;
-    }
     mCurrentRoute->exitStandby(mIsInput);
     return (mIsInput ? inRead(buffer, frameCount, actualFrameCount)
                      : outWrite(buffer, frameCount, actualFrameCount));
@@ -202,6 +183,10 @@
     return ::android::OK;
 }
 
+long StreamRemoteSubmix::getDelayInUsForFrameCount(size_t frameCount) {
+    return frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate;
+}
+
 // Calculate the maximum size of the pipe buffer in frames for the specified stream.
 size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
     auto pipeConfig = mCurrentRoute->mPipeConfig;
@@ -215,11 +200,11 @@
     if (sink != nullptr) {
         if (sink->isShutdown()) {
             sink.clear();
-            LOG(VERBOSE) << __func__ << ": pipe shutdown, ignoring the write.";
+            const auto delayUs = getDelayInUsForFrameCount(frameCount);
+            LOG(DEBUG) << __func__ << ": pipe shutdown, ignoring the write, sleeping for "
+                       << delayUs << " us";
             // the pipe has already been shutdown, this buffer will be lost but we must
             // simulate timing so we don't drain the output faster than realtime
-            const size_t delayUs = static_cast<size_t>(
-                    std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate));
             usleep(delayUs);
             *actualFrameCount = frameCount;
             return ::android::OK;
@@ -229,17 +214,18 @@
         return ::android::UNKNOWN_ERROR;
     }
 
-    const size_t availableToWrite = sink->availableToWrite();
+    const bool shouldBlockWrite = mCurrentRoute->shouldBlockWrite();
+    size_t availableToWrite = sink->availableToWrite();
     // NOTE: sink has been checked above and sink and source life cycles are synchronized
     sp<MonoPipeReader> source = mCurrentRoute->getSource();
     // If the write to the sink should be blocked, flush enough frames from the pipe to make space
     // to write the most recent data.
-    if (!mCurrentRoute->shouldBlockWrite() && availableToWrite < frameCount) {
+    if (!shouldBlockWrite && availableToWrite < frameCount) {
         static uint8_t flushBuffer[64];
         const size_t flushBufferSizeFrames = sizeof(flushBuffer) / mStreamConfig.frameSize;
         size_t framesToFlushFromSource = frameCount - availableToWrite;
-        LOG(VERBOSE) << __func__ << ": flushing " << framesToFlushFromSource
-                     << " frames from the pipe to avoid blocking";
+        LOG(DEBUG) << __func__ << ": flushing " << framesToFlushFromSource
+                   << " frames from the pipe to avoid blocking";
         while (framesToFlushFromSource) {
             const size_t flushSize = std::min(framesToFlushFromSource, flushBufferSizeFrames);
             framesToFlushFromSource -= flushSize;
@@ -247,7 +233,12 @@
             source->read(flushBuffer, flushSize);
         }
     }
+    availableToWrite = sink->availableToWrite();
 
+    if (!shouldBlockWrite && frameCount > availableToWrite) {
+        // Truncate the request to avoid blocking.
+        frameCount = availableToWrite;
+    }
     ssize_t writtenFrames = sink->write(buffer, frameCount);
     if (writtenFrames < 0) {
         if (writtenFrames == (ssize_t)::android::NEGOTIATE) {
@@ -261,7 +252,6 @@
             writtenFrames = sink->write(buffer, frameCount);
         }
     }
-    sink.clear();
 
     if (writtenFrames < 0) {
         LOG(ERROR) << __func__ << ": failed writing to pipe with " << writtenFrames;
@@ -286,8 +276,9 @@
         } else {
             LOG(ERROR) << __func__ << ": Read errors " << readErrorCount;
         }
-        const size_t delayUs = static_cast<size_t>(
-                std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate));
+        const auto delayUs = getDelayInUsForFrameCount(frameCount);
+        LOG(DEBUG) << __func__ << ": no source, ignoring the read, sleeping for " << delayUs
+                   << " us";
         usleep(delayUs);
         memset(buffer, 0, mStreamConfig.frameSize * frameCount);
         *actualFrameCount = frameCount;
@@ -296,7 +287,7 @@
 
     // read the data from the pipe
     int attempts = 0;
-    const size_t delayUs = static_cast<size_t>(std::roundf(kReadAttemptSleepUs));
+    const long delayUs = kReadAttemptSleepUs;
     char* buff = (char*)buffer;
     size_t remainingFrames = frameCount;
     int availableToRead = source->availableToRead();
@@ -313,11 +304,12 @@
             buff += framesRead * mStreamConfig.frameSize;
             availableToRead -= framesRead;
             LOG(VERBOSE) << __func__ << ": (attempts = " << attempts << ") got " << framesRead
-                         << " frames, remaining=" << remainingFrames;
+                         << " frames, remaining =" << remainingFrames;
         } else {
             attempts++;
             LOG(WARNING) << __func__ << ": read returned " << framesRead
-                         << " , read failure attempts = " << attempts;
+                         << " , read failure attempts = " << attempts << ", sleeping for "
+                         << delayUs << " us";
             usleep(delayUs);
         }
     }
@@ -337,18 +329,18 @@
     // compute how much we need to sleep after reading the data by comparing the wall clock with
     //   the projected time at which we should return.
     // wall clock after reading from the pipe
-    auto recordDurationUs = std::chrono::steady_clock::now() - mCurrentRoute->getRecordStartTime();
+    auto recordDurationUs = std::chrono::duration_cast<std::chrono::microseconds>(
+            std::chrono::steady_clock::now() - mCurrentRoute->getRecordStartTime());
 
     // readCounterFrames contains the number of frames that have been read since the beginning of
     // recording (including this call): it's converted to usec and compared to how long we've been
     // recording for, which gives us how long we must wait to sync the projected recording time, and
     // the observed recording time.
-    const int projectedVsObservedOffsetUs =
-            std::roundf((readCounterFrames * MICROS_PER_SECOND / mStreamConfig.sampleRate) -
-                        recordDurationUs.count());
+    const long projectedVsObservedOffsetUs =
+            getDelayInUsForFrameCount(readCounterFrames) - recordDurationUs.count();
 
     LOG(VERBOSE) << __func__ << ": record duration " << recordDurationUs.count()
-                 << " microseconds, will wait: " << projectedVsObservedOffsetUs << " microseconds";
+                 << " us, will wait: " << projectedVsObservedOffsetUs << " us";
     if (projectedVsObservedOffsetUs > 0) {
         usleep(projectedVsObservedOffsetUs);
     }
diff --git a/audio/aidl/default/r_submix/SubmixRoute.h b/audio/aidl/default/r_submix/SubmixRoute.h
index 1a98df2..92b95e9 100644
--- a/audio/aidl/default/r_submix/SubmixRoute.h
+++ b/audio/aidl/default/r_submix/SubmixRoute.h
@@ -14,16 +14,19 @@
  * limitations under the License.
  */
 
+#pragma once
+
+#include <chrono>
 #include <mutex>
 
+#include <android-base/thread_annotations.h>
 #include <audio_utils/clock.h>
 
 #include <media/nbaio/MonoPipe.h>
 #include <media/nbaio/MonoPipeReader.h>
 
 #include <aidl/android/media/audio/common/AudioChannelLayout.h>
-
-#include "core-impl/Stream.h"
+#include <aidl/android/media/audio/common/AudioFormatDescription.h>
 
 using aidl::android::media::audio::common::AudioChannelLayout;
 using aidl::android::media::audio::common::AudioFormatDescription;
@@ -36,9 +39,13 @@
 namespace aidl::android::hardware::audio::core::r_submix {
 
 static constexpr int kDefaultSampleRateHz = 48000;
-// Size at default sample rate
-// NOTE: This value will be rounded up to the nearest power of 2 by MonoPipe().
-static constexpr int kDefaultPipeSizeInFrames = (1024 * 4);
+// Value used to divide the MonoPipe buffer into segments that are written to the source and
+// read from the sink. The maximum latency of the device is the size of the MonoPipe's buffer
+// the minimum latency is the MonoPipe buffer size divided by this value.
+static constexpr int kDefaultPipePeriodCount = 4;
+// Size at the default sample rate
+// NOTE: This value will be rounded up to the nearest power of 2 by MonoPipe.
+static constexpr int kDefaultPipeSizeInFrames = 1024 * kDefaultPipePeriodCount;
 
 // Configuration of the audio stream.
 struct AudioConfig {
diff --git a/audio/aidl/default/stub/StreamStub.cpp b/audio/aidl/default/stub/StreamStub.cpp
index 660a51e..2422fe4 100644
--- a/audio/aidl/default/stub/StreamStub.cpp
+++ b/audio/aidl/default/stub/StreamStub.cpp
@@ -94,7 +94,7 @@
 }
 
 ::android::status_t StreamStub::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
-                                         int32_t* latencyMs) {
+                                         int32_t*) {
     if (!mIsInitialized) {
         LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
     }
@@ -117,7 +117,6 @@
         }
     }
     *actualFrameCount = frameCount;
-    *latencyMs = Module::kLatencyMs;
     return ::android::OK;
 }
 
diff --git a/audio/aidl/sounddose/Android.bp b/audio/aidl/sounddose/Android.bp
index 6f2f790..c65e4ff 100644
--- a/audio/aidl/sounddose/Android.bp
+++ b/audio/aidl/sounddose/Android.bp
@@ -52,11 +52,11 @@
         // IMPORTANT: Update latest_android_hardware_audio_sounddose every time you
         // add the latest frozen version to versions_with_info
     ],
-    frozen: true,
+    frozen: false,
 }
 
 // Note: This should always be one version ahead of the last frozen version
-latest_android_hardware_audio_sounddose = "android.hardware.audio.sounddose-V1"
+latest_android_hardware_audio_sounddose = "android.hardware.audio.sounddose-V2"
 
 // Modules that depend on android.hardware.audio.sounddose directly can include
 // the following cc_defaults to avoid explicitly managing dependency versions
diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp
index a2f0260..ad816c7 100644
--- a/audio/aidl/vts/Android.bp
+++ b/audio/aidl/vts/Android.bp
@@ -11,6 +11,7 @@
     name: "VtsHalAudioTargetTestDefaults",
     defaults: [
         "latest_android_hardware_audio_common_ndk_static",
+        "latest_android_hardware_audio_effect_ndk_static",
         "latest_android_media_audio_common_types_ndk_static",
         "use_libaidlvintf_gtest_helper_static",
         "VtsHalTargetTestDefaults",
@@ -20,7 +21,6 @@
         "libfmq",
     ],
     static_libs: [
-        "android.hardware.audio.effect-V1-ndk",
         "android.hardware.common-V2-ndk",
         "android.hardware.common.fmq-V1-ndk",
         "libaudioaidlcommon",
@@ -40,7 +40,10 @@
         "general-tests",
         "vts",
     ],
-    srcs: [":effectCommonFile"],
+    srcs: [
+        ":effectCommonFile",
+        "TestUtils.cpp",
+    ],
 }
 
 cc_test {
diff --git a/audio/aidl/vts/AudioHalBinderServiceUtil.h b/audio/aidl/vts/AudioHalBinderServiceUtil.h
index b4b4632..4ebc1b1 100644
--- a/audio/aidl/vts/AudioHalBinderServiceUtil.h
+++ b/audio/aidl/vts/AudioHalBinderServiceUtil.h
@@ -42,20 +42,9 @@
 
     ndk::SpAIBinder restartService(
             std::chrono::milliseconds timeoutMs = std::chrono::milliseconds(3000)) {
-        mDeathHandler.reset(new AidlDeathRecipient(mBinder));
-        if (STATUS_OK != mDeathHandler->linkToDeath()) {
-            LOG(ERROR) << "linkToDeath failed";
-            return nullptr;
+        if (!stopService(timeoutMs)) {
+            return {};
         }
-        if (!android::base::SetProperty("sys.audio.restart.hal", "1")) {
-            LOG(ERROR) << "SetProperty failed";
-            return nullptr;
-        }
-        if (!mDeathHandler->waitForFired(timeoutMs)) {
-            LOG(ERROR) << "Timeout wait for death";
-            return nullptr;
-        }
-        mDeathHandler.reset();
         return connectToService(mServiceName);
     }
 
@@ -71,8 +60,7 @@
 
         bool waitForFired(std::chrono::milliseconds timeoutMs) {
             std::unique_lock<std::mutex> lock(mutex);
-            condition.wait_for(lock, timeoutMs, [this]() { return fired; });
-            return fired;
+            return condition.wait_for(lock, timeoutMs, [this]() { return fired; });
         }
 
       private:
@@ -94,7 +82,23 @@
         }
     };
 
+    bool stopService(std::chrono::milliseconds timeoutMs) {
+        AidlDeathRecipient deathHandler(mBinder);
+        if (STATUS_OK != deathHandler.linkToDeath()) {
+            LOG(ERROR) << "linkToDeath failed";
+            return false;
+        }
+        if (!android::base::SetProperty("sys.audio.restart.hal", "1")) {
+            LOG(ERROR) << "SetProperty failed";
+            return false;
+        }
+        if (!deathHandler.waitForFired(timeoutMs)) {
+            LOG(ERROR) << "Timeout wait for death of " << mServiceName;
+            return false;
+        }
+        return true;
+    }
+
     std::string mServiceName;
     ndk::SpAIBinder mBinder;
-    std::unique_ptr<AidlDeathRecipient> mDeathHandler;
 };
diff --git a/audio/aidl/vts/ModuleConfig.cpp b/audio/aidl/vts/ModuleConfig.cpp
index af3597d..2b86271 100644
--- a/audio/aidl/vts/ModuleConfig.cpp
+++ b/audio/aidl/vts/ModuleConfig.cpp
@@ -17,6 +17,9 @@
 #include <algorithm>
 #include <chrono>
 
+#define LOG_TAG "VtsHalAudio.ModuleConfig"
+#include <android-base/logging.h>
+
 #include <Utils.h>
 #include <aidl/android/media/audio/common/AudioInputFlags.h>
 #include <aidl/android/media/audio/common/AudioIoFlags.h>
@@ -67,20 +70,7 @@
     return {};
 }
 
-std::vector<aidl::android::media::audio::common::AudioPort>
-ModuleConfig::getAudioPortsForDeviceTypes(const std::vector<AudioDeviceType>& deviceTypes,
-                                          const std::string& connection) {
-    return getAudioPortsForDeviceTypes(mPorts, deviceTypes, connection);
-}
-
 // static
-std::vector<aidl::android::media::audio::common::AudioPort> ModuleConfig::getBuiltInMicPorts(
-        const std::vector<aidl::android::media::audio::common::AudioPort>& ports) {
-    return getAudioPortsForDeviceTypes(
-            ports, std::vector<AudioDeviceType>{AudioDeviceType::IN_MICROPHONE,
-                                                AudioDeviceType::IN_MICROPHONE_BACK});
-}
-
 std::vector<aidl::android::media::audio::common::AudioPort>
 ModuleConfig::getAudioPortsForDeviceTypes(
         const std::vector<aidl::android::media::audio::common::AudioPort>& ports,
@@ -100,6 +90,14 @@
     return result;
 }
 
+// static
+std::vector<aidl::android::media::audio::common::AudioPort> ModuleConfig::getBuiltInMicPorts(
+        const std::vector<aidl::android::media::audio::common::AudioPort>& ports) {
+    return getAudioPortsForDeviceTypes(
+            ports, std::vector<AudioDeviceType>{AudioDeviceType::IN_MICROPHONE,
+                                                AudioDeviceType::IN_MICROPHONE_BACK});
+}
+
 template <typename T>
 auto findById(const std::vector<T>& v, int32_t id) {
     return std::find_if(v.begin(), v.end(), [&](const auto& p) { return p.id == id; });
@@ -119,10 +117,7 @@
             } else {
                 mAttachedSinkDevicePorts.insert(port.id);
             }
-        } else if (devicePort.device.type.connection != AudioDeviceDescription::CONNECTION_VIRTUAL
-                   // The "virtual" connection is used for remote submix which is a dynamic
-                   // device but it can be connected and used w/o external hardware.
-                   && port.profiles.empty()) {
+        } else {
             mExternalDevicePorts.insert(port.id);
         }
     }
@@ -141,6 +136,12 @@
     return result;
 }
 
+std::vector<aidl::android::media::audio::common::AudioPort>
+ModuleConfig::getAudioPortsForDeviceTypes(const std::vector<AudioDeviceType>& deviceTypes,
+                                          const std::string& connection) const {
+    return getAudioPortsForDeviceTypes(mPorts, deviceTypes, connection);
+}
+
 std::vector<AudioPort> ModuleConfig::getConnectedExternalDevicePorts() const {
     std::vector<AudioPort> result;
     std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) {
@@ -229,6 +230,16 @@
     });
 }
 
+std::vector<AudioPort> ModuleConfig::getRemoteSubmixPorts(bool isInput, bool singlePort) const {
+    AudioDeviceType deviceType = isInput ? AudioDeviceType::IN_SUBMIX : AudioDeviceType::OUT_SUBMIX;
+    auto ports = getAudioPortsForDeviceTypes(std::vector<AudioDeviceType>{deviceType},
+                                             AudioDeviceDescription::CONNECTION_VIRTUAL);
+    if (singlePort) {
+        if (!ports.empty()) ports.resize(1);
+    }
+    return ports;
+}
+
 std::vector<AudioPort> ModuleConfig::getConnectedDevicesPortsForMixPort(
         bool isInput, const AudioPortConfig& mixPortConfig) const {
     const auto mixPortIt = findById<AudioPort>(mPorts, mixPortConfig.portId);
@@ -281,6 +292,32 @@
     return {};
 }
 
+std::vector<AudioPort> ModuleConfig::getRoutableDevicePortsForMixPort(const AudioPort& port,
+                                                                      bool connectedOnly) const {
+    std::set<int32_t> portIds = findRoutablePortIds(port.id);
+    const bool isInput = port.flags.getTag() == AudioIoFlags::input;
+    std::set<int32_t> devicePortIds;
+    if (connectedOnly) {
+        devicePortIds = isInput ? getConnectedSourceDevicePorts() : getConnectedSinkDevicePorts();
+    } else {
+        devicePortIds = portIds;
+    }
+    std::vector<AudioPort> result;
+    std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) {
+        return port.ext.getTag() == AudioPortExt::Tag::device && portIds.count(port.id) > 0 &&
+               devicePortIds.count(port.id) > 0;
+    });
+    return result;
+}
+
+std::vector<AudioPort> ModuleConfig::getRoutableMixPortsForDevicePort(const AudioPort& port,
+                                                                      bool connectedOnly) const {
+    std::set<int32_t> portIds = findRoutablePortIds(port.id);
+    const bool isInput = port.flags.getTag() == AudioIoFlags::input;
+    return findMixPorts(isInput, connectedOnly, false /*singlePort*/,
+                        [&portIds](const AudioPort& p) { return portIds.count(p.id) > 0; });
+}
+
 std::optional<ModuleConfig::SrcSinkPair> ModuleConfig::getNonRoutableSrcSinkPair(
         bool isInput) const {
     const auto mixPorts = getMixPorts(isInput, false /*connectedOnly*/);
@@ -454,6 +491,20 @@
     return result;
 }
 
+std::set<int32_t> ModuleConfig::findRoutablePortIds(int32_t portId) const {
+    std::set<int32_t> portIds;
+    for (const auto& route : mRoutes) {
+        if (portId == route.sinkPortId) {
+            portIds.insert(route.sourcePortIds.begin(), route.sourcePortIds.end());
+        } else if (auto it = std::find(route.sourcePortIds.begin(), route.sourcePortIds.end(),
+                                       portId);
+                   it != route.sourcePortIds.end()) {
+            portIds.insert(route.sinkPortId);
+        }
+    }
+    return portIds;
+}
+
 std::vector<AudioPortConfig> ModuleConfig::generateAudioMixPortConfigs(
         const std::vector<AudioPort>& ports, bool isInput, bool singleProfile) const {
     std::vector<AudioPortConfig> result;
diff --git a/audio/aidl/vts/ModuleConfig.h b/audio/aidl/vts/ModuleConfig.h
index 0cbf24d..4a87f8c 100644
--- a/audio/aidl/vts/ModuleConfig.h
+++ b/audio/aidl/vts/ModuleConfig.h
@@ -38,9 +38,6 @@
     generateOffloadInfoIfNeeded(
             const aidl::android::media::audio::common::AudioPortConfig& portConfig);
 
-    std::vector<aidl::android::media::audio::common::AudioPort> getAudioPortsForDeviceTypes(
-            const std::vector<aidl::android::media::audio::common::AudioDeviceType>& deviceTypes,
-            const std::string& connection = "");
     static std::vector<aidl::android::media::audio::common::AudioPort> getAudioPortsForDeviceTypes(
             const std::vector<aidl::android::media::audio::common::AudioPort>& ports,
             const std::vector<aidl::android::media::audio::common::AudioDeviceType>& deviceTypes,
@@ -53,6 +50,9 @@
     std::string getError() const { return mStatus.getMessage(); }
 
     std::vector<aidl::android::media::audio::common::AudioPort> getAttachedDevicePorts() const;
+    std::vector<aidl::android::media::audio::common::AudioPort> getAudioPortsForDeviceTypes(
+            const std::vector<aidl::android::media::audio::common::AudioDeviceType>& deviceTypes,
+            const std::string& connection = "") const;
     std::vector<aidl::android::media::audio::common::AudioPort> getConnectedExternalDevicePorts()
             const;
     std::set<int32_t> getConnectedSinkDevicePorts() const;
@@ -85,6 +85,8 @@
     std::vector<aidl::android::media::audio::common::AudioPort> getMmapInMixPorts(
             bool connectedOnly /*Permanently attached and connected external devices*/,
             bool singlePort) const;
+    std::vector<aidl::android::media::audio::common::AudioPort> getRemoteSubmixPorts(
+            bool isInput, bool singlePort) const;
 
     std::vector<aidl::android::media::audio::common::AudioPort> getConnectedDevicesPortsForMixPort(
             bool isInput, const aidl::android::media::audio::common::AudioPort& mixPort) const {
@@ -103,6 +105,13 @@
     std::optional<aidl::android::media::audio::common::AudioPort>
     getSourceMixPortForConnectedDevice() const;
 
+    std::vector<aidl::android::media::audio::common::AudioPort> getRoutableDevicePortsForMixPort(
+            const aidl::android::media::audio::common::AudioPort& port,
+            bool connectedOnly /*Permanently attached and connected external devices*/) const;
+    std::vector<aidl::android::media::audio::common::AudioPort> getRoutableMixPortsForDevicePort(
+            const aidl::android::media::audio::common::AudioPort& port,
+            bool connectedOnly /*Permanently attached and connected external devices*/) const;
+
     std::optional<SrcSinkPair> getNonRoutableSrcSinkPair(bool isInput) const;
     std::optional<SrcSinkPair> getRoutableSrcSinkPair(bool isInput) const;
     std::vector<SrcSinkGroup> getRoutableSrcSinkGroups(bool isInput) const;
@@ -173,6 +182,7 @@
             bool isInput, bool connectedOnly, bool singlePort,
             const std::function<bool(const aidl::android::media::audio::common::AudioPort&)>& pred)
             const;
+    std::set<int32_t> findRoutablePortIds(int32_t portId) const;
     std::vector<aidl::android::media::audio::common::AudioPortConfig> generateAudioMixPortConfigs(
             const std::vector<aidl::android::media::audio::common::AudioPort>& ports, bool isInput,
             bool singleProfile) const;
diff --git a/audio/aidl/vts/TestUtils.cpp b/audio/aidl/vts/TestUtils.cpp
new file mode 100644
index 0000000..f018468
--- /dev/null
+++ b/audio/aidl/vts/TestUtils.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestUtils.h"
+
+#define LOG_TAG "VtsHalAudio_TestUtils"
+
+#include <android-base/logging.h>
+
+namespace android::hardware::audio::common::testing {
+
+namespace detail {
+void TestExecutionTracer::OnTestStart(const ::testing::TestInfo& test_info) {
+    TraceTestState("Started", test_info);
+}
+
+void TestExecutionTracer::OnTestEnd(const ::testing::TestInfo& test_info) {
+    TraceTestState("Completed", test_info);
+}
+
+void TestExecutionTracer::OnTestPartResult(const ::testing::TestPartResult& result) {
+    LOG(INFO) << result;
+}
+
+void TestExecutionTracer::TraceTestState(const std::string& state,
+                                         const ::testing::TestInfo& test_info) {
+    LOG(INFO) << state << " " << test_info.test_suite_name() << "::" << test_info.name();
+}
+}
+}
\ No newline at end of file
diff --git a/audio/aidl/vts/TestUtils.h b/audio/aidl/vts/TestUtils.h
index b559669..e9f3251 100644
--- a/audio/aidl/vts/TestUtils.h
+++ b/audio/aidl/vts/TestUtils.h
@@ -18,15 +18,24 @@
 
 #include <algorithm>
 #include <initializer_list>
-#include <iostream>
 
 #include <android/binder_auto_utils.h>
 #include <gtest/gtest.h>
+#include <system/audio_aidl_utils.h>
 
 namespace android::hardware::audio::common::testing {
 
 namespace detail {
 
+class TestExecutionTracer : public ::testing::EmptyTestEventListener {
+  public:
+    void OnTestStart(const ::testing::TestInfo& test_info) override;
+    void OnTestEnd(const ::testing::TestInfo& test_info) override;
+    void OnTestPartResult(const ::testing::TestPartResult& result) override;
+  private:
+    static void TraceTestState(const std::string& state, const ::testing::TestInfo& test_info);
+};
+
 inline ::testing::AssertionResult assertIsOk(const char* expr, const ::ndk::ScopedAStatus& status) {
     if (status.isOk()) {
         return ::testing::AssertionSuccess();
@@ -83,4 +92,4 @@
         if ((flags).hwAcceleratorMode == Flags::HardwareAccelerator::TUNNEL || (flags).bypass) { \
             GTEST_SKIP() << "Skip data path for offload";                                        \
         }                                                                                        \
-    })
\ No newline at end of file
+    })
diff --git a/audio/aidl/vts/VtsHalAECTargetTest.cpp b/audio/aidl/vts/VtsHalAECTargetTest.cpp
index 0354e3c..39168b1 100644
--- a/audio/aidl/vts/VtsHalAECTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAECTargetTest.cpp
@@ -34,6 +34,7 @@
 using aidl::android::hardware::audio::effect::IFactory;
 using aidl::android::hardware::audio::effect::Parameter;
 using aidl::android::hardware::audio::effect::Range;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
 
 enum ParamName { PARAM_INSTANCE_NAME, PARAM_ECHO_DELAY, PARAM_MOBILE_MODE };
 using AECParamTestParam = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>,
@@ -178,6 +179,7 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
+    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
     ABinderProcess_setThreadPoolMaxThreadCount(1);
     ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalAGC1TargetTest.cpp b/audio/aidl/vts/VtsHalAGC1TargetTest.cpp
index 65c6a8f..6066025 100644
--- a/audio/aidl/vts/VtsHalAGC1TargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAGC1TargetTest.cpp
@@ -28,6 +28,7 @@
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::IFactory;
 using aidl::android::hardware::audio::effect::Parameter;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
 
 enum ParamName {
     PARAM_INSTANCE_NAME,
@@ -189,6 +190,7 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
+    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
     ABinderProcess_setThreadPoolMaxThreadCount(1);
     ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalAGC2TargetTest.cpp b/audio/aidl/vts/VtsHalAGC2TargetTest.cpp
index 46a569e..8793e4c 100644
--- a/audio/aidl/vts/VtsHalAGC2TargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAGC2TargetTest.cpp
@@ -29,6 +29,7 @@
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::IFactory;
 using aidl::android::hardware::audio::effect::Parameter;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
 
 enum ParamName {
     PARAM_INSTANCE_NAME,
@@ -194,6 +195,7 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
+    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
     ABinderProcess_setThreadPoolMaxThreadCount(1);
     ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
index ccf7cfc..697aae9 100644
--- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -87,6 +87,7 @@
 using aidl::android::media::audio::common::AudioFormatType;
 using aidl::android::media::audio::common::AudioIoFlags;
 using aidl::android::media::audio::common::AudioLatencyMode;
+using aidl::android::media::audio::common::AudioMMapPolicy;
 using aidl::android::media::audio::common::AudioMMapPolicyInfo;
 using aidl::android::media::audio::common::AudioMMapPolicyType;
 using aidl::android::media::audio::common::AudioMode;
@@ -107,14 +108,28 @@
 using aidl::android::media::audio::common::Void;
 using android::hardware::audio::common::StreamLogic;
 using android::hardware::audio::common::StreamWorker;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
 using ndk::enum_range;
 using ndk::ScopedAStatus;
 
 template <typename T>
-auto findById(std::vector<T>& v, int32_t id) {
+std::set<int32_t> extractIds(const std::vector<T>& v) {
+    std::set<int32_t> ids;
+    std::transform(v.begin(), v.end(), std::inserter(ids, ids.begin()),
+                   [](const auto& entity) { return entity.id; });
+    return ids;
+}
+
+template <typename T>
+auto findById(const std::vector<T>& v, int32_t id) {
     return std::find_if(v.begin(), v.end(), [&](const auto& e) { return e.id == id; });
 }
 
+template <typename T>
+auto findAny(const std::vector<T>& v, const std::set<int32_t>& ids) {
+    return std::find_if(v.begin(), v.end(), [&](const auto& e) { return ids.count(e.id) > 0; });
+}
+
 template <typename C>
 std::vector<int32_t> GetNonExistentIds(const C& allIds) {
     if (allIds.empty()) {
@@ -153,8 +168,10 @@
     static int nextId = 0;
     using Tag = AudioDeviceAddress::Tag;
     const auto& deviceDescription = port.ext.get<AudioPortExt::Tag::device>().device.type;
-    AudioDeviceAddress address;
-    if (kPointToPointConnections.count(deviceDescription.connection) == 0) {
+    AudioDeviceAddress address = port.ext.get<AudioPortExt::Tag::device>().device.address;
+    // If the address is already set, do not re-generate.
+    if (address == AudioDeviceAddress() &&
+        kPointToPointConnections.count(deviceDescription.connection) == 0) {
         switch (suggestDeviceAddressTag(deviceDescription)) {
             case Tag::id:
                 address = AudioDeviceAddress::make<Tag::id>(std::to_string(++nextId));
@@ -415,18 +432,21 @@
 // Can be used as a base for any test here, does not depend on the fixture GTest parameters.
 class AudioCoreModuleBase {
   public:
-    // Default buffer sizes are used mostly for negative tests.
-    static constexpr int kDefaultBufferSizeFrames = 256;
+    // Fixed buffer size are used for negative tests only. For any tests involving stream
+    // opening that must success, the minimum buffer size must be obtained from a patch.
+    // This is implemented by the 'StreamFixture' utility class.
+    static constexpr int kNegativeTestBufferSizeFrames = 256;
     static constexpr int kDefaultLargeBufferSizeFrames = 48000;
 
-    void SetUpImpl(const std::string& moduleName) {
-        ASSERT_NO_FATAL_FAILURE(ConnectToService(moduleName));
+    void SetUpImpl(const std::string& moduleName, bool setUpDebug = true) {
+        ASSERT_NO_FATAL_FAILURE(ConnectToService(moduleName, setUpDebug));
         ASSERT_IS_OK(module->getAudioPorts(&initialPorts));
         ASSERT_IS_OK(module->getAudioRoutes(&initialRoutes));
     }
 
     void TearDownImpl() {
         debug.reset();
+        ASSERT_NE(module, nullptr);
         std::vector<AudioPort> finalPorts;
         ASSERT_IS_OK(module->getAudioPorts(&finalPorts));
         EXPECT_NO_FATAL_FAILURE(VerifyVectorsAreEqual<AudioPort>(initialPorts, finalPorts))
@@ -437,21 +457,26 @@
                 << "The list of audio routes was not restored to the initial state";
     }
 
-    void ConnectToService(const std::string& moduleName) {
+    void ConnectToService(const std::string& moduleName, bool setUpDebug) {
         ASSERT_EQ(module, nullptr);
         ASSERT_EQ(debug, nullptr);
         module = IModule::fromBinder(binderUtil.connectToService(moduleName));
         ASSERT_NE(module, nullptr);
-        ASSERT_NO_FATAL_FAILURE(SetUpDebug());
+        if (setUpDebug) {
+            ASSERT_NO_FATAL_FAILURE(SetUpDebug());
+        }
     }
 
     void RestartService() {
         ASSERT_NE(module, nullptr);
         moduleConfig.reset();
+        const bool setUpDebug = !!debug;
         debug.reset();
         module = IModule::fromBinder(binderUtil.restartService());
         ASSERT_NE(module, nullptr);
-        ASSERT_NO_FATAL_FAILURE(SetUpDebug());
+        if (setUpDebug) {
+            ASSERT_NO_FATAL_FAILURE(SetUpDebug());
+        }
     }
 
     void SetUpDebug() {
@@ -491,9 +516,7 @@
                          const std::string& errorMessage) {
         std::vector<Entity> entities;
         { ASSERT_IS_OK((module.get()->*getter)(&entities)); }
-        std::transform(entities.begin(), entities.end(),
-                       std::inserter(*entityIds, entityIds->begin()),
-                       [](const auto& entity) { return entity.id; });
+        *entityIds = extractIds<Entity>(entities);
         EXPECT_EQ(entities.size(), entityIds->size()) << errorMessage;
     }
 
@@ -1110,6 +1133,7 @@
 template <typename T>
 struct IOTraits {
     static constexpr bool is_input = std::is_same_v<T, IStreamIn>;
+    static constexpr const char* directionStr = is_input ? "input" : "output";
     using Worker = std::conditional_t<is_input, StreamReader, StreamWriter>;
 };
 
@@ -1141,8 +1165,7 @@
     }
     ScopedAStatus SetUpNoChecks(IModule* module, const AudioPortConfig& portConfig,
                                 long bufferSizeFrames);
-    void SetUp(IModule* module, long bufferSizeFrames) {
-        ASSERT_NO_FATAL_FAILURE(SetUpPortConfig(module));
+    void SetUpStream(IModule* module, long bufferSizeFrames) {
         ASSERT_IS_OK(SetUpNoChecks(module, bufferSizeFrames)) << "port config id " << getPortId();
         ASSERT_NE(nullptr, mStream) << "port config id " << getPortId();
         EXPECT_GE(mDescriptor.bufferSizeFrames, bufferSizeFrames)
@@ -1150,6 +1173,10 @@
         mContext.emplace(mDescriptor);
         ASSERT_NO_FATAL_FAILURE(mContext.value().checkIsValid());
     }
+    void SetUp(IModule* module, long bufferSizeFrames) {
+        ASSERT_NO_FATAL_FAILURE(SetUpPortConfig(module));
+        ASSERT_NO_FATAL_FAILURE(SetUpStream(module, bufferSizeFrames));
+    }
     Stream* get() const { return mStream.get(); }
     const StreamContext* getContext() const { return mContext ? &(mContext.value()) : nullptr; }
     StreamEventReceiver* getEventReceiver() { return mStreamCallback->getEventReceiver(); }
@@ -1237,11 +1264,25 @@
                    const AudioPortConfig& portConfig2)
         : mSrcPortConfig(sinkIsCfg1 ? portConfig2 : portConfig1),
           mSinkPortConfig(sinkIsCfg1 ? portConfig1 : portConfig2) {}
+    WithAudioPatch(const WithAudioPatch& patch, const AudioPortConfig& srcPortConfig,
+                   const AudioPortConfig& sinkPortConfig)
+        : mInitialPatch(patch.mPatch),
+          mSrcPortConfig(srcPortConfig),
+          mSinkPortConfig(sinkPortConfig),
+          mModule(patch.mModule),
+          mPatch(patch.mPatch) {}
     WithAudioPatch(const WithAudioPatch&) = delete;
     WithAudioPatch& operator=(const WithAudioPatch&) = delete;
     ~WithAudioPatch() {
         if (mModule != nullptr && mPatch.id != 0) {
-            EXPECT_IS_OK(mModule->resetAudioPatch(mPatch.id)) << "patch id " << getId();
+            if (mInitialPatch.has_value()) {
+                AudioPatch ignored;
+                // This releases our port configs so that they can be reset.
+                EXPECT_IS_OK(mModule->setAudioPatch(*mInitialPatch, &ignored))
+                        << "patch id " << mInitialPatch->id;
+            } else {
+                EXPECT_IS_OK(mModule->resetAudioPatch(mPatch.id)) << "patch id " << getId();
+            }
         }
     }
     void SetUpPortConfigs(IModule* module) {
@@ -1263,8 +1304,21 @@
             EXPECT_GT(latencyMs, 0) << "patch id " << getId();
         }
     }
+    void VerifyAgainstAllPatches(IModule* module) {
+        std::vector<AudioPatch> allPatches;
+        ASSERT_IS_OK(module->getAudioPatches(&allPatches));
+        const auto& patchIt = findById(allPatches, getId());
+        ASSERT_NE(patchIt, allPatches.end()) << "patch id " << getId();
+        if (get() != *patchIt) {
+            FAIL() << "Stored patch: " << get().toString() << " is not the same as returned "
+                   << "by the HAL module: " << patchIt->toString();
+        }
+    }
     int32_t getId() const { return mPatch.id; }
     const AudioPatch& get() const { return mPatch; }
+    int32_t getMinimumStreamBufferSizeFrames() const {
+        return mPatch.minimumStreamBufferSizeFrames;
+    }
     const AudioPortConfig& getSinkPortConfig() const { return mSinkPortConfig.get(); }
     const AudioPortConfig& getSrcPortConfig() const { return mSrcPortConfig.get(); }
     const AudioPortConfig& getPortConfig(bool getSink) const {
@@ -1272,6 +1326,7 @@
     }
 
   private:
+    std::optional<AudioPatch> mInitialPatch;
     WithAudioPortConfig mSrcPortConfig;
     WithAudioPortConfig mSinkPortConfig;
     IModule* mModule = nullptr;
@@ -1475,8 +1530,25 @@
                 << "port ID " << connectedPortId;
         EXPECT_EQ(portConnected.get(), connectedPort);
         const auto& portProfiles = connectedPort.profiles;
-        EXPECT_NE(0UL, portProfiles.size())
-                << "Connected port has no profiles: " << connectedPort.toString();
+        if (portProfiles.empty()) {
+            const auto routableMixPorts = moduleConfig->getRoutableMixPortsForDevicePort(
+                    connectedPort, true /*connectedOnly*/);
+            bool hasMixPortWithStaticProfile = false;
+            for (const auto& mixPort : routableMixPorts) {
+                const auto& mixPortProfiles = mixPort.profiles;
+                if (!mixPortProfiles.empty() &&
+                    !std::all_of(mixPortProfiles.begin(), mixPortProfiles.end(),
+                                 [](const auto& profile) {
+                                     return profile.format.type == AudioFormatType::DEFAULT;
+                                 })) {
+                    hasMixPortWithStaticProfile = true;
+                    break;
+                }
+            }
+            EXPECT_TRUE(hasMixPortWithStaticProfile)
+                    << "Connected port has no profiles and no routable mix ports with profiles: "
+                    << connectedPort.toString();
+        }
         const auto dynamicProfileIt =
                 std::find_if(portProfiles.begin(), portProfiles.end(), [](const auto& profile) {
                     return profile.format.type == AudioFormatType::DEFAULT;
@@ -1501,7 +1573,7 @@
         {
             aidl::android::hardware::audio::core::IModule::OpenInputStreamArguments args;
             args.portConfigId = portConfigId;
-            args.bufferSizeFrames = kDefaultBufferSizeFrames;
+            args.bufferSizeFrames = kNegativeTestBufferSizeFrames;
             aidl::android::hardware::audio::core::IModule::OpenInputStreamReturn ret;
             EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->openInputStream(args, &ret))
                     << "port config ID " << portConfigId;
@@ -1510,7 +1582,7 @@
         {
             aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args;
             args.portConfigId = portConfigId;
-            args.bufferSizeFrames = kDefaultBufferSizeFrames;
+            args.bufferSizeFrames = kNegativeTestBufferSizeFrames;
             aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret;
             EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->openOutputStream(args, &ret))
                     << "port config ID " << portConfigId;
@@ -1560,7 +1632,8 @@
         EXPECT_NE(portConfigsAfter.end(), afterIt)
                 << " port config ID " << c.id << " was removed by reset";
         if (afterIt != portConfigsAfter.end()) {
-            EXPECT_EQ(c, *afterIt);
+            EXPECT_TRUE(c == *afterIt)
+                    << "Expected: " << c.toString() << "; Actual: " << afterIt->toString();
         }
     }
 }
@@ -1672,6 +1745,10 @@
     doNotSimulateConnections.flags().simulateDeviceConnections = false;
     ASSERT_NO_FATAL_FAILURE(doNotSimulateConnections.SetUp(module.get()));
     for (const auto& port : ports) {
+        // Virtual devices may not require external hardware and thus can always be connected.
+        if (port.ext.get<AudioPortExt::device>().device.type.connection ==
+            AudioDeviceDescription::CONNECTION_VIRTUAL)
+            continue;
         AudioPort portWithData = GenerateUniqueDeviceAddress(port), connectedPort;
         ScopedAStatus status = module->connectExternalDevice(portWithData, &connectedPort);
         EXPECT_STATUS(EX_ILLEGAL_STATE, status) << "static port " << portWithData.toString();
@@ -2133,7 +2210,13 @@
         std::vector<AudioMMapPolicyInfo> policyInfos;
         EXPECT_IS_OK(module->getMmapPolicyInfos(mmapPolicyType, &policyInfos))
                 << toString(mmapPolicyType);
-        EXPECT_EQ(isMmapSupported, !policyInfos.empty());
+        const bool isMMapSupportedByPolicyInfos =
+                std::find_if(policyInfos.begin(), policyInfos.end(), [](const auto& info) {
+                    return info.mmapPolicy == AudioMMapPolicy::AUTO ||
+                           info.mmapPolicy == AudioMMapPolicy::ALWAYS;
+                }) != policyInfos.end();
+        EXPECT_EQ(isMmapSupported, isMMapSupportedByPolicyInfos)
+                << ::android::internal::ToString(policyInfos);
     }
 }
 
@@ -2512,6 +2595,260 @@
     std::vector<std::string> mStatuses;
 };
 
+// A helper which sets up necessary HAL structures for a proper stream initialization.
+//
+// The full sequence of actions to set up a stream is as follows:
+//
+//  device port -> connect if necessary -> set up port config   | -> set up patch
+//  mix port -> set up port config, unless it has been provided |
+//
+//  then, from the patch, figure out the minimum HAL buffer size -> set up stream
+//
+// This sequence is reflected in the order of fields declaration.
+// Various tests need to be able to start and stop at various point in this sequence,
+// this is why there are methods that do just part of the work.
+//
+// Note: To maximize test coverage, this class relies on simulation of external device
+// connections by the HAL module.
+template <typename Stream>
+class StreamFixture {
+  public:
+    // Tests might need to override the direction.
+    StreamFixture(bool isInput = IOTraits<Stream>::is_input) : mIsInput(isInput) {}
+
+    void SetUpPortConfigAnyMixPort(IModule* module, ModuleConfig* moduleConfig,
+                                   bool connectedOnly) {
+        const auto mixPorts = moduleConfig->getMixPorts(mIsInput, connectedOnly);
+        mSkipTestReason = "No mix ports";
+        for (const auto& mixPort : mixPorts) {
+            mSkipTestReason = "";
+            ASSERT_NO_FATAL_FAILURE(SetUpPortConfigForMixPortOrConfig(module, moduleConfig, mixPort,
+                                                                      connectedOnly));
+            if (mSkipTestReason.empty()) break;
+        }
+    }
+
+    void SetUpPortConfigForMixPortOrConfig(
+            IModule* module, ModuleConfig* moduleConfig, const AudioPort& initialMixPort,
+            bool connectedOnly, const std::optional<AudioPortConfig>& mixPortConfig = {}) {
+        if (mixPortConfig.has_value() && !connectedOnly) {
+            // Connecting an external device may cause change in mix port profiles and the provided
+            // config may become invalid.
+            LOG(FATAL) << __func__ << ": when specifying a mix port config, it is not allowed "
+                       << "to change connected devices, thus `connectedOnly` must be `true`";
+        }
+        std::optional<AudioPort> connectedDevicePort;
+        ASSERT_NO_FATAL_FAILURE(SetUpDevicePortForMixPort(module, moduleConfig, initialMixPort,
+                                                          connectedOnly, &connectedDevicePort));
+        if (!mSkipTestReason.empty()) return;
+        if (mixPortConfig.has_value()) {
+            ASSERT_NO_FATAL_FAILURE(
+                    SetUpPortConfig(module, moduleConfig, *mixPortConfig, *connectedDevicePort));
+        } else {
+            // If an external device was connected, the profiles of the mix port might have changed.
+            AudioPort mixPort;
+            ASSERT_NO_FATAL_FAILURE(module->getAudioPort(initialMixPort.id, &mixPort));
+            ASSERT_NO_FATAL_FAILURE(
+                    SetUpPortConfig(module, moduleConfig, mixPort, *connectedDevicePort));
+        }
+    }
+
+    void SetUpPortConfig(IModule* module, ModuleConfig* moduleConfig, const AudioPort& mixPort,
+                         const AudioPort& devicePort) {
+        auto mixPortConfig = moduleConfig->getSingleConfigForMixPort(mIsInput, mixPort);
+        ASSERT_TRUE(mixPortConfig.has_value())
+                << "Unable to generate port config for mix port " << mixPort.toString();
+        ASSERT_NO_FATAL_FAILURE(SetUpPortConfig(module, moduleConfig, *mixPortConfig, devicePort));
+    }
+    void SetUpPortConfig(IModule* module, ModuleConfig* moduleConfig,
+                         const AudioPortConfig& mixPortConfig, const AudioPort& devicePort) {
+        ASSERT_NO_FATAL_FAILURE(SetUpPatch(module, moduleConfig, mixPortConfig, devicePort));
+        mStream = std::make_unique<WithStream<Stream>>(mMixPortConfig->get());
+        ASSERT_NO_FATAL_FAILURE(mStream->SetUpPortConfig(module));
+    }
+
+    ScopedAStatus SetUpStreamNoChecks(IModule* module) {
+        return mStream->SetUpNoChecks(module, getMinimumStreamBufferSizeFrames());
+    }
+    void SetUpStream(IModule* module) {
+        ASSERT_NO_FATAL_FAILURE(mStream->SetUpStream(module, getMinimumStreamBufferSizeFrames()));
+    }
+
+    void SetUpStreamForDevicePort(IModule* module, ModuleConfig* moduleConfig,
+                                  const AudioPort& devicePort, bool connectedOnly = false) {
+        ASSERT_NO_FATAL_FAILURE(
+                SetUpPortConfigForDevicePort(module, moduleConfig, devicePort, connectedOnly));
+        if (!mSkipTestReason.empty()) return;
+        ASSERT_NO_FATAL_FAILURE(SetUpStream(module));
+    }
+    void SetUpStreamForAnyMixPort(IModule* module, ModuleConfig* moduleConfig,
+                                  bool connectedOnly = false) {
+        ASSERT_NO_FATAL_FAILURE(SetUpPortConfigAnyMixPort(module, moduleConfig, connectedOnly));
+        if (!mSkipTestReason.empty()) return;
+        ASSERT_NO_FATAL_FAILURE(SetUpStream(module));
+    }
+    void SetUpStreamForMixPort(IModule* module, ModuleConfig* moduleConfig,
+                               const AudioPort& mixPort, bool connectedOnly = false) {
+        ASSERT_NO_FATAL_FAILURE(
+                SetUpPortConfigForMixPortOrConfig(module, moduleConfig, mixPort, connectedOnly));
+        if (!mSkipTestReason.empty()) return;
+        ASSERT_NO_FATAL_FAILURE(SetUpStream(module));
+    }
+    void SetUpStreamForPortsPair(IModule* module, ModuleConfig* moduleConfig,
+                                 const AudioPort& mixPort, const AudioPort& devicePort) {
+        ASSERT_NO_FATAL_FAILURE(SetUpPortConfig(module, moduleConfig, mixPort, devicePort));
+        if (!mSkipTestReason.empty()) return;
+        ASSERT_NO_FATAL_FAILURE(SetUpStream(module));
+    }
+    void SetUpStreamForMixPortConfig(IModule* module, ModuleConfig* moduleConfig,
+                                     const AudioPortConfig& mixPortConfig) {
+        // Since mix port configs may change after connecting an external device,
+        // only connected device ports are considered.
+        constexpr bool connectedOnly = true;
+        const auto& ports = moduleConfig->getMixPorts(mIsInput, connectedOnly);
+        const auto mixPortIt = findById<AudioPort>(ports, mixPortConfig.portId);
+        ASSERT_NE(mixPortIt, ports.end()) << "Port id " << mixPortConfig.portId << " not found";
+        ASSERT_NO_FATAL_FAILURE(SetUpPortConfigForMixPortOrConfig(module, moduleConfig, *mixPortIt,
+                                                                  connectedOnly, mixPortConfig));
+        if (!mSkipTestReason.empty()) return;
+        ASSERT_NO_FATAL_FAILURE(SetUpStream(module));
+    }
+    void SetUpPatchForMixPortConfig(IModule* module, ModuleConfig* moduleConfig,
+                                    const AudioPortConfig& mixPortConfig) {
+        constexpr bool connectedOnly = true;
+        const auto& ports = moduleConfig->getMixPorts(mIsInput, connectedOnly);
+        const auto mixPortIt = findById<AudioPort>(ports, mixPortConfig.portId);
+        ASSERT_NE(mixPortIt, ports.end()) << "Port id " << mixPortConfig.portId << " not found";
+        std::optional<AudioPort> connectedDevicePort;
+        ASSERT_NO_FATAL_FAILURE(SetUpDevicePortForMixPort(module, moduleConfig, *mixPortIt,
+                                                          connectedOnly, &connectedDevicePort));
+        if (!mSkipTestReason.empty()) return;
+        ASSERT_NO_FATAL_FAILURE(
+                SetUpPatch(module, moduleConfig, mixPortConfig, *connectedDevicePort));
+    }
+
+    void ReconnectPatch(IModule* module) {
+        mPatch = std::make_unique<WithAudioPatch>(mIsInput, mMixPortConfig->get(),
+                                                  mDevicePortConfig->get());
+        ASSERT_NO_FATAL_FAILURE(mPatch->SetUp(module));
+    }
+    void TeardownPatch() { mPatch.reset(); }
+    // Assuming that the patch is set up, while the stream isn't yet,
+    // tear the patch down and set up stream.
+    void TeardownPatchSetUpStream(IModule* module) {
+        const int32_t bufferSize = getMinimumStreamBufferSizeFrames();
+        ASSERT_NO_FATAL_FAILURE(TeardownPatch());
+        mStream = std::make_unique<WithStream<Stream>>(mMixPortConfig->get());
+        ASSERT_NO_FATAL_FAILURE(mStream->SetUpPortConfig(module));
+        ASSERT_NO_FATAL_FAILURE(mStream->SetUpStream(module, bufferSize));
+    }
+
+    const AudioDevice& getDevice() const { return mDevice; }
+    int32_t getMinimumStreamBufferSizeFrames() const {
+        return mPatch->getMinimumStreamBufferSizeFrames();
+    }
+    const AudioPatch& getPatch() const { return mPatch->get(); }
+    const AudioPortConfig& getPortConfig() const { return mMixPortConfig->get(); }
+    int32_t getPortId() const { return mMixPortConfig->getId(); }
+    Stream* getStream() const { return mStream->get(); }
+    const StreamContext* getStreamContext() const { return mStream->getContext(); }
+    StreamEventReceiver* getStreamEventReceiver() { return mStream->getEventReceiver(); }
+    std::shared_ptr<Stream> getStreamSharedPointer() const { return mStream->getSharedPointer(); }
+    const std::string& skipTestReason() const { return mSkipTestReason; }
+
+  private:
+    void SetUpDevicePort(IModule* module, ModuleConfig* moduleConfig,
+                         const std::set<int32_t>& devicePortIds, bool connectedOnly,
+                         std::optional<AudioPort>* connectedDevicePort) {
+        const auto attachedDevicePorts = moduleConfig->getAttachedDevicePorts();
+        if (auto it = findAny<AudioPort>(attachedDevicePorts, devicePortIds);
+            it != attachedDevicePorts.end()) {
+            *connectedDevicePort = *it;
+            LOG(DEBUG) << __func__ << ": found attached port " << it->toString();
+        }
+        const auto connectedDevicePorts = moduleConfig->getConnectedExternalDevicePorts();
+        if (auto it = findAny<AudioPort>(connectedDevicePorts, devicePortIds);
+            it != connectedDevicePorts.end()) {
+            *connectedDevicePort = *it;
+            LOG(DEBUG) << __func__ << ": found connected port " << it->toString();
+        }
+        if (!connectedOnly && !connectedDevicePort->has_value()) {
+            const auto externalDevicePorts = moduleConfig->getExternalDevicePorts();
+            if (auto it = findAny<AudioPort>(externalDevicePorts, devicePortIds);
+                it != externalDevicePorts.end()) {
+                AudioPort portWithData = GenerateUniqueDeviceAddress(*it);
+                mPortConnected = std::make_unique<WithDevicePortConnectedState>(portWithData);
+                ASSERT_NO_FATAL_FAILURE(mPortConnected->SetUp(module, moduleConfig));
+                *connectedDevicePort = mPortConnected->get();
+                LOG(DEBUG) << __func__ << ": connected port " << mPortConnected->get().toString();
+            }
+        }
+    }
+    void SetUpDevicePortForMixPort(IModule* module, ModuleConfig* moduleConfig,
+                                   const AudioPort& mixPort, bool connectedOnly,
+                                   std::optional<AudioPort>* connectedDevicePort) {
+        const auto devicePorts =
+                moduleConfig->getRoutableDevicePortsForMixPort(mixPort, connectedOnly);
+        if (devicePorts.empty()) {
+            mSkipTestReason = std::string("No routable device ports found for mix port id ")
+                                      .append(std::to_string(mixPort.id));
+            LOG(DEBUG) << __func__ << ": " << mSkipTestReason;
+            return;
+        };
+        ASSERT_NO_FATAL_FAILURE(SetUpDevicePort(module, moduleConfig,
+                                                extractIds<AudioPort>(devicePorts), connectedOnly,
+                                                connectedDevicePort));
+        if (!connectedDevicePort->has_value()) {
+            mSkipTestReason = std::string("Unable to find a device port pair for mix port id ")
+                                      .append(std::to_string(mixPort.id));
+            LOG(DEBUG) << __func__ << ": " << mSkipTestReason;
+            return;
+        }
+    }
+    void SetUpPortConfigForDevicePort(IModule* module, ModuleConfig* moduleConfig,
+                                      const AudioPort& devicePort, bool connectedOnly) {
+        std::optional<AudioPort> connectedDevicePort;
+        ASSERT_NO_FATAL_FAILURE(SetUpDevicePort(module, moduleConfig, {devicePort.id},
+                                                connectedOnly, &connectedDevicePort));
+        if (!connectedDevicePort.has_value()) {
+            mSkipTestReason = std::string("Device port id ")
+                                      .append(std::to_string(devicePort.id))
+                                      .append(" is not attached and can not be connected");
+            return;
+        }
+        const auto mixPorts = moduleConfig->getRoutableMixPortsForDevicePort(
+                *connectedDevicePort, true /*connectedOnly*/);
+        if (mixPorts.empty()) {
+            mSkipTestReason = std::string("No routable mix ports found for device port id ")
+                                      .append(std::to_string(devicePort.id));
+            return;
+        }
+        ASSERT_NO_FATAL_FAILURE(
+                SetUpPortConfig(module, moduleConfig, *mixPorts.begin(), *connectedDevicePort));
+    }
+    void SetUpPatch(IModule* module, ModuleConfig* moduleConfig,
+                    const AudioPortConfig& mixPortConfig, const AudioPort& devicePort) {
+        mMixPortConfig = std::make_unique<WithAudioPortConfig>(mixPortConfig);
+        ASSERT_NO_FATAL_FAILURE(mMixPortConfig->SetUp(module));
+        mDevicePortConfig = std::make_unique<WithAudioPortConfig>(
+                moduleConfig->getSingleConfigForDevicePort(devicePort));
+        ASSERT_NO_FATAL_FAILURE(mDevicePortConfig->SetUp(module));
+        mDevice = devicePort.ext.get<AudioPortExt::device>().device;
+        mPatch = std::make_unique<WithAudioPatch>(mIsInput, mMixPortConfig->get(),
+                                                  mDevicePortConfig->get());
+        ASSERT_NO_FATAL_FAILURE(mPatch->SetUp(module));
+    }
+
+    const bool mIsInput;
+    std::string mSkipTestReason;
+    std::unique_ptr<WithDevicePortConnectedState> mPortConnected;
+    AudioDevice mDevice;
+    std::unique_ptr<WithAudioPortConfig> mMixPortConfig;
+    std::unique_ptr<WithAudioPortConfig> mDevicePortConfig;
+    std::unique_ptr<WithAudioPatch> mPatch;
+    std::unique_ptr<WithStream<Stream>> mStream;
+};
+
 template <typename Stream>
 class AudioStream : public AudioCoreModule {
   public:
@@ -2521,16 +2858,15 @@
     }
 
     void GetStreamCommon() {
-        const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
-        if (!portConfig.has_value()) {
-            GTEST_SKIP() << "No mix port for attached devices";
+        StreamFixture<Stream> stream;
+        ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForAnyMixPort(module.get(), moduleConfig.get()));
+        if (auto reason = stream.skipTestReason(); !reason.empty()) {
+            GTEST_SKIP() << reason;
         }
-        WithStream<Stream> stream(portConfig.value());
-        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
         std::shared_ptr<IStreamCommon> streamCommon1;
-        EXPECT_IS_OK(stream.get()->getStreamCommon(&streamCommon1));
+        EXPECT_IS_OK(stream.getStream()->getStreamCommon(&streamCommon1));
         std::shared_ptr<IStreamCommon> streamCommon2;
-        EXPECT_IS_OK(stream.get()->getStreamCommon(&streamCommon2));
+        EXPECT_IS_OK(stream.getStream()->getStreamCommon(&streamCommon2));
         ASSERT_NE(nullptr, streamCommon1);
         ASSERT_NE(nullptr, streamCommon2);
         EXPECT_EQ(streamCommon1->asBinder(), streamCommon2->asBinder())
@@ -2538,31 +2874,31 @@
     }
 
     void CloseTwice() {
-        const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
-        if (!portConfig.has_value()) {
-            GTEST_SKIP() << "No mix port for attached devices";
-        }
         std::shared_ptr<Stream> heldStream;
         {
-            WithStream<Stream> stream(portConfig.value());
-            ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
-            heldStream = stream.getSharedPointer();
+            StreamFixture<Stream> stream;
+            ASSERT_NO_FATAL_FAILURE(
+                    stream.SetUpStreamForAnyMixPort(module.get(), moduleConfig.get()));
+            if (auto reason = stream.skipTestReason(); !reason.empty()) {
+                GTEST_SKIP() << reason;
+            }
+            heldStream = stream.getStreamSharedPointer();
         }
         EXPECT_STATUS(EX_ILLEGAL_STATE, WithStream<Stream>::callClose(heldStream))
                 << "when closing the stream twice";
     }
 
     void PrepareToCloseTwice() {
-        const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
-        if (!portConfig.has_value()) {
-            GTEST_SKIP() << "No mix port for attached devices";
-        }
         std::shared_ptr<IStreamCommon> heldStreamCommon;
         {
-            WithStream<Stream> stream(portConfig.value());
-            ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+            StreamFixture<Stream> stream;
+            ASSERT_NO_FATAL_FAILURE(
+                    stream.SetUpStreamForAnyMixPort(module.get(), moduleConfig.get()));
+            if (auto reason = stream.skipTestReason(); !reason.empty()) {
+                GTEST_SKIP() << reason;
+            }
             std::shared_ptr<IStreamCommon> streamCommon;
-            ASSERT_IS_OK(stream.get()->getStreamCommon(&streamCommon));
+            ASSERT_IS_OK(stream.getStream()->getStreamCommon(&streamCommon));
             heldStreamCommon = streamCommon;
             EXPECT_IS_OK(streamCommon->prepareToClose());
             EXPECT_IS_OK(streamCommon->prepareToClose())
@@ -2575,9 +2911,13 @@
     void OpenAllConfigs() {
         const auto allPortConfigs =
                 moduleConfig->getPortConfigsForMixPorts(IOTraits<Stream>::is_input);
+        if (allPortConfigs.empty()) {
+            GTEST_SKIP() << "No mix ports for attached devices";
+        }
         for (const auto& portConfig : allPortConfigs) {
-            WithStream<Stream> stream(portConfig);
-            ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+            StreamFixture<Stream> stream;
+            ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForMixPortConfig(
+                    module.get(), moduleConfig.get(), portConfig));
         }
     }
 
@@ -2597,22 +2937,21 @@
 
     void OpenInvalidDirection() {
         // Important! The direction of the port config must be reversed.
-        const auto portConfig =
-                moduleConfig->getSingleConfigForMixPort(!IOTraits<Stream>::is_input);
-        if (!portConfig.has_value()) {
-            GTEST_SKIP() << "No mix port for attached devices";
+        StreamFixture<Stream> stream(!IOTraits<Stream>::is_input);
+        ASSERT_NO_FATAL_FAILURE(stream.SetUpPortConfigAnyMixPort(module.get(), moduleConfig.get(),
+                                                                 false /*connectedOnly*/));
+        if (auto reason = stream.skipTestReason(); !reason.empty()) {
+            GTEST_SKIP() << reason;
         }
-        WithStream<Stream> stream(portConfig.value());
-        ASSERT_NO_FATAL_FAILURE(stream.SetUpPortConfig(module.get()));
-        EXPECT_STATUS(EX_ILLEGAL_ARGUMENT,
-                      stream.SetUpNoChecks(module.get(), kDefaultBufferSizeFrames))
+        EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, stream.SetUpStreamNoChecks(module.get()))
                 << "port config ID " << stream.getPortId();
-        EXPECT_EQ(nullptr, stream.get());
+        EXPECT_EQ(nullptr, stream.getStream());
     }
 
     void OpenOverMaxCount() {
+        constexpr bool connectedOnly = true;
         constexpr bool isInput = IOTraits<Stream>::is_input;
-        auto ports = moduleConfig->getMixPorts(isInput, true /*connectedOnly*/);
+        auto ports = moduleConfig->getMixPorts(isInput, connectedOnly);
         bool hasSingleRun = false;
         for (const auto& port : ports) {
             const size_t maxStreamCount = port.ext.get<AudioPortExt::Tag::mix>().maxOpenStreamCount;
@@ -2625,16 +2964,16 @@
                 continue;
             }
             hasSingleRun = true;
-            std::optional<WithStream<Stream>> streamWraps[maxStreamCount + 1];
+            StreamFixture<Stream> streams[maxStreamCount + 1];
             for (size_t i = 0; i <= maxStreamCount; ++i) {
-                streamWraps[i].emplace(portConfigs[i]);
-                WithStream<Stream>& stream = streamWraps[i].value();
+                ASSERT_NO_FATAL_FAILURE(streams[i].SetUpPortConfigForMixPortOrConfig(
+                        module.get(), moduleConfig.get(), port, connectedOnly, portConfigs[i]));
+                ASSERT_EQ("", streams[i].skipTestReason());
+                auto& stream = streams[i];
                 if (i < maxStreamCount) {
-                    ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+                    ASSERT_NO_FATAL_FAILURE(stream.SetUpStream(module.get()));
                 } else {
-                    ASSERT_NO_FATAL_FAILURE(stream.SetUpPortConfig(module.get()));
-                    EXPECT_STATUS(EX_ILLEGAL_STATE,
-                                  stream.SetUpNoChecks(module.get(), kDefaultBufferSizeFrames))
+                    EXPECT_STATUS(EX_ILLEGAL_STATE, stream.SetUpStreamNoChecks(module.get()))
                             << "port config ID " << stream.getPortId() << ", maxOpenStreamCount is "
                             << maxStreamCount;
                 }
@@ -2654,12 +2993,11 @@
     }
 
     void ResetPortConfigWithOpenStream() {
-        const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
-        if (!portConfig.has_value()) {
-            GTEST_SKIP() << "No mix port for attached devices";
+        StreamFixture<Stream> stream;
+        ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForAnyMixPort(module.get(), moduleConfig.get()));
+        if (auto reason = stream.skipTestReason(); !reason.empty()) {
+            GTEST_SKIP() << reason;
         }
-        WithStream<Stream> stream(portConfig.value());
-        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
         EXPECT_STATUS(EX_ILLEGAL_STATE, module->resetAudioPortConfig(stream.getPortId()))
                 << "port config ID " << stream.getPortId();
     }
@@ -2673,14 +3011,13 @@
     }
 
     void UpdateHwAvSyncId() {
-        const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
-        if (!portConfig.has_value()) {
-            GTEST_SKIP() << "No mix port for attached devices";
+        StreamFixture<Stream> stream;
+        ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForAnyMixPort(module.get(), moduleConfig.get()));
+        if (auto reason = stream.skipTestReason(); !reason.empty()) {
+            GTEST_SKIP() << reason;
         }
-        WithStream<Stream> stream(portConfig.value());
-        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
         std::shared_ptr<IStreamCommon> streamCommon;
-        ASSERT_IS_OK(stream.get()->getStreamCommon(&streamCommon));
+        ASSERT_IS_OK(stream.getStream()->getStreamCommon(&streamCommon));
         ASSERT_NE(nullptr, streamCommon);
         const auto kStatuses = {EX_NONE, EX_ILLEGAL_ARGUMENT, EX_ILLEGAL_STATE};
         for (const auto id : {-100, -1, 0, 1, 100}) {
@@ -2693,14 +3030,13 @@
     }
 
     void GetVendorParameters() {
-        const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
-        if (!portConfig.has_value()) {
-            GTEST_SKIP() << "No mix port for attached devices";
+        StreamFixture<Stream> stream;
+        ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForAnyMixPort(module.get(), moduleConfig.get()));
+        if (auto reason = stream.skipTestReason(); !reason.empty()) {
+            GTEST_SKIP() << reason;
         }
-        WithStream<Stream> stream(portConfig.value());
-        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
         std::shared_ptr<IStreamCommon> streamCommon;
-        ASSERT_IS_OK(stream.get()->getStreamCommon(&streamCommon));
+        ASSERT_IS_OK(stream.getStream()->getStreamCommon(&streamCommon));
         ASSERT_NE(nullptr, streamCommon);
 
         bool isGetterSupported = false;
@@ -2714,14 +3050,13 @@
     }
 
     void SetVendorParameters() {
-        const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
-        if (!portConfig.has_value()) {
-            GTEST_SKIP() << "No mix port for attached devices";
+        StreamFixture<Stream> stream;
+        ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForAnyMixPort(module.get(), moduleConfig.get()));
+        if (auto reason = stream.skipTestReason(); !reason.empty()) {
+            GTEST_SKIP() << reason;
         }
-        WithStream<Stream> stream(portConfig.value());
-        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
         std::shared_ptr<IStreamCommon> streamCommon;
-        ASSERT_IS_OK(stream.get()->getStreamCommon(&streamCommon));
+        ASSERT_IS_OK(stream.getStream()->getStreamCommon(&streamCommon));
         ASSERT_NE(nullptr, streamCommon);
 
         bool isSupported = false;
@@ -2732,32 +3067,37 @@
     }
 
     void HwGainHwVolume() {
-        const auto ports =
-                moduleConfig->getMixPorts(IOTraits<Stream>::is_input, true /*connectedOnly*/);
+        // Since device connection emulation does not cover complete functionality,
+        // only use this test with connected devices.
+        constexpr bool connectedOnly = true;
+        const auto ports = moduleConfig->getMixPorts(IOTraits<Stream>::is_input, connectedOnly);
         if (ports.empty()) {
             GTEST_SKIP() << "No mix ports";
         }
         bool atLeastOneSupports = false;
         for (const auto& port : ports) {
-            const auto portConfig = moduleConfig->getSingleConfigForMixPort(true, port);
-            if (!portConfig.has_value()) continue;
-            WithStream<Stream> stream(portConfig.value());
-            ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+            SCOPED_TRACE(port.toString());
+            StreamFixture<Stream> stream;
+            ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForMixPort(module.get(), moduleConfig.get(),
+                                                                 port, connectedOnly));
+            if (!stream.skipTestReason().empty()) continue;
+            const auto portConfig = stream.getPortConfig();
+            SCOPED_TRACE(portConfig.toString());
             std::vector<std::vector<float>> validValues, invalidValues;
             bool isSupported = false;
             if constexpr (IOTraits<Stream>::is_input) {
-                GenerateTestArrays<float>(getChannelCount(portConfig.value().channelMask.value()),
+                GenerateTestArrays<float>(getChannelCount(portConfig.channelMask.value()),
                                           IStreamIn::HW_GAIN_MIN, IStreamIn::HW_GAIN_MAX,
                                           &validValues, &invalidValues);
                 EXPECT_NO_FATAL_FAILURE(TestAccessors<std::vector<float>>(
-                        stream.get(), &IStreamIn::getHwGain, &IStreamIn::setHwGain, validValues,
-                        invalidValues, &isSupported));
+                        stream.getStream(), &IStreamIn::getHwGain, &IStreamIn::setHwGain,
+                        validValues, invalidValues, &isSupported));
             } else {
-                GenerateTestArrays<float>(getChannelCount(portConfig.value().channelMask.value()),
+                GenerateTestArrays<float>(getChannelCount(portConfig.channelMask.value()),
                                           IStreamOut::HW_VOLUME_MIN, IStreamOut::HW_VOLUME_MAX,
                                           &validValues, &invalidValues);
                 EXPECT_NO_FATAL_FAILURE(TestAccessors<std::vector<float>>(
-                        stream.get(), &IStreamOut::getHwVolume, &IStreamOut::setHwVolume,
+                        stream.getStream(), &IStreamOut::getHwVolume, &IStreamOut::setHwVolume,
                         validValues, invalidValues, &isSupported));
             }
             if (isSupported) atLeastOneSupports = true;
@@ -2771,19 +3111,22 @@
     // currently we can only pass a nullptr, and the HAL module must either reject
     // it as an invalid argument, or say that offloaded effects are not supported.
     void AddRemoveEffectInvalidArguments() {
-        const auto ports =
-                moduleConfig->getMixPorts(IOTraits<Stream>::is_input, true /*connectedOnly*/);
+        constexpr bool connectedOnly = true;
+        const auto ports = moduleConfig->getMixPorts(IOTraits<Stream>::is_input, connectedOnly);
         if (ports.empty()) {
             GTEST_SKIP() << "No mix ports";
         }
         bool atLeastOneSupports = false;
         for (const auto& port : ports) {
-            const auto portConfig = moduleConfig->getSingleConfigForMixPort(true, port);
-            if (!portConfig.has_value()) continue;
-            WithStream<Stream> stream(portConfig.value());
-            ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+            SCOPED_TRACE(port.toString());
+            StreamFixture<Stream> stream;
+            ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForMixPort(module.get(), moduleConfig.get(),
+                                                                 port, connectedOnly));
+            if (!stream.skipTestReason().empty()) continue;
+            const auto portConfig = stream.getPortConfig();
+            SCOPED_TRACE(portConfig.toString());
             std::shared_ptr<IStreamCommon> streamCommon;
-            ASSERT_IS_OK(stream.get()->getStreamCommon(&streamCommon));
+            ASSERT_IS_OK(stream.getStream()->getStreamCommon(&streamCommon));
             ASSERT_NE(nullptr, streamCommon);
             ndk::ScopedAStatus addEffectStatus = streamCommon->addEffect(nullptr);
             ndk::ScopedAStatus removeEffectStatus = streamCommon->removeEffect(nullptr);
@@ -2803,11 +3146,14 @@
     }
 
     void OpenTwiceSamePortConfigImpl(const AudioPortConfig& portConfig) {
-        WithStream<Stream> stream1(portConfig);
-        ASSERT_NO_FATAL_FAILURE(stream1.SetUp(module.get(), kDefaultBufferSizeFrames));
+        StreamFixture<Stream> stream1;
+        ASSERT_NO_FATAL_FAILURE(
+                stream1.SetUpStreamForMixPortConfig(module.get(), moduleConfig.get(), portConfig));
+        ASSERT_EQ("", stream1.skipTestReason());
         WithStream<Stream> stream2;
-        EXPECT_STATUS(EX_ILLEGAL_STATE, stream2.SetUpNoChecks(module.get(), stream1.getPortConfig(),
-                                                              kDefaultBufferSizeFrames))
+        EXPECT_STATUS(EX_ILLEGAL_STATE,
+                      stream2.SetUpNoChecks(module.get(), stream1.getPortConfig(),
+                                            stream1.getMinimumStreamBufferSizeFrames()))
                 << "when opening a stream twice for the same port config ID "
                 << stream1.getPortId();
     }
@@ -2842,11 +3188,13 @@
         for (const auto& seq : sequences) {
             SCOPED_TRACE(std::string("Sequence ").append(seq.first));
             LOG(DEBUG) << __func__ << ": Sequence " << seq.first;
-            WithStream<Stream> stream(portConfig);
-            ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+            StreamFixture<Stream> stream;
+            ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForMixPortConfig(
+                    module.get(), moduleConfig.get(), portConfig));
+            ASSERT_EQ("", stream.skipTestReason());
             StreamLogicDriverInvalidCommand driver(seq.second);
-            typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver,
-                                                     stream.getEventReceiver());
+            typename IOTraits<Stream>::Worker worker(*stream.getStreamContext(), &driver,
+                                                     stream.getStreamEventReceiver());
             LOG(DEBUG) << __func__ << ": starting worker...";
             ASSERT_TRUE(worker.start());
             LOG(DEBUG) << __func__ << ": joining worker...";
@@ -2899,63 +3247,59 @@
     if (ports.empty()) {
         GTEST_SKIP() << "No input mix ports for attached devices";
     }
+    bool atLeastOnePort = false;
     for (const auto& port : ports) {
-        const auto portConfig = moduleConfig->getSingleConfigForMixPort(true, port);
-        ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for input mix port";
-        WithStream<IStreamIn> stream(portConfig.value());
-        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
-        {
-            // The port of the stream is not connected, thus the list of active mics must be empty.
-            std::vector<MicrophoneDynamicInfo> activeMics;
-            EXPECT_IS_OK(stream.get()->getActiveMicrophones(&activeMics));
-            EXPECT_TRUE(activeMics.empty()) << "a stream on an unconnected port returns a "
-                                               "non-empty list of active microphones";
+        auto micDevicePorts = ModuleConfig::getBuiltInMicPorts(
+                moduleConfig->getConnectedSourceDevicesPortsForMixPort(port));
+        if (micDevicePorts.empty()) continue;
+        atLeastOnePort = true;
+        SCOPED_TRACE(port.toString());
+        StreamFixture<IStreamIn> stream;
+        ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForPortsPair(module.get(), moduleConfig.get(),
+                                                               port, micDevicePorts[0]));
+        if (!stream.skipTestReason().empty()) continue;
+        std::vector<MicrophoneDynamicInfo> activeMics;
+        EXPECT_IS_OK(stream.getStream()->getActiveMicrophones(&activeMics));
+        EXPECT_FALSE(activeMics.empty());
+        for (const auto& mic : activeMics) {
+            EXPECT_NE(micInfos.end(),
+                      std::find_if(micInfos.begin(), micInfos.end(),
+                                   [&](const auto& micInfo) { return micInfo.id == mic.id; }))
+                    << "active microphone \"" << mic.id << "\" is not listed in "
+                    << "microphone infos returned by the module: "
+                    << ::android::internal::ToString(micInfos);
+            EXPECT_NE(0UL, mic.channelMapping.size())
+                    << "No channels specified for the microphone \"" << mic.id << "\"";
         }
-        if (auto micDevicePorts = ModuleConfig::getBuiltInMicPorts(
-                    moduleConfig->getConnectedSourceDevicesPortsForMixPort(port));
-            !micDevicePorts.empty()) {
-            auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(micDevicePorts[0]);
-            WithAudioPatch patch(true /*isInput*/, stream.getPortConfig(), devicePortConfig);
-            ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
-            std::vector<MicrophoneDynamicInfo> activeMics;
-            EXPECT_IS_OK(stream.get()->getActiveMicrophones(&activeMics));
-            EXPECT_FALSE(activeMics.empty());
-            for (const auto& mic : activeMics) {
-                EXPECT_NE(micInfos.end(),
-                          std::find_if(micInfos.begin(), micInfos.end(),
-                                       [&](const auto& micInfo) { return micInfo.id == mic.id; }))
-                        << "active microphone \"" << mic.id << "\" is not listed in "
-                        << "microphone infos returned by the module: "
-                        << ::android::internal::ToString(micInfos);
-                EXPECT_NE(0UL, mic.channelMapping.size())
-                        << "No channels specified for the microphone \"" << mic.id << "\"";
-            }
-        }
-        {
-            // Now the port of the stream is not connected again, re-check that there are no
-            // active microphones.
-            std::vector<MicrophoneDynamicInfo> activeMics;
-            EXPECT_IS_OK(stream.get()->getActiveMicrophones(&activeMics));
-            EXPECT_TRUE(activeMics.empty()) << "a stream on an unconnected port returns a "
-                                               "non-empty list of active microphones";
-        }
+        stream.TeardownPatch();
+        // Now the port of the stream is not connected, check that there are no active microphones.
+        std::vector<MicrophoneDynamicInfo> emptyMics;
+        EXPECT_IS_OK(stream.getStream()->getActiveMicrophones(&emptyMics));
+        EXPECT_TRUE(emptyMics.empty()) << "a stream on an unconnected port returns a "
+                                          "non-empty list of active microphones";
+    }
+    if (!atLeastOnePort) {
+        GTEST_SKIP() << "No input mix ports could be routed to built-in microphone devices";
     }
 }
 
 TEST_P(AudioStreamIn, MicrophoneDirection) {
     using MD = IStreamIn::MicrophoneDirection;
-    const auto ports = moduleConfig->getInputMixPorts(true /*connectedOnly*/);
+    constexpr bool connectedOnly = true;
+    const auto ports = moduleConfig->getInputMixPorts(connectedOnly);
     if (ports.empty()) {
         GTEST_SKIP() << "No input mix ports for attached devices";
     }
-    bool isSupported = false;
+    bool isSupported = false, atLeastOnePort = false;
     for (const auto& port : ports) {
-        const auto portConfig = moduleConfig->getSingleConfigForMixPort(true, port);
-        ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for input mix port";
-        WithStream<IStreamIn> stream(portConfig.value());
-        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+        SCOPED_TRACE(port.toString());
+        StreamFixture<IStreamIn> stream;
+        ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForMixPort(module.get(), moduleConfig.get(), port,
+                                                             connectedOnly));
+        if (!stream.skipTestReason().empty()) continue;
+        atLeastOnePort = true;
         EXPECT_NO_FATAL_FAILURE(
-                TestAccessors<MD>(stream.get(), &IStreamIn::getMicrophoneDirection,
+                TestAccessors<MD>(stream.getStream(), &IStreamIn::getMicrophoneDirection,
                                   &IStreamIn::setMicrophoneDirection,
                                   std::vector<MD>(enum_range<MD>().begin(), enum_range<MD>().end()),
                                   {}, &isSupported));
@@ -2964,21 +3308,27 @@
     if (!isSupported) {
         GTEST_SKIP() << "Microphone direction is not supported";
     }
+    if (!atLeastOnePort) {
+        GTEST_SKIP() << "No input mix ports could be routed to built-in microphone devices";
+    }
 }
 
 TEST_P(AudioStreamIn, MicrophoneFieldDimension) {
-    const auto ports = moduleConfig->getInputMixPorts(true /*connectedOnly*/);
+    constexpr bool connectedOnly = true;
+    const auto ports = moduleConfig->getInputMixPorts(connectedOnly);
     if (ports.empty()) {
         GTEST_SKIP() << "No input mix ports for attached devices";
     }
-    bool isSupported = false;
+    bool isSupported = false, atLeastOnePort = false;
     for (const auto& port : ports) {
-        const auto portConfig = moduleConfig->getSingleConfigForMixPort(true, port);
-        ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for input mix port";
-        WithStream<IStreamIn> stream(portConfig.value());
-        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+        SCOPED_TRACE(port.toString());
+        StreamFixture<IStreamIn> stream;
+        ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForMixPort(module.get(), moduleConfig.get(), port,
+                                                             connectedOnly));
+        if (!stream.skipTestReason().empty()) continue;
+        atLeastOnePort = true;
         EXPECT_NO_FATAL_FAILURE(TestAccessors<float>(
-                stream.get(), &IStreamIn::getMicrophoneFieldDimension,
+                stream.getStream(), &IStreamIn::getMicrophoneFieldDimension,
                 &IStreamIn::setMicrophoneFieldDimension,
                 {IStreamIn::MIC_FIELD_DIMENSION_WIDE_ANGLE,
                  IStreamIn::MIC_FIELD_DIMENSION_WIDE_ANGLE / 2.0f,
@@ -2995,6 +3345,9 @@
     if (!isSupported) {
         GTEST_SKIP() << "Microphone direction is not supported";
     }
+    if (!atLeastOnePort) {
+        GTEST_SKIP() << "No input mix ports could be routed to built-in microphone devices";
+    }
 }
 
 TEST_P(AudioStreamOut, OpenTwicePrimary) {
@@ -3009,65 +3362,79 @@
 }
 
 TEST_P(AudioStreamOut, RequireOffloadInfo) {
+    constexpr bool connectedOnly = true;
     const auto offloadMixPorts =
-            moduleConfig->getOffloadMixPorts(true /*connectedOnly*/, true /*singlePort*/);
+            moduleConfig->getOffloadMixPorts(connectedOnly, true /*singlePort*/);
     if (offloadMixPorts.empty()) {
         GTEST_SKIP()
                 << "No mix port for compressed offload that could be routed to attached devices";
     }
-    const auto config = moduleConfig->getSingleConfigForMixPort(false, *offloadMixPorts.begin());
-    ASSERT_TRUE(config.has_value()) << "No profiles specified for the compressed offload mix port";
-    WithAudioPortConfig portConfig(config.value());
-    ASSERT_NO_FATAL_FAILURE(portConfig.SetUp(module.get()));
+    StreamFixture<IStreamOut> stream;
+    ASSERT_NO_FATAL_FAILURE(stream.SetUpPortConfigForMixPortOrConfig(
+            module.get(), moduleConfig.get(), *offloadMixPorts.begin(), connectedOnly));
+    if (auto reason = stream.skipTestReason(); !reason.empty()) {
+        GTEST_SKIP() << reason;
+    }
+    const auto portConfig = stream.getPortConfig();
     StreamDescriptor descriptor;
-    std::shared_ptr<IStreamOut> ignored;
     aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args;
-    args.portConfigId = portConfig.getId();
-    args.sourceMetadata = GenerateSourceMetadata(portConfig.get());
+    args.portConfigId = portConfig.id;
+    args.sourceMetadata = GenerateSourceMetadata(portConfig);
     args.bufferSizeFrames = kDefaultLargeBufferSizeFrames;
     aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret;
     EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->openOutputStream(args, &ret))
             << "when no offload info is provided for a compressed offload mix port";
+    if (ret.stream != nullptr) {
+        (void)WithStream<IStreamOut>::callClose(ret.stream);
+    }
 }
 
 TEST_P(AudioStreamOut, RequireAsyncCallback) {
+    constexpr bool connectedOnly = true;
     const auto nonBlockingMixPorts =
-            moduleConfig->getNonBlockingMixPorts(true /*connectedOnly*/, true /*singlePort*/);
+            moduleConfig->getNonBlockingMixPorts(connectedOnly, true /*singlePort*/);
     if (nonBlockingMixPorts.empty()) {
         GTEST_SKIP()
                 << "No mix port for non-blocking output that could be routed to attached devices";
     }
-    const auto config =
-            moduleConfig->getSingleConfigForMixPort(false, *nonBlockingMixPorts.begin());
-    ASSERT_TRUE(config.has_value()) << "No profiles specified for the non-blocking mix port";
-    WithAudioPortConfig portConfig(config.value());
-    ASSERT_NO_FATAL_FAILURE(portConfig.SetUp(module.get()));
+    StreamFixture<IStreamOut> stream;
+    ASSERT_NO_FATAL_FAILURE(stream.SetUpPortConfigForMixPortOrConfig(
+            module.get(), moduleConfig.get(), *nonBlockingMixPorts.begin(), connectedOnly));
+    if (auto reason = stream.skipTestReason(); !reason.empty()) {
+        GTEST_SKIP() << reason;
+    }
+    const auto portConfig = stream.getPortConfig();
     StreamDescriptor descriptor;
-    std::shared_ptr<IStreamOut> ignored;
     aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args;
-    args.portConfigId = portConfig.getId();
-    args.sourceMetadata = GenerateSourceMetadata(portConfig.get());
-    args.offloadInfo = ModuleConfig::generateOffloadInfoIfNeeded(portConfig.get());
-    args.bufferSizeFrames = kDefaultBufferSizeFrames;
+    args.portConfigId = portConfig.id;
+    args.sourceMetadata = GenerateSourceMetadata(portConfig);
+    args.offloadInfo = ModuleConfig::generateOffloadInfoIfNeeded(portConfig);
+    args.bufferSizeFrames = stream.getPatch().minimumStreamBufferSizeFrames;
     aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret;
     EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->openOutputStream(args, &ret))
             << "when no async callback is provided for a non-blocking mix port";
+    if (ret.stream != nullptr) {
+        (void)WithStream<IStreamOut>::callClose(ret.stream);
+    }
 }
 
 TEST_P(AudioStreamOut, AudioDescriptionMixLevel) {
-    const auto ports = moduleConfig->getOutputMixPorts(true /*connectedOnly*/);
+    constexpr bool connectedOnly = true;
+    const auto ports = moduleConfig->getOutputMixPorts(connectedOnly);
     if (ports.empty()) {
-        GTEST_SKIP() << "No output mix ports";
+        GTEST_SKIP() << "No output mix ports for attached devices";
     }
-    bool atLeastOneSupports = false;
+    bool atLeastOneSupports = false, atLeastOnePort = false;
     for (const auto& port : ports) {
-        const auto portConfig = moduleConfig->getSingleConfigForMixPort(false, port);
-        ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for output mix port";
-        WithStream<IStreamOut> stream(portConfig.value());
-        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+        SCOPED_TRACE(port.toString());
+        StreamFixture<IStreamOut> stream;
+        ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForMixPort(module.get(), moduleConfig.get(), port,
+                                                             connectedOnly));
+        if (!stream.skipTestReason().empty()) continue;
+        atLeastOnePort = true;
         bool isSupported = false;
         EXPECT_NO_FATAL_FAILURE(
-                TestAccessors<float>(stream.get(), &IStreamOut::getAudioDescriptionMixLevel,
+                TestAccessors<float>(stream.getStream(), &IStreamOut::getAudioDescriptionMixLevel,
                                      &IStreamOut::setAudioDescriptionMixLevel,
                                      {IStreamOut::AUDIO_DESCRIPTION_MIX_LEVEL_MAX,
                                       IStreamOut::AUDIO_DESCRIPTION_MIX_LEVEL_MAX - 1, 0,
@@ -3077,48 +3444,60 @@
                                      &isSupported));
         if (isSupported) atLeastOneSupports = true;
     }
+    if (!atLeastOnePort) {
+        GTEST_SKIP() << "No output mix ports could be routed to devices";
+    }
     if (!atLeastOneSupports) {
         GTEST_SKIP() << "Audio description mix level is not supported";
     }
 }
 
 TEST_P(AudioStreamOut, DualMonoMode) {
-    const auto ports = moduleConfig->getOutputMixPorts(true /*connectedOnly*/);
+    constexpr bool connectedOnly = true;
+    const auto ports = moduleConfig->getOutputMixPorts(connectedOnly);
     if (ports.empty()) {
-        GTEST_SKIP() << "No output mix ports";
+        GTEST_SKIP() << "No output mix ports for attached devices";
     }
-    bool atLeastOneSupports = false;
+    bool atLeastOneSupports = false, atLeastOnePort = false;
     for (const auto& port : ports) {
-        const auto portConfig = moduleConfig->getSingleConfigForMixPort(false, port);
-        ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for output mix port";
-        WithStream<IStreamOut> stream(portConfig.value());
-        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+        SCOPED_TRACE(port.toString());
+        StreamFixture<IStreamOut> stream;
+        ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForMixPort(module.get(), moduleConfig.get(), port,
+                                                             connectedOnly));
+        if (!stream.skipTestReason().empty()) continue;
+        atLeastOnePort = true;
         bool isSupported = false;
         EXPECT_NO_FATAL_FAILURE(TestAccessors<AudioDualMonoMode>(
-                stream.get(), &IStreamOut::getDualMonoMode, &IStreamOut::setDualMonoMode,
+                stream.getStream(), &IStreamOut::getDualMonoMode, &IStreamOut::setDualMonoMode,
                 std::vector<AudioDualMonoMode>(enum_range<AudioDualMonoMode>().begin(),
                                                enum_range<AudioDualMonoMode>().end()),
                 {}, &isSupported));
         if (isSupported) atLeastOneSupports = true;
     }
+    if (!atLeastOnePort) {
+        GTEST_SKIP() << "No output mix ports could be routed to devices";
+    }
     if (!atLeastOneSupports) {
         GTEST_SKIP() << "Audio dual mono mode is not supported";
     }
 }
 
 TEST_P(AudioStreamOut, LatencyMode) {
-    const auto ports = moduleConfig->getOutputMixPorts(true /*connectedOnly*/);
+    constexpr bool connectedOnly = true;
+    const auto ports = moduleConfig->getOutputMixPorts(connectedOnly);
     if (ports.empty()) {
-        GTEST_SKIP() << "No output mix ports";
+        GTEST_SKIP() << "No output mix ports for attached devices";
     }
-    bool atLeastOneSupports = false;
+    bool atLeastOneSupports = false, atLeastOnePort = false;
     for (const auto& port : ports) {
-        const auto portConfig = moduleConfig->getSingleConfigForMixPort(false, port);
-        ASSERT_TRUE(portConfig.has_value()) << "No profiles specified for output mix port";
-        WithStream<IStreamOut> stream(portConfig.value());
-        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+        SCOPED_TRACE(port.toString());
+        StreamFixture<IStreamOut> stream;
+        ASSERT_NO_FATAL_FAILURE(stream.SetUpStreamForMixPort(module.get(), moduleConfig.get(), port,
+                                                             connectedOnly));
+        if (!stream.skipTestReason().empty()) continue;
+        atLeastOnePort = true;
         std::vector<AudioLatencyMode> supportedModes;
-        ndk::ScopedAStatus status = stream.get()->getRecommendedLatencyModes(&supportedModes);
+        ndk::ScopedAStatus status = stream.getStream()->getRecommendedLatencyModes(&supportedModes);
         if (status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) continue;
         atLeastOneSupports = true;
         if (!status.isOk()) {
@@ -3130,7 +3509,7 @@
                                                     enum_range<AudioLatencyMode>().end());
         for (const auto mode : supportedModes) {
             unsupportedModes.erase(mode);
-            ndk::ScopedAStatus status = stream.get()->setLatencyMode(mode);
+            ndk::ScopedAStatus status = stream.getStream()->setLatencyMode(mode);
             if (status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
                 ADD_FAILURE() << "When latency modes are supported, both getRecommendedLatencyModes"
                               << " and setLatencyMode must be supported";
@@ -3138,12 +3517,15 @@
             EXPECT_IS_OK(status) << "Setting of supported latency mode must succeed";
         }
         for (const auto mode : unsupportedModes) {
-            EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, stream.get()->setLatencyMode(mode));
+            EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, stream.getStream()->setLatencyMode(mode));
         }
     }
     if (!atLeastOneSupports) {
         GTEST_SKIP() << "Audio latency modes are not supported";
     }
+    if (!atLeastOnePort) {
+        GTEST_SKIP() << "No output mix ports could be routed to devices";
+    }
 }
 
 TEST_P(AudioStreamOut, PlaybackRate) {
@@ -3445,29 +3827,22 @@
         }
     }
 
-    bool ValidateObservablePosition(const AudioPortConfig& devicePortConfig) {
-        return !isTelephonyDeviceType(
-                devicePortConfig.ext.get<AudioPortExt::Tag::device>().device.type.type);
+    bool ValidateObservablePosition(const AudioDevice& device) {
+        return !isTelephonyDeviceType(device.type.type);
     }
 
     // Set up a patch first, then open a stream.
     void RunStreamIoCommandsImplSeq1(const AudioPortConfig& portConfig,
                                      std::shared_ptr<StateSequence> commandsAndStates,
                                      bool validatePositionIncrease) {
-        auto devicePorts = moduleConfig->getConnectedDevicesPortsForMixPort(
-                IOTraits<Stream>::is_input, portConfig);
-        ASSERT_FALSE(devicePorts.empty());
-        auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(devicePorts[0]);
-        SCOPED_TRACE(devicePortConfig.toString());
-        WithAudioPatch patch(IOTraits<Stream>::is_input, portConfig, devicePortConfig);
-        ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
-
-        WithStream<Stream> stream(patch.getPortConfig(IOTraits<Stream>::is_input));
-        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+        StreamFixture<Stream> stream;
+        ASSERT_NO_FATAL_FAILURE(
+                stream.SetUpStreamForMixPortConfig(module.get(), moduleConfig.get(), portConfig));
+        ASSERT_EQ("", stream.skipTestReason());
         StreamLogicDefaultDriver driver(commandsAndStates,
-                                        stream.getContext()->getFrameSizeBytes());
-        typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver,
-                                                 stream.getEventReceiver());
+                                        stream.getStreamContext()->getFrameSizeBytes());
+        typename IOTraits<Stream>::Worker worker(*stream.getStreamContext(), &driver,
+                                                 stream.getStreamEventReceiver());
 
         LOG(DEBUG) << __func__ << ": starting worker...";
         ASSERT_TRUE(worker.start());
@@ -3475,7 +3850,7 @@
         worker.join();
         EXPECT_FALSE(worker.hasError()) << worker.getError();
         EXPECT_EQ("", driver.getUnexpectedStateTransition());
-        if (ValidateObservablePosition(devicePortConfig)) {
+        if (ValidateObservablePosition(stream.getDevice())) {
             if (validatePositionIncrease) {
                 EXPECT_TRUE(driver.hasObservablePositionIncrease());
             }
@@ -3483,24 +3858,21 @@
         }
     }
 
-    // Open a stream, then set up a patch for it.
+    // Open a stream, then set up a patch for it. Since first it is needed to get
+    // the minimum buffer size, a preliminary patch is set up, then removed.
     void RunStreamIoCommandsImplSeq2(const AudioPortConfig& portConfig,
                                      std::shared_ptr<StateSequence> commandsAndStates,
                                      bool validatePositionIncrease) {
-        WithStream<Stream> stream(portConfig);
-        ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+        StreamFixture<Stream> stream;
+        ASSERT_NO_FATAL_FAILURE(
+                stream.SetUpPatchForMixPortConfig(module.get(), moduleConfig.get(), portConfig));
+        ASSERT_EQ("", stream.skipTestReason());
+        ASSERT_NO_FATAL_FAILURE(stream.TeardownPatchSetUpStream(module.get()));
         StreamLogicDefaultDriver driver(commandsAndStates,
-                                        stream.getContext()->getFrameSizeBytes());
-        typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver,
-                                                 stream.getEventReceiver());
-
-        auto devicePorts = moduleConfig->getConnectedDevicesPortsForMixPort(
-                IOTraits<Stream>::is_input, portConfig);
-        ASSERT_FALSE(devicePorts.empty());
-        auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(devicePorts[0]);
-        SCOPED_TRACE(devicePortConfig.toString());
-        WithAudioPatch patch(IOTraits<Stream>::is_input, stream.getPortConfig(), devicePortConfig);
-        ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
+                                        stream.getStreamContext()->getFrameSizeBytes());
+        typename IOTraits<Stream>::Worker worker(*stream.getStreamContext(), &driver,
+                                                 stream.getStreamEventReceiver());
+        ASSERT_NO_FATAL_FAILURE(stream.ReconnectPatch(module.get()));
 
         LOG(DEBUG) << __func__ << ": starting worker...";
         ASSERT_TRUE(worker.start());
@@ -3508,7 +3880,7 @@
         worker.join();
         EXPECT_FALSE(worker.hasError()) << worker.getError();
         EXPECT_EQ("", driver.getUnexpectedStateTransition());
-        if (ValidateObservablePosition(devicePortConfig)) {
+        if (ValidateObservablePosition(stream.getDevice())) {
             if (validatePositionIncrease) {
                 EXPECT_TRUE(driver.hasObservablePositionIncrease());
             }
@@ -3630,9 +4002,12 @@
                     patches.push_back(
                             std::make_unique<WithAudioPatch>(srcSink.first, srcSink.second));
                     EXPECT_NO_FATAL_FAILURE(patches[patches.size() - 1]->SetUp(module.get()));
+                    EXPECT_NO_FATAL_FAILURE(
+                            patches[patches.size() - 1]->VerifyAgainstAllPatches(module.get()));
                 } else {
                     WithAudioPatch patch(srcSink.first, srcSink.second);
                     EXPECT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
+                    EXPECT_NO_FATAL_FAILURE(patch.VerifyAgainstAllPatches(module.get()));
                 }
             }
         }
@@ -3653,6 +4028,29 @@
         }
     }
 
+    void UpdatePatchPorts(bool isInput) {
+        auto srcSinkGroups = moduleConfig->getRoutableSrcSinkGroups(isInput);
+        if (srcSinkGroups.empty()) {
+            GTEST_SKIP() << "No routes to any attached " << direction(isInput, false) << " devices";
+        }
+        bool hasAtLeastOnePair = false;
+        for (const auto& srcSinkGroup : srcSinkGroups) {
+            const auto& srcSinks = srcSinkGroup.second;
+            if (srcSinks.size() < 2) continue;
+            hasAtLeastOnePair = true;
+            const auto& pair1 = srcSinks[0];
+            const auto& pair2 = srcSinks[1];
+            WithAudioPatch patch(pair1.first, pair1.second);
+            ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
+            WithAudioPatch update(patch, pair2.first, pair2.second);
+            EXPECT_NO_FATAL_FAILURE(update.SetUp(module.get()));
+            EXPECT_NO_FATAL_FAILURE(update.VerifyAgainstAllPatches(module.get()));
+        }
+        if (!hasAtLeastOnePair) {
+            GTEST_SKIP() << "No routes with multiple sources";
+        }
+    }
+
     void UpdateInvalidPatchId(bool isInput) {
         auto srcSinkGroups = moduleConfig->getRoutableSrcSinkGroups(isInput);
         if (srcSinkGroups.empty()) {
@@ -3692,6 +4090,7 @@
 TEST_PATCH_BOTH_DIRECTIONS(SetPatch);
 TEST_PATCH_BOTH_DIRECTIONS(UpdateInvalidPatchId);
 TEST_PATCH_BOTH_DIRECTIONS(UpdatePatch);
+TEST_PATCH_BOTH_DIRECTIONS(UpdatePatchPorts);
 
 TEST_P(AudioModulePatch, ResetInvalidPatchId) {
     std::set<int32_t> patchIds;
@@ -4172,174 +4571,164 @@
 class WithRemoteSubmix {
   public:
     WithRemoteSubmix() = default;
-    WithRemoteSubmix(AudioDeviceAddress address) : mAddress(address) {}
+    explicit WithRemoteSubmix(AudioDeviceAddress address) : mAddress(address) {}
     WithRemoteSubmix(const WithRemoteSubmix&) = delete;
     WithRemoteSubmix& operator=(const WithRemoteSubmix&) = delete;
-    std::optional<AudioPort> getAudioPort() {
-        AudioDeviceType deviceType = IOTraits<Stream>::is_input ? AudioDeviceType::IN_SUBMIX
-                                                                : AudioDeviceType::OUT_SUBMIX;
-        auto ports = mModuleConfig->getAudioPortsForDeviceTypes(
-                std::vector<AudioDeviceType>{deviceType},
-                AudioDeviceDescription::CONNECTION_VIRTUAL);
-        if (!ports.empty()) return ports.front();
-        return {};
-    }
-    /* Connect remote submix external device */
-    void SetUpPortConnection() {
-        auto port = getAudioPort();
-        ASSERT_TRUE(port.has_value()) << "Device AudioPort for remote submix not found";
-        if (mAddress.has_value()) {
-            port.value().ext.template get<AudioPortExt::Tag::device>().device.address =
-                    mAddress.value();
-        } else {
-            port = GenerateUniqueDeviceAddress(port.value());
-        }
-        mConnectedPort = std::make_unique<WithDevicePortConnectedState>(port.value());
-        ASSERT_NO_FATAL_FAILURE(mConnectedPort->SetUp(mModule, mModuleConfig));
-    }
-    AudioDeviceAddress getAudioDeviceAddress() {
-        if (!mAddress.has_value()) {
-            mAddress = mConnectedPort->get()
-                               .ext.template get<AudioPortExt::Tag::device>()
-                               .device.address;
-        }
-        return mAddress.value();
-    }
-    /* Get mix port config for stream and setup patch for it. */
-    void SetupPatch() {
-        const auto portConfig =
-                mModuleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
-        if (!portConfig.has_value()) {
-            LOG(DEBUG) << __func__ << ": portConfig not found";
-            mSkipTest = true;
-            return;
-        }
-        auto devicePortConfig = mModuleConfig->getSingleConfigForDevicePort(mConnectedPort->get());
-        mPatch = std::make_unique<WithAudioPatch>(IOTraits<Stream>::is_input, portConfig.value(),
-                                                  devicePortConfig);
-        ASSERT_NO_FATAL_FAILURE(mPatch->SetUp(mModule));
-    }
-    void SetUp(IModule* module, ModuleConfig* moduleConfig) {
-        mModule = module;
-        mModuleConfig = moduleConfig;
-        ASSERT_NO_FATAL_FAILURE(SetUpPortConnection());
-        ASSERT_NO_FATAL_FAILURE(SetupPatch());
-        if (!mSkipTest) {
-            // open stream
-            mStream = std::make_unique<WithStream<Stream>>(
-                    mPatch->getPortConfig(IOTraits<Stream>::is_input));
-            ASSERT_NO_FATAL_FAILURE(
-                    mStream->SetUp(mModule, AudioCoreModuleBase::kDefaultBufferSizeFrames));
-        }
-    }
-    void sendBurstCommands() {
-        const StreamContext* context = mStream->getContext();
-        StreamLogicDefaultDriver driver(makeBurstCommands(true), context->getFrameSizeBytes());
-        typename IOTraits<Stream>::Worker worker(*context, &driver, mStream->getEventReceiver());
 
-        LOG(DEBUG) << __func__ << ": starting worker...";
-        ASSERT_TRUE(worker.start());
-        LOG(DEBUG) << __func__ << ": joining worker...";
-        worker.join();
-        EXPECT_FALSE(worker.hasError()) << worker.getError();
-        EXPECT_EQ("", driver.getUnexpectedStateTransition());
-        if (IOTraits<Stream>::is_input) {
-            EXPECT_TRUE(driver.hasObservablePositionIncrease());
+    static std::optional<AudioPort> getRemoteSubmixAudioPort(
+            ModuleConfig* moduleConfig,
+            const std::optional<AudioDeviceAddress>& address = std::nullopt) {
+        auto ports =
+                moduleConfig->getRemoteSubmixPorts(IOTraits<Stream>::is_input, true /*singlePort*/);
+        if (ports.empty()) return {};
+        AudioPort port = ports.front();
+        if (address) {
+            port.ext.template get<AudioPortExt::Tag::device>().device.address = address.value();
         }
-        EXPECT_FALSE(driver.hasRetrogradeObservablePosition());
+        return port;
     }
-    bool skipTest() { return mSkipTest; }
+
+    void SetUp(IModule* module, ModuleConfig* moduleConfig) {
+        auto devicePort = getRemoteSubmixAudioPort(moduleConfig, mAddress);
+        ASSERT_TRUE(devicePort.has_value()) << "Device port for remote submix device not found";
+        ASSERT_NO_FATAL_FAILURE(SetUp(module, moduleConfig, *devicePort));
+    }
+
+    void SendBurstCommandsStartWorker() {
+        const StreamContext* context = mStream->getStreamContext();
+        mWorkerDriver = std::make_unique<StreamLogicDefaultDriver>(makeBurstCommands(true),
+                                                                   context->getFrameSizeBytes());
+        mWorker = std::make_unique<typename IOTraits<Stream>::Worker>(
+                *context, mWorkerDriver.get(), mStream->getStreamEventReceiver());
+        LOG(DEBUG) << __func__ << ": starting " << IOTraits<Stream>::directionStr << " worker...";
+        ASSERT_TRUE(mWorker->start());
+    }
+
+    void SendBurstCommandsJoinWorker() {
+        // Must call 'prepareToClose' before attempting to join because the stream may be
+        // stuck due to absence of activity from the other side of the remote submix pipe.
+        std::shared_ptr<IStreamCommon> common;
+        ASSERT_IS_OK(mStream->getStream()->getStreamCommon(&common));
+        ASSERT_IS_OK(common->prepareToClose());
+        LOG(DEBUG) << __func__ << ": joining " << IOTraits<Stream>::directionStr << " worker...";
+        mWorker->join();
+        EXPECT_FALSE(mWorker->hasError()) << mWorker->getError();
+        EXPECT_EQ("", mWorkerDriver->getUnexpectedStateTransition());
+        if (IOTraits<Stream>::is_input) {
+            EXPECT_TRUE(mWorkerDriver->hasObservablePositionIncrease());
+        }
+        EXPECT_FALSE(mWorkerDriver->hasRetrogradeObservablePosition());
+        mWorker.reset();
+        mWorkerDriver.reset();
+    }
+
+    void SendBurstCommands() {
+        ASSERT_NO_FATAL_FAILURE(SendBurstCommandsStartWorker());
+        ASSERT_NO_FATAL_FAILURE(SendBurstCommandsJoinWorker());
+    }
+
+    std::optional<AudioDeviceAddress> getAudioDeviceAddress() const { return mAddress; }
+    std::string skipTestReason() const { return mStream->skipTestReason(); }
 
   private:
-    bool mSkipTest = false;
-    IModule* mModule = nullptr;
-    ModuleConfig* mModuleConfig = nullptr;
+    void SetUp(IModule* module, ModuleConfig* moduleConfig, const AudioPort& devicePort) {
+        mStream = std::make_unique<StreamFixture<Stream>>();
+        ASSERT_NO_FATAL_FAILURE(
+                mStream->SetUpStreamForDevicePort(module, moduleConfig, devicePort));
+        mAddress = mStream->getDevice().address;
+    }
+
     std::optional<AudioDeviceAddress> mAddress;
-    std::unique_ptr<WithDevicePortConnectedState> mConnectedPort;
-    std::unique_ptr<WithAudioPatch> mPatch;
-    std::unique_ptr<WithStream<Stream>> mStream;
+    std::unique_ptr<StreamFixture<Stream>> mStream;
+    std::unique_ptr<StreamLogicDefaultDriver> mWorkerDriver;
+    std::unique_ptr<typename IOTraits<Stream>::Worker> mWorker;
 };
 
 class AudioModuleRemoteSubmix : public AudioCoreModule {
   public:
     void SetUp() override {
-        ASSERT_NO_FATAL_FAILURE(AudioCoreModule::SetUp());
+        // Turn off "debug" which enables connections simulation. Since devices of the remote
+        // submix module are virtual, there is no need for simulation.
+        ASSERT_NO_FATAL_FAILURE(SetUpImpl(GetParam(), false /*setUpDebug*/));
         ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
     }
-
-    void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownImpl()); }
 };
 
 TEST_P(AudioModuleRemoteSubmix, OutputDoesNotBlockWhenNoInput) {
-    // open output stream
     WithRemoteSubmix<IStreamOut> streamOut;
     ASSERT_NO_FATAL_FAILURE(streamOut.SetUp(module.get(), moduleConfig.get()));
-    if (streamOut.skipTest()) {
-        GTEST_SKIP() << "No mix port for attached devices";
-    }
-    // write something to stream
-    ASSERT_NO_FATAL_FAILURE(streamOut.sendBurstCommands());
+    // Note: here and in other tests any issue with connection attempts is considered as a problem.
+    ASSERT_EQ("", streamOut.skipTestReason());
+    ASSERT_NO_FATAL_FAILURE(streamOut.SendBurstCommands());
 }
 
 TEST_P(AudioModuleRemoteSubmix, OutputDoesNotBlockWhenInputStuck) {
-    // open output stream
     WithRemoteSubmix<IStreamOut> streamOut;
     ASSERT_NO_FATAL_FAILURE(streamOut.SetUp(module.get(), moduleConfig.get()));
-    if (streamOut.skipTest()) {
-        GTEST_SKIP() << "No mix port for attached devices";
-    }
+    ASSERT_EQ("", streamOut.skipTestReason());
+    auto address = streamOut.getAudioDeviceAddress();
+    ASSERT_TRUE(address.has_value());
 
-    // open input stream
-    WithRemoteSubmix<IStreamIn> streamIn(streamOut.getAudioDeviceAddress());
+    WithRemoteSubmix<IStreamIn> streamIn(address.value());
     ASSERT_NO_FATAL_FAILURE(streamIn.SetUp(module.get(), moduleConfig.get()));
-    if (streamIn.skipTest()) {
-        GTEST_SKIP() << "No mix port for attached devices";
-    }
+    ASSERT_EQ("", streamIn.skipTestReason());
 
-    // write something to stream
-    ASSERT_NO_FATAL_FAILURE(streamOut.sendBurstCommands());
+    ASSERT_NO_FATAL_FAILURE(streamOut.SendBurstCommands());
 }
 
 TEST_P(AudioModuleRemoteSubmix, OutputAndInput) {
-    // open output stream
     WithRemoteSubmix<IStreamOut> streamOut;
     ASSERT_NO_FATAL_FAILURE(streamOut.SetUp(module.get(), moduleConfig.get()));
-    if (streamOut.skipTest()) {
-        GTEST_SKIP() << "No mix port for attached devices";
-    }
+    ASSERT_EQ("", streamOut.skipTestReason());
+    auto address = streamOut.getAudioDeviceAddress();
+    ASSERT_TRUE(address.has_value());
 
-    // open input stream
-    WithRemoteSubmix<IStreamIn> streamIn(streamOut.getAudioDeviceAddress());
+    WithRemoteSubmix<IStreamIn> streamIn(address.value());
     ASSERT_NO_FATAL_FAILURE(streamIn.SetUp(module.get(), moduleConfig.get()));
-    if (streamIn.skipTest()) {
-        GTEST_SKIP() << "No mix port for attached devices";
-    }
+    ASSERT_EQ("", streamIn.skipTestReason());
 
-    // write something to stream
-    ASSERT_NO_FATAL_FAILURE(streamOut.sendBurstCommands());
-    // read from input stream
-    ASSERT_NO_FATAL_FAILURE(streamIn.sendBurstCommands());
+    // Start writing into the output stream.
+    ASSERT_NO_FATAL_FAILURE(streamOut.SendBurstCommandsStartWorker());
+    // Simultaneously, read from the input stream.
+    ASSERT_NO_FATAL_FAILURE(streamIn.SendBurstCommands());
+    ASSERT_NO_FATAL_FAILURE(streamOut.SendBurstCommandsJoinWorker());
+}
+
+TEST_P(AudioModuleRemoteSubmix, OpenInputMultipleTimes) {
+    WithRemoteSubmix<IStreamOut> streamOut;
+    ASSERT_NO_FATAL_FAILURE(streamOut.SetUp(module.get(), moduleConfig.get()));
+    ASSERT_EQ("", streamOut.skipTestReason());
+    auto address = streamOut.getAudioDeviceAddress();
+    ASSERT_TRUE(address.has_value());
+
+    const size_t streamInCount = 3;
+    std::vector<std::unique_ptr<WithRemoteSubmix<IStreamIn>>> streamIns(streamInCount);
+    for (size_t i = 0; i < streamInCount; i++) {
+        streamIns[i] = std::make_unique<WithRemoteSubmix<IStreamIn>>(address.value());
+        ASSERT_NO_FATAL_FAILURE(streamIns[i]->SetUp(module.get(), moduleConfig.get()));
+        ASSERT_EQ("", streamIns[i]->skipTestReason());
+    }
+    // Start writing into the output stream.
+    ASSERT_NO_FATAL_FAILURE(streamOut.SendBurstCommandsStartWorker());
+    // Simultaneously, read from input streams.
+    for (size_t i = 0; i < streamInCount; i++) {
+        ASSERT_NO_FATAL_FAILURE(streamIns[i]->SendBurstCommandsStartWorker());
+    }
+    for (size_t i = 0; i < streamInCount; i++) {
+        ASSERT_NO_FATAL_FAILURE(streamIns[i]->SendBurstCommandsJoinWorker());
+    }
+    ASSERT_NO_FATAL_FAILURE(streamOut.SendBurstCommandsJoinWorker());
+    // Clean up input streams in the reverse order because the device connection is owned
+    // by the first one.
+    for (size_t i = streamInCount; i != 0; --i) {
+        streamIns[i - 1].reset();
+    }
 }
 
 INSTANTIATE_TEST_SUITE_P(AudioModuleRemoteSubmixTest, AudioModuleRemoteSubmix,
                          ::testing::ValuesIn(getRemoteSubmixModuleInstance()));
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioModuleRemoteSubmix);
 
-class TestExecutionTracer : public ::testing::EmptyTestEventListener {
-  public:
-    void OnTestStart(const ::testing::TestInfo& test_info) override {
-        TraceTestState("Started", test_info);
-    }
-    void OnTestEnd(const ::testing::TestInfo& test_info) override {
-        TraceTestState("Completed", test_info);
-    }
-
-  private:
-    static void TraceTestState(const std::string& state, const ::testing::TestInfo& test_info) {
-        LOG(INFO) << state << " " << test_info.test_suite_name() << "::" << test_info.name();
-    }
-};
-
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
     ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
diff --git a/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
index 225640e..523f20d 100644
--- a/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
@@ -52,6 +52,7 @@
 using aidl::android::media::audio::common::AudioSource;
 using aidl::android::media::audio::common::AudioStreamType;
 using aidl::android::media::audio::common::AudioUuid;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
 
 /// Effect factory testing.
 class EffectFactoryTest : public testing::TestWithParam<std::string> {
@@ -303,6 +304,7 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
+    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
     ABinderProcess_setThreadPoolMaxThreadCount(1);
     ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
index 1876756..ca1cea9 100644
--- a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
@@ -51,6 +51,7 @@
 using aidl::android::media::audio::common::AudioDeviceType;
 using aidl::android::media::audio::common::AudioMode;
 using aidl::android::media::audio::common::AudioSource;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
 
 enum ParamName { PARAM_INSTANCE_NAME };
 using EffectTestParam = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>>;
@@ -907,6 +908,7 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
+    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
     ABinderProcess_setThreadPoolMaxThreadCount(1);
     ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp b/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp
index afddb84..2d9a233 100644
--- a/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalBassBoostTargetTest.cpp
@@ -32,7 +32,7 @@
 using aidl::android::hardware::audio::effect::IFactory;
 using aidl::android::hardware::audio::effect::Parameter;
 using aidl::android::hardware::audio::effect::Range;
-
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
 /**
  * Here we focus on specific parameter checking, general IEffect interfaces testing performed in
  * VtsAudioEffectTargetTest.
@@ -155,6 +155,7 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
+    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
     ABinderProcess_setThreadPoolMaxThreadCount(1);
     ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalDownmixTargetTest.cpp b/audio/aidl/vts/VtsHalDownmixTargetTest.cpp
index 7a2f31b..c01a9a2 100644
--- a/audio/aidl/vts/VtsHalDownmixTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalDownmixTargetTest.cpp
@@ -28,7 +28,7 @@
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::IFactory;
 using aidl::android::hardware::audio::effect::Parameter;
-
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
 /**
  * Here we focus on specific parameter checking, general IEffect interfaces testing performed in
  * VtsAudioEffectTargetTest.
@@ -136,6 +136,7 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
+    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
     ABinderProcess_setThreadPoolMaxThreadCount(1);
     ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
index 5509c76..2650f49 100644
--- a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
+++ b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
@@ -36,6 +36,7 @@
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::IFactory;
 using aidl::android::hardware::audio::effect::Parameter;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
 
 /**
  * Here we focus on specific parameter checking, general IEffect interfaces testing performed in
@@ -996,6 +997,7 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
+    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
     ABinderProcess_setThreadPoolMaxThreadCount(1);
     ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp b/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp
index f2ef185..474b361 100644
--- a/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalEnvironmentalReverbTargetTest.cpp
@@ -28,6 +28,7 @@
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::IFactory;
 using aidl::android::hardware::audio::effect::Parameter;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
 
 /**
  * Here we focus on specific parameter checking, general IEffect interfaces testing performed in
@@ -536,6 +537,7 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
+    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
     ABinderProcess_setThreadPoolMaxThreadCount(1);
     ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp b/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp
index 37e7c0a..09396d1 100644
--- a/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp
@@ -46,6 +46,7 @@
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::IFactory;
 using aidl::android::hardware::audio::effect::Parameter;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
 
 /**
  * Here we focus on specific effect (equalizer) parameter checking, general IEffect interfaces
@@ -220,6 +221,7 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
+    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
     ABinderProcess_setThreadPoolMaxThreadCount(1);
     ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp b/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
index b33234b..5a32398 100644
--- a/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
@@ -33,6 +33,7 @@
 using aidl::android::hardware::audio::effect::IEffect;
 using aidl::android::hardware::audio::effect::IFactory;
 using aidl::android::hardware::audio::effect::Parameter;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
 
 /**
  * Here we focus on specific parameter checking, general IEffect interfaces testing performed in
@@ -431,6 +432,7 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
+    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
     ABinderProcess_setThreadPoolMaxThreadCount(1);
     ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp b/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp
index cbb80a9..925f9ec 100644
--- a/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalLoudnessEnhancerTargetTest.cpp
@@ -30,28 +30,21 @@
 using aidl::android::hardware::audio::effect::IFactory;
 using aidl::android::hardware::audio::effect::LoudnessEnhancer;
 using aidl::android::hardware::audio::effect::Parameter;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
 
-/**
- * Here we focus on specific parameter checking, general IEffect interfaces testing performed in
- * VtsAudioEffectTargetTest.
- */
-enum ParamName { PARAM_INSTANCE_NAME, PARAM_GAIN_MB };
-using LoudnessEnhancerParamTestParam =
-        std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int>;
+static constexpr float kMaxAudioSample = 1;
+static constexpr int kZeroGain = 0;
+static constexpr int kMaxGain = std::numeric_limits<int>::max();
+static constexpr int kMinGain = std::numeric_limits<int>::min();
+static constexpr float kAbsError = 0.0001;
 
 // Every int 32 bit value is a valid gain, so testing the corner cases and one regular value.
 // TODO : Update the test values once range/capability is updated by implementation.
-const std::vector<int> kGainMbValues = {std::numeric_limits<int>::min(), 100,
-                                        std::numeric_limits<int>::max()};
+static const std::vector<int> kGainMbValues = {kMinGain, -100, -50, kZeroGain, 50, 100, kMaxGain};
 
-class LoudnessEnhancerParamTest : public ::testing::TestWithParam<LoudnessEnhancerParamTestParam>,
-                                  public EffectHelper {
+class LoudnessEnhancerEffectHelper : public EffectHelper {
   public:
-    LoudnessEnhancerParamTest() : mParamGainMb(std::get<PARAM_GAIN_MB>(GetParam())) {
-        std::tie(mFactory, mDescriptor) = std::get<PARAM_INSTANCE_NAME>(GetParam());
-    }
-
-    void SetUp() override {
+    void SetUpLoudnessEnhancer() {
         ASSERT_NE(nullptr, mFactory);
         ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
 
@@ -59,13 +52,14 @@
         Parameter::Common common = EffectHelper::createParamCommon(
                 0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */,
                 kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */);
-        IEffect::OpenEffectReturn ret;
-        ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &ret, EX_NONE));
+        ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &mOpenEffectReturn, EX_NONE));
         ASSERT_NE(nullptr, mEffect);
     }
-    void TearDown() override {
+
+    void TearDownLoudnessEnhancer() {
         ASSERT_NO_FATAL_FAILURE(close(mEffect));
         ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
+        mOpenEffectReturn = IEffect::OpenEffectReturn{};
     }
 
     Parameter::Specific getDefaultParamSpecific() {
@@ -75,52 +69,230 @@
         return specific;
     }
 
-    static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100;
-    std::shared_ptr<IFactory> mFactory;
-    std::shared_ptr<IEffect> mEffect;
-    Descriptor mDescriptor;
-    int mParamGainMb = 0;
+    Parameter createLoudnessParam(int gainMb) {
+        LoudnessEnhancer le;
+        le.set<LoudnessEnhancer::gainMb>(gainMb);
+        Parameter param;
+        Parameter::Specific specific;
+        specific.set<Parameter::Specific::loudnessEnhancer>(le);
+        param.set<Parameter::specific>(specific);
+        return param;
+    }
 
-    void SetAndGetParameters() {
-        for (auto& it : mTags) {
-            auto& tag = it.first;
-            auto& le = it.second;
-
-            // set parameter
-            Parameter expectParam;
-            Parameter::Specific specific;
-            specific.set<Parameter::Specific::loudnessEnhancer>(le);
-            expectParam.set<Parameter::specific>(specific);
-            // All values are valid, set parameter should succeed
-            EXPECT_STATUS(EX_NONE, mEffect->setParameter(expectParam)) << expectParam.toString();
-
-            // get parameter
-            Parameter getParam;
-            Parameter::Id id;
-            LoudnessEnhancer::Id leId;
-            leId.set<LoudnessEnhancer::Id::commonTag>(tag);
-            id.set<Parameter::Id::loudnessEnhancerTag>(leId);
-            EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam));
-
-            EXPECT_EQ(expectParam, getParam) << "\nexpect:" << expectParam.toString()
-                                             << "\ngetParam:" << getParam.toString();
+    binder_exception_t isGainValid(int gainMb) {
+        LoudnessEnhancer le;
+        le.set<LoudnessEnhancer::gainMb>(gainMb);
+        if (isParameterValid<LoudnessEnhancer, Range::loudnessEnhancer>(le, mDescriptor)) {
+            return EX_NONE;
+        } else {
+            return EX_ILLEGAL_ARGUMENT;
         }
     }
 
-    void addGainMbParam(int gainMb) {
-        LoudnessEnhancer le;
-        le.set<LoudnessEnhancer::gainMb>(gainMb);
-        mTags.push_back({LoudnessEnhancer::gainMb, le});
+    void setParameters(int gain, binder_exception_t expected) {
+        // set parameter
+        auto param = createLoudnessParam(gain);
+        EXPECT_STATUS(expected, mEffect->setParameter(param)) << param.toString();
     }
 
-  private:
-    std::vector<std::pair<LoudnessEnhancer::Tag, LoudnessEnhancer>> mTags;
-    void CleanUp() { mTags.clear(); }
+    void validateParameters(int gain) {
+        // get parameter
+        LoudnessEnhancer::Id leId;
+        Parameter getParam;
+        Parameter::Id id;
+
+        LoudnessEnhancer::Tag tag(LoudnessEnhancer::gainMb);
+        leId.set<LoudnessEnhancer::Id::commonTag>(tag);
+        id.set<Parameter::Id::loudnessEnhancerTag>(leId);
+        EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam));
+        auto expectedParam = createLoudnessParam(gain);
+        EXPECT_EQ(expectedParam, getParam) << "\nexpectedParam:" << expectedParam.toString()
+                                           << "\ngetParam:" << getParam.toString();
+    }
+
+    static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100;
+    IEffect::OpenEffectReturn mOpenEffectReturn;
+    std::shared_ptr<IFactory> mFactory;
+    std::shared_ptr<IEffect> mEffect;
+    Descriptor mDescriptor;
+};
+
+/**
+ * Here we focus on specific parameter checking, general IEffect interfaces testing performed in
+ * VtsAudioEffectTargetTest.
+ */
+enum ParamName { PARAM_INSTANCE_NAME, PARAM_GAIN_MB };
+using LoudnessEnhancerParamTestParam =
+        std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int>;
+
+class LoudnessEnhancerParamTest : public ::testing::TestWithParam<LoudnessEnhancerParamTestParam>,
+                                  public LoudnessEnhancerEffectHelper {
+  public:
+    LoudnessEnhancerParamTest() : mParamGainMb(std::get<PARAM_GAIN_MB>(GetParam())) {
+        std::tie(mFactory, mDescriptor) = std::get<PARAM_INSTANCE_NAME>(GetParam());
+    }
+
+    void SetUp() override { SetUpLoudnessEnhancer(); }
+    void TearDown() override { TearDownLoudnessEnhancer(); }
+    int mParamGainMb = 0;
 };
 
 TEST_P(LoudnessEnhancerParamTest, SetAndGetGainMb) {
-    EXPECT_NO_FATAL_FAILURE(addGainMbParam(mParamGainMb));
-    SetAndGetParameters();
+    binder_exception_t expected = isGainValid(mParamGainMb);
+    setParameters(mParamGainMb, expected);
+    if (expected == EX_NONE) {
+        validateParameters(mParamGainMb);
+    }
+}
+
+using LoudnessEnhancerDataTestParam = std::pair<std::shared_ptr<IFactory>, Descriptor>;
+
+class LoudnessEnhancerDataTest : public ::testing::TestWithParam<LoudnessEnhancerDataTestParam>,
+                                 public LoudnessEnhancerEffectHelper {
+  public:
+    LoudnessEnhancerDataTest() {
+        std::tie(mFactory, mDescriptor) = GetParam();
+        generateInputBuffer();
+        mOutputBuffer.resize(kBufferSize);
+    }
+
+    void SetUp() override {
+        SetUpLoudnessEnhancer();
+
+        // Creating AidlMessageQueues
+        mStatusMQ = std::make_unique<EffectHelper::StatusMQ>(mOpenEffectReturn.statusMQ);
+        mInputMQ = std::make_unique<EffectHelper::DataMQ>(mOpenEffectReturn.inputDataMQ);
+        mOutputMQ = std::make_unique<EffectHelper::DataMQ>(mOpenEffectReturn.outputDataMQ);
+    }
+
+    void TearDown() override { TearDownLoudnessEnhancer(); }
+
+    // Fill inputBuffer with random values between -kMaxAudioSample to kMaxAudioSample
+    void generateInputBuffer() {
+        for (size_t i = 0; i < kBufferSize; i++) {
+            mInputBuffer.push_back(((static_cast<float>(std::rand()) / RAND_MAX) * 2 - 1) *
+                                   kMaxAudioSample);
+        }
+    }
+
+    // Add gains to the mInputBuffer and store processed output to mOutputBuffer
+    void processAndWriteToOutput() {
+        // Check AidlMessageQueues are not null
+        ASSERT_TRUE(mStatusMQ->isValid());
+        ASSERT_TRUE(mInputMQ->isValid());
+        ASSERT_TRUE(mOutputMQ->isValid());
+
+        // Enabling the process
+        ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::START));
+        ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::PROCESSING));
+
+        // Write from buffer to message queues and calling process
+        EXPECT_NO_FATAL_FAILURE(EffectHelper::writeToFmq(mStatusMQ, mInputMQ, mInputBuffer));
+
+        // Read the updated message queues into buffer
+        EXPECT_NO_FATAL_FAILURE(EffectHelper::readFromFmq(mStatusMQ, 1, mOutputMQ,
+                                                          mOutputBuffer.size(), mOutputBuffer));
+
+        // Disable the process
+        ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::STOP));
+    }
+
+    void assertGreaterGain(const std::vector<float>& first, const std::vector<float>& second) {
+        for (size_t i = 0; i < first.size(); i++) {
+            if (first[i] != 0) {
+                ASSERT_GT(abs(first[i]), abs(second[i]));
+
+            } else {
+                ASSERT_EQ(first[i], second[i]);
+            }
+        }
+    }
+
+    void assertSequentialGains(const std::vector<int>& gainValues, bool isIncreasing) {
+        std::vector<float> baseOutput(kBufferSize);
+
+        // Process a reference output buffer with 0 gain which gives compressed input values
+        binder_exception_t expected;
+        expected = isGainValid(kZeroGain);
+        ASSERT_EQ(expected, EX_NONE);
+        setParameters(kZeroGain, expected);
+        ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput());
+        baseOutput = mOutputBuffer;
+
+        // Compare the outputs for increasing gain
+        for (int gain : gainValues) {
+            // Setting the parameters
+            binder_exception_t expected = isGainValid(gain);
+            if (expected != EX_NONE) {
+                GTEST_SKIP() << "Gains not supported.";
+            }
+            setParameters(gain, expected);
+            ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput());
+
+            // Compare the mOutputBuffer values with baseOutput and update it
+            if (isIncreasing) {
+                ASSERT_NO_FATAL_FAILURE(assertGreaterGain(mOutputBuffer, baseOutput));
+            } else {
+                ASSERT_NO_FATAL_FAILURE(assertGreaterGain(baseOutput, mOutputBuffer));
+            }
+
+            baseOutput = mOutputBuffer;
+        }
+    }
+
+    std::unique_ptr<StatusMQ> mStatusMQ;
+    std::unique_ptr<DataMQ> mInputMQ;
+    std::unique_ptr<DataMQ> mOutputMQ;
+
+    std::vector<float> mInputBuffer;
+    std::vector<float> mOutputBuffer;
+    static constexpr float kBufferSize = 128;
+};
+
+TEST_P(LoudnessEnhancerDataTest, IncreasingGains) {
+    static const std::vector<int> kIncreasingGains = {50, 100};
+
+    assertSequentialGains(kIncreasingGains, true /*isIncreasing*/);
+}
+
+TEST_P(LoudnessEnhancerDataTest, DecreasingGains) {
+    static const std::vector<int> kDecreasingGains = {-50, -100};
+
+    assertSequentialGains(kDecreasingGains, false /*isIncreasing*/);
+}
+
+TEST_P(LoudnessEnhancerDataTest, MinimumGain) {
+    // Setting the parameters
+    binder_exception_t expected = isGainValid(kMinGain);
+    if (expected != EX_NONE) {
+        GTEST_SKIP() << "Minimum integer value not supported";
+    }
+    setParameters(kMinGain, expected);
+    ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput());
+
+    // Validate that mOutputBuffer has 0 values for INT_MIN gain
+    for (size_t i = 0; i < mOutputBuffer.size(); i++) {
+        ASSERT_FLOAT_EQ(mOutputBuffer[i], 0);
+    }
+}
+
+TEST_P(LoudnessEnhancerDataTest, MaximumGain) {
+    // Setting the parameters
+    binder_exception_t expected = isGainValid(kMaxGain);
+    if (expected != EX_NONE) {
+        GTEST_SKIP() << "Maximum integer value not supported";
+    }
+    setParameters(kMaxGain, expected);
+    ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput());
+
+    // Validate that mOutputBuffer reaches to kMaxAudioSample for INT_MAX gain
+    for (size_t i = 0; i < mOutputBuffer.size(); i++) {
+        if (mInputBuffer[i] != 0) {
+            EXPECT_NEAR(kMaxAudioSample, abs(mOutputBuffer[i]), kAbsError);
+        } else {
+            ASSERT_EQ(mOutputBuffer[i], mInputBuffer[i]);
+        }
+    }
 }
 
 INSTANTIATE_TEST_SUITE_P(
@@ -139,8 +311,23 @@
 
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(LoudnessEnhancerParamTest);
 
+INSTANTIATE_TEST_SUITE_P(
+        LoudnessEnhancerTest, LoudnessEnhancerDataTest,
+        testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+                IFactory::descriptor, getEffectTypeUuidLoudnessEnhancer())),
+        [](const testing::TestParamInfo<LoudnessEnhancerDataTest::ParamType>& info) {
+            auto descriptor = info.param;
+            std::string name = getPrefix(descriptor.second);
+            std::replace_if(
+                    name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
+            return name;
+        });
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(LoudnessEnhancerDataTest);
+
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
+    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
     ABinderProcess_setThreadPoolMaxThreadCount(1);
     ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalNSTargetTest.cpp b/audio/aidl/vts/VtsHalNSTargetTest.cpp
index 624d5d2..12d56b0 100644
--- a/audio/aidl/vts/VtsHalNSTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalNSTargetTest.cpp
@@ -32,6 +32,7 @@
 using aidl::android::hardware::audio::effect::IFactory;
 using aidl::android::hardware::audio::effect::NoiseSuppression;
 using aidl::android::hardware::audio::effect::Parameter;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
 
 enum ParamName { PARAM_INSTANCE_NAME, PARAM_LEVEL, PARAM_TYPE };
 using NSParamTestParam = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>,
@@ -171,6 +172,7 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
+    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
     ABinderProcess_setThreadPoolMaxThreadCount(1);
     ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp b/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp
index 3056c6c..57eda09 100644
--- a/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalPresetReverbTargetTest.cpp
@@ -29,6 +29,7 @@
 using aidl::android::hardware::audio::effect::IFactory;
 using aidl::android::hardware::audio::effect::Parameter;
 using aidl::android::hardware::audio::effect::PresetReverb;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
 
 /**
  * Here we focus on specific parameter checking, general IEffect interfaces testing performed in
@@ -147,6 +148,7 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
+    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
     ABinderProcess_setThreadPoolMaxThreadCount(1);
     ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp b/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp
index 07a9fa4..3e39d3a 100644
--- a/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalVirtualizerTargetTest.cpp
@@ -28,6 +28,7 @@
 using aidl::android::hardware::audio::effect::IFactory;
 using aidl::android::hardware::audio::effect::Parameter;
 using aidl::android::hardware::audio::effect::Virtualizer;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
 
 /**
  * Here we focus on specific parameter checking, general IEffect interfaces testing performed in
@@ -151,6 +152,7 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
+    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
     ABinderProcess_setThreadPoolMaxThreadCount(1);
     ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp b/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp
index 903ba69..1b8352b 100644
--- a/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalVisualizerTargetTest.cpp
@@ -31,6 +31,7 @@
 using aidl::android::hardware::audio::effect::IFactory;
 using aidl::android::hardware::audio::effect::Parameter;
 using aidl::android::hardware::audio::effect::Visualizer;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
 
 /**
  * Here we focus on specific parameter checking, general IEffect interfaces testing performed in
@@ -207,6 +208,7 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
+    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
     ABinderProcess_setThreadPoolMaxThreadCount(1);
     ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
diff --git a/audio/aidl/vts/VtsHalVolumeTargetTest.cpp b/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
index 0b5b9fc..257100b 100644
--- a/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
@@ -28,6 +28,7 @@
 using aidl::android::hardware::audio::effect::IFactory;
 using aidl::android::hardware::audio::effect::Parameter;
 using aidl::android::hardware::audio::effect::Volume;
+using android::hardware::audio::common::testing::detail::TestExecutionTracer;
 
 /**
  * Here we focus on specific parameter checking, general IEffect interfaces testing performed in
@@ -159,6 +160,7 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
+    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
     ABinderProcess_setThreadPoolMaxThreadCount(1);
     ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
diff --git a/audio/common/all-versions/default/service/android.hardware.audio.service.rc b/audio/common/all-versions/default/service/android.hardware.audio.service.rc
index 0de4eea..a1df67a 100644
--- a/audio/common/all-versions/default/service/android.hardware.audio.service.rc
+++ b/audio/common/all-versions/default/service/android.hardware.audio.service.rc
@@ -2,7 +2,7 @@
     class hal
     user audioserver
     # media gid needed for /dev/fm (radio) and for /data/misc/media (tee)
-    group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct wakelock context_hub
+    group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct wakelock context_hub system
     capabilities BLOCK_SUSPEND SYS_NICE
     # setting RLIMIT_RTPRIO allows binder RT priority inheritance
     rlimit rtprio 10 10
diff --git a/audio/common/all-versions/test/utility/src/ValidateXml.cpp b/audio/common/all-versions/test/utility/src/ValidateXml.cpp
index 4d6f003..b7e685b 100644
--- a/audio/common/all-versions/test/utility/src/ValidateXml.cpp
+++ b/audio/common/all-versions/test/utility/src/ValidateXml.cpp
@@ -19,6 +19,7 @@
 
 #include <numeric>
 
+#include <libxml/parser.h>
 #define LIBXML_SCHEMAS_ENABLED
 #include <libxml/xmlschemastypes.h>
 #define LIBXML_XINCLUDE_ENABLED
diff --git a/audio/common/all-versions/test/utility/tests/utility_tests.cpp b/audio/common/all-versions/test/utility/tests/utility_tests.cpp
index c523066..c2bcfbc 100644
--- a/audio/common/all-versions/test/utility/tests/utility_tests.cpp
+++ b/audio/common/all-versions/test/utility/tests/utility_tests.cpp
@@ -70,6 +70,7 @@
 std::string substitute(const char* fmt, const char* param) {
     std::string buffer(static_cast<size_t>(strlen(fmt) + strlen(param)), '\0');
     snprintf(buffer.data(), buffer.size(), fmt, param);
+    buffer.resize(strlen(buffer.c_str()));
     return buffer;
 }
 
diff --git a/authsecret/aidl/default/Android.bp b/authsecret/aidl/default/Android.bp
index 7e6e48b..91f4fae 100644
--- a/authsecret/aidl/default/Android.bp
+++ b/authsecret/aidl/default/Android.bp
@@ -55,23 +55,12 @@
     installable: false,
 }
 
-apex_key {
-    name: "com.android.hardware.authsecret.key",
-    public_key: "com.android.hardware.authsecret.avbpubkey",
-    private_key: "com.android.hardware.authsecret.pem",
-}
-
-android_app_certificate {
-    name: "com.android.hardware.authsecret.certificate",
-    certificate: "com.android.hardware.authsecret",
-}
-
 apex {
     name: "com.android.hardware.authsecret",
     manifest: "manifest.json",
     file_contexts: "file_contexts",
-    key: "com.android.hardware.authsecret.key",
-    certificate: ":com.android.hardware.authsecret.certificate",
+    key: "com.android.hardware.key",
+    certificate: ":com.android.hardware.certificate",
     updatable: false,
     vendor: true,
 
diff --git a/authsecret/aidl/default/com.android.hardware.authsecret.avbpubkey b/authsecret/aidl/default/com.android.hardware.authsecret.avbpubkey
deleted file mode 100644
index 2fb5f0b..0000000
--- a/authsecret/aidl/default/com.android.hardware.authsecret.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/authsecret/aidl/default/com.android.hardware.authsecret.pem b/authsecret/aidl/default/com.android.hardware.authsecret.pem
deleted file mode 100644
index 644868c..0000000
--- a/authsecret/aidl/default/com.android.hardware.authsecret.pem
+++ /dev/null
@@ -1,52 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQCkkrpV5StewKq6
-HOog0IkbBRAtuChTsQjE1yQY6VXF/f/QWc2L0++pe0PTNzuKJZuqD05tgLYBxsFl
-QSLMKoUnGkQshVHxFLCls0CQo/umgygydxyVNW5cTdDDbl0VHrYcJJSWBobfy/hh
-dizJVET6HMQLY0shKM9CcdFDRqvM+WB6ceBxcFsxwm8r5qcB6CCeIDsPuBBo5Mpa
-NlVbaMBc/qrSRFLqLiVph6Goycg0Zk5+i1A4VBTJoHlOQwgX4uMdlNoAnaf1rLIm
-Qn2zNfcfeZ3BoiwKv8qMsvLNotaN+oIYLi4t21JcPsroByI+Ps5Gia17BhQvbbXx
-3eRShBn6/YcxcMQmCPoN5JbqeyzcE9f0grh3I8ubf1+ZAW0dL2r6QRM6uo6T4Jf7
-BFiMVB0RjTzfo8ngwgPLIm/aXU2O8IG8sILO1s1iOaQb23E2PveF1B7EOmFzW8x3
-xSQQLwn83zY+V+BTZs/Dfgv+M3omctsLS7lgGuvSx9KHV+EnbRMoIYa/2fx5KUxP
-kC3rGpR2BRHZibm3tBlHmdkzOsaHaoQy2fLtqK39Au+cL05O5VpOYObBN2wOlKUO
-iWLS79YoaWAKUZtZKwQNu1jpmK0OMgCeC13QSTz2AuPgL6XEuCUpcmblX3KMsu5w
-MK79Yke/V61cQ71e5qI0vr3IXjcjywIDAQABAoICAAYZ0GGNyNFO6CVVHBLSWDrR
-sbtYJ9qOZgpSBWsM/1qDI4AYTCfiV/Ca+rUyR3lEEqS3w4sIqfaf5Rx5US5rZxs/
-fIZ//L0orLG/1uVlxtbx5sQUKVGYtPokAli0VywIwuyBKKb1H/vc5lzKkjd2ccYp
-2dSoPilBB4npiT3quUS0e/CeFxltdlv+XrusZcWK0ua5wCbBho406RF2ESz90Z/A
-6xk3YjF/O3DRj9sfe9YBcuh7BqLH7ytYURbnIj4scYnvsjMypP7VA5eqgFlr5zjZ
-+9CpT+OoH3yex6R65GRIBJmb4KdfiYqU41W9qfXPwzrXMMCuRYJKmWOZe7TZY9Mc
-V46jaDgLgKxe+mI4CLi2auKFE4KL8x68KSqa22y2dEjWwBPiT7If6v0ZL4CiAy9n
-SNHFaceMY3l485vaZEtXxusRB/UGDZnAXr9NqBVm4YVAfOaEnJNDSqvYefM5iyOG
-yQZ7dCXS9Ey4JvVlceA6mybj2JSx20QS2wN/tcyZjWsjM0f/sIHAJRS6KhEqCIfX
-4L8d5nXJ1wvnBFvcfboSERkPOTQsuipsvn9uj8Zs9QWNYYRSyleptL+ce8fBqed6
-9ryErCuB9lpVTjUsOiaIibtGdePleQb10club1B/4vsgPl5wvTPRNCTmpOCP3pSf
-Rei2x4z1VGFOBwd3MiTtAoIBAQDiQCsK87Zs8E5cwc0CqeMWstWHvJLTkj2B42OI
-Zrbr6ByRixuLpWgVWtJJLKbLXPN83wl8eksv3+Ba+yi17uafhXX7M1O5RlOzvTHt
-bbFPeysB3KEmNt96dRDRKYY3z0KHJxCRWKKZjZjp8Usf3TuKi9Xbque8o2n1LKKB
-KANRC4xtHmUesl6d4S4iAfIkq5/nA4ocuJ2qd/2t3nc6yhPPRrP9+4sUPYdqBEUz
-ds9isqt5erUybstCoHRgsEwWo/9ew8Dyj1TCIDTSqRt1/0QnEVm77bgBrA8P66HI
-KKFVoo/MLQSw5V+CDZwTJNlPQwrG9tyrSkcMFgLSRzi+7d/3AoIBAQC6Nm5Ztiad
-zm/1jM89uHmvedimKCKKG6+Eom5D96PduP76uRr65s6Usn0QCZ4Jvou0xnbtCP1K
-lzVI1+v6EiIQyWX0y1bd0UJDdyYo4Ck2nqOh0vgw+cBO70faV50J5QA2mS/wFdw0
-PvykQpPYGiIfv1MMHWA+rPDzMtf1uUQ18vzzN7RaZll88pletC7mc7qgBaucQVvB
-/qzOZSwhiaSSvXS1sFKfkqLqpJ3x9748D74MIwDD2i3fRxxfqPcgrG3B7xxIKVgd
-CYoFjeC9inZbnwvPmtaKzROJknsuJA21s/ckkSiWJJbjbyymVc1hWNhoMbtSPopa
-OOJ7u695Ls3NAoIBADtRE3fVmXhKMGFFNhiCrdTfoffqSpxJdPK+yPOT6lVDD2ph
-DCG6heVDYGpq2HfssLGGUBhgf6HXkhyISI4aSkB8Xwgy1rp2Y6915McYwSnTYt0k
-GOPJ8yFJ29TajCPJpOmGJmPU1xxm8TY0WrvJ5rhWHQVwcz0Tos3ym9A8y1HOM0zQ
-cTZxETlXNh8YX4GZtVx9oxIQnNV6i/mvn5a8MCFhqgLmlfoCf6Qd5n6toYWAzlAV
-CbhlL8kSBDDtR6WP7X3M2KM/TLtwcijgySBQgm+zrtEEa/+UOoa0AkBV1qZ67jRb
-gSVXnYidRNQIDykmrIapZgVKfgH/K1Ix9gCooNUCggEAeSMzwnS2xm4nc2w43YQG
-1VrEz8LIRWQhWH16kgilt3XDmkOVA6fmt+EtbqNzBg/JPr7lWupALKgVZ9/fiX0G
-YDlEdG1bg03Ad7cpQeohpYCqHnnqL6IpsrAC5E2ewXMSInKhNuRhrjNTk2AkYa8O
-h+ylD/qERAGpdeybhSUS9K2wVGDmmPCAQsJnd65r3EtpGvTVYP87vAX7UQGMJf0u
-7K8HH7Mm7Nwt08tnXKO4Q8ZR8f9LXh2vPdM66Bg5PC4v8LumgGM1CR7NhTN5ApTy
-zkO3IUUvUHh8v0BlleyqZow+uLEd4B7Jcgc+2q5yv2NW1OGVZLl+s5bR74B3dLQ3
-+QKCAQBtxqTUKaRE/g+paQByORt0mFuXhO5UyfEBpg6l7+aUS4wTGMeKKDCFjTME
-lDjEK7eiAAOgN3AJYQ+r6831pm3hg6DG5lpxTKUuz2eMqMHk3LVlDFkH6BZF3Jxg
-XxWP1Abi88hK3nnbuGrt6rlUxvJRdWaJcF5nXybJzPMpvzPcDjAg5fCT11vZQsRl
-piAO6NjAECwUOaBHSHPvIXO9fWu4zY03rhg9px+dkutydSJ/6B3jq33q1CzHDQMd
-bklkBBrLu9inpuvETbhVK6IWP2zMHzdViR58M+xd5rg2E3GBQePyd6mjOB+7p7Gd
-hUHo4XX1/r2CDceZTmOjaZP/MQOJ
------END PRIVATE KEY-----
diff --git a/authsecret/aidl/default/com.android.hardware.authsecret.pk8 b/authsecret/aidl/default/com.android.hardware.authsecret.pk8
deleted file mode 100644
index 1453366..0000000
--- a/authsecret/aidl/default/com.android.hardware.authsecret.pk8
+++ /dev/null
Binary files differ
diff --git a/authsecret/aidl/default/com.android.hardware.authsecret.x509.pem b/authsecret/aidl/default/com.android.hardware.authsecret.x509.pem
deleted file mode 100644
index 71fe854..0000000
--- a/authsecret/aidl/default/com.android.hardware.authsecret.x509.pem
+++ /dev/null
@@ -1,34 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIF4zCCA8sCFH8r8uUt7ZiBGNZm/OxNbzMI34N3MA0GCSqGSIb3DQEBCwUAMIGs
-MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
-bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEi
-MCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTEoMCYGA1UEAwwfY29t
-LmFuZHJvaWQuaGFyZHdhcmUuYXV0aHNlY3JldDAgFw0yMzA4MTgwNzA3MDFaGA80
-NzYxMDcxNDA3MDcwMVowgawxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9y
-bmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAw
-DgYDVQQLDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQu
-Y29tMSgwJgYDVQQDDB9jb20uYW5kcm9pZC5oYXJkd2FyZS5hdXRoc2VjcmV0MIIC
-IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1+ml99ip2TVpYdJXxqVnizLg
-/DAnOUy5rZE+mCpjua1zyHl7GFFnseq6GO5wftptWekcC9fVSPxg1YS+RVduRcNz
-rt3mCsJ60DexaHrElITc1GCC1vzyt9cg6UtmdYg+OXSPlfWZE2T7OLfGWrhU56El
-IFt1eQDu5RDBOHZ2/N30KmKXv3yhpdl5un/kaC6q6p1PPih0aYXT2PrHsyN17wBl
-smhhpWNg/OAzFhWKwFcSTLMCOAI+pjqWKqXmjQ1awBly+lLAtHEBxxEUDMD6Z4lv
-2OGftL9bdseb1Wbj2bgj22bkfwWj5Yu77ilz5H27aLxQouQsmSfBkVgLq6seQMzd
-ovg+MOFb9iyWgcWkGg7eKdSzjfzCKQ/d2GOxEqwGofEYUOxYM1+a0Fnb7vUtgT/v
-I4tJCgXxAMFtsYGySC+gBhNx7vqLfo/8gtcZjJL6PRtRJ2V7hF9x3xKFa2/6qOLn
-UD/R5z+uwzaEqom+AmmmKNdWtn58nBns7EGq/3KVPcb7CSi9Xf6CCJiV+AmRaBSx
-2CtLt1fBX46LM3bV+iw7ZFAYAAXE3j6FgpM/PlozQjl61TuomHQP1ILmu/988tiF
-FMbuRH6mI0u4VOkDBUg9lxmjr0uAVmysWmzkRrAKydzedsuG5WG6hy2ZcD+lCV05
-JzvE6pB65OaIEPB5cMECAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAozFo1vOIy/Js
-/EU5oRtSGSIoWCNIn7EYQ68jVHQk2ZhklVH2jW+6WVGmreOjPz5iWeDb0fC4KVtH
-MPXJ1Vd+GfvDysrnW5mDBzlI1O2xv/BWYeHt+RFWghhH/NFHaTQqDJpdOZXMZM4F
-dwFefxLchcXftE9jihHJXJ4k0cxC03Kpd0caZE7b67W1YQdJhU9mZx6CP1A3MdWU
-f48QIsmHgejkihtGNDRheBRzNdpHPhdOjYIWhOeAHh/xnm7PVZBMXmZeNW7MrAS0
-+lN99r7Xj3eqtSdrMrrob845PWYsATA/a8ouUuTT7812fl8tZx69xb8wV5hNztDj
-srOxxJbjt1uwcSp67f2K91D97CQuMMPnOt1oFEXT+5QtLEiybXKfkshwO7VVDoIg
-owMLoKIiA2Xr+rZn7CEBeTrqQuRJfJEI+k/49zg2zWQIKGQAz8iaO0l95Hk5kPTE
-A5i9qxhn0UqC6a4Dkj8ybQOACzVA06IsbbYdprfzURQHjcUiSrEzjrwWxkkX/9El
-Z0CJAtkXx4pMxo5s6zt26ZPC3cxWB7aageWSC4xDbKuQP5VCNVg1nWymg6nF4Xk9
-d7n7yvSNH6fAZrpmZo7o7CBxCb4QN8j7TpyuaPd7nzyCyR6aGbh7fz8xksukvj6w
-ZSbAAy5uw4hyUwTTpyPyw+qQxI7O/PU=
------END CERTIFICATE-----
diff --git a/automotive/audiocontrol/aidl/Android.bp b/automotive/audiocontrol/aidl/Android.bp
index 9ae77cd..86b63a6 100644
--- a/automotive/audiocontrol/aidl/Android.bp
+++ b/automotive/audiocontrol/aidl/Android.bp
@@ -13,9 +13,9 @@
     name: "android.hardware.automotive.audiocontrol",
     vendor_available: true,
     srcs: ["android/hardware/automotive/audiocontrol/*.aidl"],
-    imports: [
-        "android.hardware.audio.common-V1",
-        "android.media.audio.common.types-V2",
+    defaults: [
+        "latest_android_hardware_audio_common_import_interface",
+        "latest_android_media_audio_common_types_import_interface",
     ],
     stability: "vintf",
     backend: {
@@ -52,6 +52,37 @@
         },
 
     ],
-    frozen: true,
+    frozen: false,
 
 }
+
+// Note: This should always be one version ahead of the last frozen version
+latest_android_hardware_automotive_audiocontrol = "android.hardware.automotive.audiocontrol-V4"
+
+cc_defaults {
+    name: "latest_android_hardware_automotive_audiocontrol_cpp_static",
+    static_libs: [
+        latest_android_hardware_automotive_audiocontrol + "-cpp",
+    ],
+}
+
+cc_defaults {
+    name: "latest_android_hardware_automotive_audiocontrol_cpp_shared",
+    shared_libs: [
+        latest_android_hardware_automotive_audiocontrol + "-cpp",
+    ],
+}
+
+cc_defaults {
+    name: "latest_android_hardware_automotive_audiocontrol_ndk_static",
+    static_libs: [
+        latest_android_hardware_automotive_audiocontrol + "-ndk",
+    ],
+}
+
+cc_defaults {
+    name: "latest_android_hardware_automotive_audiocontrol_ndk_shared",
+    shared_libs: [
+        latest_android_hardware_automotive_audiocontrol + "-ndk",
+    ],
+}
diff --git a/automotive/audiocontrol/aidl/default/audiocontrol-default.xml b/automotive/audiocontrol/aidl/default/audiocontrol-default.xml
index 95cd7f0..bcb5669 100644
--- a/automotive/audiocontrol/aidl/default/audiocontrol-default.xml
+++ b/automotive/audiocontrol/aidl/default/audiocontrol-default.xml
@@ -1,7 +1,7 @@
 <manifest version="2.0" type="device">
     <hal format="aidl">
         <name>android.hardware.automotive.audiocontrol</name>
-        <version>3</version>
+        <version>4</version>
         <fqname>IAudioControl/default</fqname>
     </hal>
 </manifest>
diff --git a/automotive/audiocontrol/aidl/vts/Android.bp b/automotive/audiocontrol/aidl/vts/Android.bp
index cfc2a3e..c73ad79 100644
--- a/automotive/audiocontrol/aidl/vts/Android.bp
+++ b/automotive/audiocontrol/aidl/vts/Android.bp
@@ -24,6 +24,8 @@
 cc_test {
     name: "VtsAidlHalAudioControlTest",
     defaults: [
+        "latest_android_hardware_audio_common_cpp_static",
+        "latest_android_hardware_automotive_audiocontrol_cpp_static",
         "latest_android_media_audio_common_types_cpp_static",
         "VtsHalTargetTestDefaults",
         "use_libaidlvintf_gtest_helper_static",
@@ -38,8 +40,6 @@
         "libxml2",
     ],
     static_libs: [
-        "android.hardware.automotive.audiocontrol-V3-cpp",
-        "android.hardware.audio.common-V1-cpp",
         "libgmock",
     ],
     test_suites: [
diff --git a/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.cpp b/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.cpp
index 96110db..e882160 100644
--- a/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.cpp
+++ b/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.cpp
@@ -18,9 +18,9 @@
 
 namespace android::hardware::automotive::can::V1_0::implementation::fuzzer {
 
-constexpr CanController::InterfaceType kInterfaceType[] = {CanController::InterfaceType::VIRTUAL,
-                                                           CanController::InterfaceType::SOCKETCAN,
-                                                           CanController::InterfaceType::SLCAN};
+constexpr CanController::InterfaceType kInterfaceType[] = {
+        CanController::InterfaceType::VIRTUAL, CanController::InterfaceType::SOCKETCAN,
+        CanController::InterfaceType::SLCAN, CanController::InterfaceType::INDEXED};
 constexpr FilterFlag kFilterFlag[] = {FilterFlag::DONT_CARE, FilterFlag::SET, FilterFlag::NOT_SET};
 constexpr size_t kInterfaceTypeLength = std::size(kInterfaceType);
 constexpr size_t kFilterFlagLength = std::size(kFilterFlag);
@@ -28,8 +28,8 @@
 constexpr size_t kMaxPayloadBytes = 64;
 constexpr size_t kMaxFilters = 20;
 constexpr size_t kMaxSerialNumber = 1000;
-constexpr size_t kMaxBuses = 10;
-constexpr size_t kMaxRepeat = 5;
+constexpr size_t kMaxBuses = 100;
+constexpr size_t kMaxRepeat = 100;
 
 Bus CanFuzzer::makeBus() {
     ICanController::BusConfig config = {};
@@ -56,9 +56,13 @@
 }
 
 void CanFuzzer::invokeUpInterface() {
-    const CanController::InterfaceType iftype =
-            kInterfaceType[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
-                    0, kInterfaceTypeLength - 1)];
+    CanController::InterfaceType controller;
+    if (mFuzzedDataProvider->ConsumeBool()) {
+        controller = (CanController::InterfaceType)mFuzzedDataProvider->ConsumeIntegral<uint8_t>();
+    } else {
+        controller = kInterfaceType[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
+                0, kInterfaceTypeLength - 1)];
+    }
     std::string configName;
 
     if (const bool shouldInvokeValidBus = mFuzzedDataProvider->ConsumeBool();
@@ -73,7 +77,7 @@
 
     ICanController::BusConfig config = {.name = configName};
 
-    if (iftype == CanController::InterfaceType::SOCKETCAN) {
+    if (controller == CanController::InterfaceType::SOCKETCAN) {
         CanController::BusConfig::InterfaceId::Socketcan socketcan = {};
         if (const bool shouldPassSerialSocket = mFuzzedDataProvider->ConsumeBool();
             shouldPassSerialSocket) {
@@ -83,7 +87,7 @@
             socketcan.ifname(ifname);
         }
         config.interfaceId.socketcan(socketcan);
-    } else if (iftype == CanController::InterfaceType::SLCAN) {
+    } else if (controller == CanController::InterfaceType::SLCAN) {
         CanController::BusConfig::InterfaceId::Slcan slcan = {};
         if (const bool shouldPassSerialSlcan = mFuzzedDataProvider->ConsumeBool();
             shouldPassSerialSlcan) {
@@ -93,8 +97,12 @@
             slcan.ttyname(ifname);
         }
         config.interfaceId.slcan(slcan);
-    } else if (iftype == CanController::InterfaceType::VIRTUAL) {
+    } else if (controller == CanController::InterfaceType::VIRTUAL) {
         config.interfaceId.virtualif({ifname});
+    } else if (controller == CanController::InterfaceType::INDEXED) {
+        CanController::BusConfig::InterfaceId::Indexed indexed;
+        indexed.index = mFuzzedDataProvider->ConsumeIntegral<uint8_t>();
+        config.interfaceId.indexed(indexed);
     }
 
     const size_t numInvocations =
@@ -108,8 +116,13 @@
     hidl_string configName;
     if (const bool shouldInvokeValidBus = mFuzzedDataProvider->ConsumeBool();
         (shouldInvokeValidBus) && (mBusNames.size() > 0)) {
-        const size_t busNameIndex =
-                mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(0, mBusNames.size() - 1);
+        size_t busNameIndex;
+        if (mBusNames.size() == 1) {
+            busNameIndex = 0;
+        } else {
+            busNameIndex =
+                    mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(0, mBusNames.size() - 1);
+        }
         configName = mBusNames[busNameIndex];
     } else {
         configName = mFuzzedDataProvider->ConsumeRandomLengthString(kMaxCharacters);
@@ -122,12 +135,6 @@
     }
 }
 
-void CanFuzzer::invokeController() {
-    getSupportedInterfaceTypes();
-    invokeUpInterface();
-    invokeDownInterface();
-}
-
 void CanFuzzer::invokeBus() {
     const size_t numBuses = mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(1, kMaxBuses);
     for (size_t i = 0; i < numBuses; ++i) {
@@ -152,12 +159,22 @@
             for (uint32_t k = 0; k < numFilters; ++k) {
                 filterVector[k].id = mFuzzedDataProvider->ConsumeIntegral<uint32_t>();
                 filterVector[k].mask = mFuzzedDataProvider->ConsumeIntegral<uint32_t>();
-                filterVector[k].rtr =
-                        kFilterFlag[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
-                                0, kFilterFlagLength - 1)];
-                filterVector[k].extendedFormat =
-                        kFilterFlag[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
-                                0, kFilterFlagLength - 1)];
+                if (mFuzzedDataProvider->ConsumeBool()) {
+                    filterVector[k].rtr =
+                            (FilterFlag)mFuzzedDataProvider->ConsumeIntegral<uint8_t>();
+                } else {
+                    filterVector[k].rtr =
+                            kFilterFlag[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
+                                    0, kFilterFlagLength - 1)];
+                }
+                if (mFuzzedDataProvider->ConsumeBool()) {
+                    filterVector[k].extendedFormat =
+                            (FilterFlag)mFuzzedDataProvider->ConsumeIntegral<uint8_t>();
+                } else {
+                    filterVector[k].extendedFormat =
+                            kFilterFlag[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
+                                    0, kFilterFlagLength - 1)];
+                }
                 filterVector[k].exclude = mFuzzedDataProvider->ConsumeBool();
             }
             auto listener = listeningBus.listen(filterVector);
@@ -175,8 +192,16 @@
 
 void CanFuzzer::process(const uint8_t* data, size_t size) {
     mFuzzedDataProvider = new FuzzedDataProvider(data, size);
-    invokeController();
-    invokeBus();
+    while (mFuzzedDataProvider->remaining_bytes()) {
+        auto CanFuzzerFunction =
+                mFuzzedDataProvider->PickValueInArray<const std::function<void()>>({
+                        [&]() { getSupportedInterfaceTypes(); },
+                        [&]() { invokeUpInterface(); },
+                        [&]() { invokeDownInterface(); },
+                        [&]() { invokeBus(); },
+                });
+        CanFuzzerFunction();
+    }
 }
 
 bool CanFuzzer::init() {
diff --git a/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.h b/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.h
index 930cddd..3211bd0 100644
--- a/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.h
+++ b/automotive/can/1.0/default/tests/fuzzer/AutomotiveCanV1_0Fuzzer.h
@@ -116,7 +116,6 @@
     hidl_vec<hidl_string> getBusNames();
     void getSupportedInterfaceTypes();
     void invokeBus();
-    void invokeController();
     void invokeUpInterface();
     void invokeDownInterface();
     FuzzedDataProvider* mFuzzedDataProvider = nullptr;
diff --git a/automotive/evs/1.0/vts/functional/VtsHalEvsV1_0TargetTest.cpp b/automotive/evs/1.0/vts/functional/VtsHalEvsV1_0TargetTest.cpp
index 9c72acd..580b0ee 100644
--- a/automotive/evs/1.0/vts/functional/VtsHalEvsV1_0TargetTest.cpp
+++ b/automotive/evs/1.0/vts/functional/VtsHalEvsV1_0TargetTest.cpp
@@ -66,8 +66,8 @@
 
         ASSERT_NE(pEnumerator.get(), nullptr);
 
-        // "default" is reserved for EVS manager.
-        constexpr static char kEvsManagerName[] = "default";
+        // "legacy_sw/0" is reserved for EVS manager v1.0 implementation.
+        constexpr static char kEvsManagerName[] = "legacy_sw/0";
         mIsHwModule = service_name.compare(kEvsManagerName);
     }
 
@@ -364,8 +364,14 @@
 TEST_P(EvsHidlTest, CameraStreamBuffering) {
     ALOGI("Starting CameraStreamBuffering test");
 
-    // Arbitrary constant (should be > 1 and not too big)
-    static const unsigned int kBuffersToHold = 2;
+    // Maximum number of frames in flight this test case will attempt. This test
+    // case chooses an arbitrary number that is large enough to run a camera
+    // pipeline for a single client.
+    constexpr unsigned int kMaxBuffersToHold = 20;
+
+    // Initial value for setMaxFramesInFlight() call. This number should be
+    // greater than 1.
+    unsigned int buffersToHold = 2;
 
     // Get the camera list
     loadCameraList();
@@ -381,9 +387,16 @@
         EXPECT_EQ(EvsResult::BUFFER_NOT_AVAILABLE, badResult);
 
         // Now ask for exactly two buffers in flight as we'll test behavior in that case
-        Return<EvsResult> goodResult = pCam->setMaxFramesInFlight(kBuffersToHold);
-        EXPECT_EQ(EvsResult::OK, goodResult);
+        // Find the minimum number of buffers to run a target camera.
+        while (buffersToHold < kMaxBuffersToHold) {
+            Return<EvsResult> goodResult = pCam->setMaxFramesInFlight(buffersToHold);
+            if (goodResult == EvsResult::OK) {
+                break;
+            }
 
+            ++buffersToHold;
+        }
+        EXPECT_LE(buffersToHold, kMaxBuffersToHold);
 
         // Set up a frame receiver object which will fire up its own thread.
         sp<FrameHandler> frameHandler = new FrameHandler(pCam, cam,
@@ -399,7 +412,7 @@
         sleep(2);   // 1 second should be enough for at least 5 frames to be delivered worst case
         unsigned framesReceived = 0;
         frameHandler->getFramesCounters(&framesReceived, nullptr);
-        ASSERT_EQ(kBuffersToHold, framesReceived) << "Stream didn't stall at expected buffer limit";
+        ASSERT_EQ(buffersToHold, framesReceived) << "Stream didn't stall at expected buffer limit";
 
 
         // Give back one buffer
@@ -410,7 +423,7 @@
         // filled since we require 10fps minimum -- but give a 10% allowance just in case.
         usleep(110 * kMillisecondsToMicroseconds);
         frameHandler->getFramesCounters(&framesReceived, nullptr);
-        EXPECT_EQ(kBuffersToHold+1, framesReceived) << "Stream should've resumed";
+        EXPECT_EQ(buffersToHold+1, framesReceived) << "Stream should've resumed";
 
         // Even when the camera pointer goes out of scope, the FrameHandler object will
         // keep the stream alive unless we tell it to shutdown.
diff --git a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
index 9c8bfc4..03f256e 100644
--- a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
+++ b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
@@ -534,8 +534,14 @@
 TEST_P(EvsHidlTest, CameraStreamBuffering) {
     LOG(INFO) << "Starting CameraStreamBuffering test";
 
-    // Arbitrary constant (should be > 1 and not too big)
-    static const unsigned int kBuffersToHold = 2;
+    // Maximum number of frames in flight this test case will attempt. This test
+    // case chooses an arbitrary number that is large enough to run a camera
+    // pipeline for a single client.
+    constexpr unsigned int kMaxBuffersToHold = 20;
+
+    // Initial value for setMaxFramesInFlight() call. This number should be
+    // greater than 1.
+    unsigned int buffersToHold = 2;
 
     // Get the camera list
     loadCameraList();
@@ -567,9 +573,15 @@
         EXPECT_EQ(EvsResult::BUFFER_NOT_AVAILABLE, badResult);
 
         // Now ask for exactly two buffers in flight as we'll test behavior in that case
-        Return<EvsResult> goodResult = pCam->setMaxFramesInFlight(kBuffersToHold);
-        EXPECT_EQ(EvsResult::OK, goodResult);
+        while (buffersToHold < kMaxBuffersToHold) {
+            Return<EvsResult> goodResult = pCam->setMaxFramesInFlight(buffersToHold);
+            if (goodResult == EvsResult::OK) {
+                break;
+            }
 
+            ++buffersToHold;
+        }
+        EXPECT_LE(buffersToHold, kMaxBuffersToHold);
 
         // Set up a frame receiver object which will fire up its own thread.
         sp<FrameHandler> frameHandler = new FrameHandler(pCam, cam,
@@ -585,7 +597,7 @@
         sleep(1);   // 1 second should be enough for at least 5 frames to be delivered worst case
         unsigned framesReceived = 0;
         frameHandler->getFramesCounters(&framesReceived, nullptr);
-        ASSERT_EQ(kBuffersToHold, framesReceived) << "Stream didn't stall at expected buffer limit";
+        ASSERT_EQ(buffersToHold, framesReceived) << "Stream didn't stall at expected buffer limit";
 
 
         // Give back one buffer
@@ -596,7 +608,7 @@
         // filled since we require 10fps minimum -- but give a 10% allowance just in case.
         usleep(110 * kMillisecondsToMicroseconds);
         frameHandler->getFramesCounters(&framesReceived, nullptr);
-        EXPECT_EQ(kBuffersToHold+1, framesReceived) << "Stream should've resumed";
+        EXPECT_EQ(buffersToHold+1, framesReceived) << "Stream should've resumed";
 
         // Even when the camera pointer goes out of scope, the FrameHandler object will
         // keep the stream alive unless we tell it to shutdown.
diff --git a/automotive/evs/aidl/Android.bp b/automotive/evs/aidl/Android.bp
index bafb4af..3bfe8f3 100644
--- a/automotive/evs/aidl/Android.bp
+++ b/automotive/evs/aidl/Android.bp
@@ -30,7 +30,7 @@
     stability: "vintf",
     imports: [
         "android.hardware.common-V2",
-        "android.hardware.graphics.common-V4",
+        "android.hardware.graphics.common-V5",
     ],
     backend: {
         java: {
@@ -53,14 +53,14 @@
             version: "1",
             imports: [
                 "android.hardware.common-V2",
-                "android.hardware.graphics.common-V4",
+                "android.hardware.graphics.common-V5",
             ],
         },
         {
             version: "2",
             imports: [
                 "android.hardware.common-V2",
-                "android.hardware.graphics.common-V4",
+                "android.hardware.graphics.common-V5",
             ],
         },
 
diff --git a/automotive/evs/aidl/impl/default/Android.bp b/automotive/evs/aidl/impl/default/Android.bp
index 6b638a3..3d5b7c4 100644
--- a/automotive/evs/aidl/impl/default/Android.bp
+++ b/automotive/evs/aidl/impl/default/Android.bp
@@ -24,6 +24,9 @@
 cc_defaults {
     name: "android.hardware.automotive.evs-aidl-default-service-default",
     defaults: ["EvsHalDefaults"],
+    header_libs: [
+        "libstagefright_headers",
+    ],
     shared_libs: [
         "android.hardware.graphics.bufferqueue@1.0",
         "android.hardware.graphics.bufferqueue@2.0",
@@ -35,6 +38,7 @@
         "libcamera_metadata",
         "libhardware_legacy",
         "libhidlbase",
+        "libmediandk",
         "libnativewindow",
         "libtinyxml2",
         "libui",
diff --git a/automotive/evs/aidl/impl/default/include/EvsCamera.h b/automotive/evs/aidl/impl/default/include/EvsCamera.h
index 5774db5..539d5f6 100644
--- a/automotive/evs/aidl/impl/default/include/EvsCamera.h
+++ b/automotive/evs/aidl/impl/default/include/EvsCamera.h
@@ -85,7 +85,7 @@
 
     void closeAllBuffers_unsafe();
 
-    // Returns (ID, handle) if succeeds. (static_cast<size_t>(-1), nullptr) otherwise.
+    // Returns (ID, handle) if succeeds. (kInvalidBufferID, nullptr) otherwise.
     [[nodiscard]] std::pair<std::size_t, buffer_handle_t> useBuffer_unsafe();
 
     void returnBuffer_unsafe(const std::size_t id);
@@ -133,6 +133,12 @@
 
     std::size_t mAvailableFrames{0};
     std::size_t mFramesInUse{0};
+
+    // We use all 1's as a reserved invalid buffer ID.
+    static constexpr std::size_t kInvalidBufferID = ~static_cast<std::size_t>(0);
+
+  public:
+    static bool IsBufferIDValid(const std::size_t bufferId) { return ~bufferId; }
 };
 
 }  // namespace aidl::android::hardware::automotive::evs::implementation
diff --git a/automotive/evs/aidl/impl/default/include/EvsMockCamera.h b/automotive/evs/aidl/impl/default/include/EvsMockCamera.h
index c7b9e0f..cd68532 100644
--- a/automotive/evs/aidl/impl/default/include/EvsMockCamera.h
+++ b/automotive/evs/aidl/impl/default/include/EvsMockCamera.h
@@ -17,29 +17,36 @@
 #pragma once
 
 #include "ConfigManager.h"
-#include "EvsCameraBase.h"
+#include "EvsCamera.h"
 
-#include <aidl/android/hardware/automotive/evs/BufferDesc.h>
 #include <aidl/android/hardware/automotive/evs/CameraDesc.h>
 #include <aidl/android/hardware/automotive/evs/CameraParam.h>
-#include <aidl/android/hardware/automotive/evs/EvsResult.h>
 #include <aidl/android/hardware/automotive/evs/IEvsCameraStream.h>
 #include <aidl/android/hardware/automotive/evs/IEvsDisplay.h>
 #include <aidl/android/hardware/automotive/evs/ParameterRange.h>
 #include <aidl/android/hardware/automotive/evs/Stream.h>
-// #include <android-base/result.h>
 #include <android/hardware_buffer.h>
 #include <ui/GraphicBuffer.h>
 
-#include <functional>
+#include <cstdint>
+#include <memory>
 #include <thread>
+#include <unordered_map>
+#include <vector>
 
 namespace aidl::android::hardware::automotive::evs::implementation {
 
-class EvsMockCamera : public EvsCameraBase {
+class EvsMockCamera : public EvsCamera {
+  private:
+    using Base = EvsCamera;
+
   public:
+    EvsMockCamera(Sigil sigil, const char* deviceName,
+                  std::unique_ptr<ConfigManager::CameraInfo>& camInfo);
+    EvsMockCamera(const EvsMockCamera&) = delete;
+    EvsMockCamera& operator=(const EvsMockCamera&) = delete;
+
     // Methods from ::android::hardware::automotive::evs::IEvsCamera follow.
-    ndk::ScopedAStatus doneWithFrame(const std::vector<evs::BufferDesc>& buffers) override;
     ndk::ScopedAStatus forcePrimaryClient(
             const std::shared_ptr<evs::IEvsDisplay>& display) override;
     ndk::ScopedAStatus getCameraInfo(evs::CameraDesc* _aidl_return) override;
@@ -51,49 +58,37 @@
     ndk::ScopedAStatus getParameterList(std::vector<evs::CameraParam>* _aidl_return) override;
     ndk::ScopedAStatus getPhysicalCameraInfo(const std::string& deviceId,
                                              evs::CameraDesc* _aidl_return) override;
-    ndk::ScopedAStatus importExternalBuffers(const std::vector<evs::BufferDesc>& buffers,
-                                             int32_t* _aidl_return) override;
-    ndk::ScopedAStatus pauseVideoStream() override;
-    ndk::ScopedAStatus resumeVideoStream() override;
     ndk::ScopedAStatus setExtendedInfo(int32_t opaqueIdentifier,
                                        const std::vector<uint8_t>& opaqueValue) override;
     ndk::ScopedAStatus setIntParameter(evs::CameraParam id, int32_t value,
                                        std::vector<int32_t>* effectiveValue) override;
     ndk::ScopedAStatus setPrimaryClient() override;
-    ndk::ScopedAStatus setMaxFramesInFlight(int32_t bufferCount) override;
-    ndk::ScopedAStatus startVideoStream(
-            const std::shared_ptr<evs::IEvsCameraStream>& receiver) override;
-    ndk::ScopedAStatus stopVideoStream() override;
     ndk::ScopedAStatus unsetPrimaryClient() override;
 
+    const evs::CameraDesc& getDesc() { return mDescription; }
+
     static std::shared_ptr<EvsMockCamera> Create(const char* deviceName);
     static std::shared_ptr<EvsMockCamera> Create(
             const char* deviceName, std::unique_ptr<ConfigManager::CameraInfo>& camInfo,
             const evs::Stream* streamCfg = nullptr);
-    EvsMockCamera(const EvsMockCamera&) = delete;
-    EvsMockCamera& operator=(const EvsMockCamera&) = delete;
-
-    virtual ~EvsMockCamera() override;
-
-    // Methods from EvsCameraBase follow.
-    void shutdown() override;
-
-    const evs::CameraDesc& getDesc() { return mDescription; }
-
-    // Constructors
-    EvsMockCamera(Sigil sigil, const char* deviceName,
-                  std::unique_ptr<ConfigManager::CameraInfo>& camInfo);
 
   private:
-    // These three functions are expected to be called while mAccessLock is held
-    bool setAvailableFrames_Locked(unsigned bufferCount);
-    unsigned increaseAvailableFrames_Locked(unsigned numToAdd);
-    unsigned decreaseAvailableFrames_Locked(unsigned numToRemove);
-
     void generateFrames();
     void fillMockFrame(buffer_handle_t handle, const AHardwareBuffer_Desc* pDesc);
-    void returnBufferLocked(const uint32_t bufferId);
-    ndk::ScopedAStatus stopVideoStream_impl();
+
+    ::android::status_t allocateOneFrame(buffer_handle_t* handle) override;
+
+    bool startVideoStreamImpl_locked(const std::shared_ptr<evs::IEvsCameraStream>& receiver,
+                                     ndk::ScopedAStatus& status,
+                                     std::unique_lock<std::mutex>& lck) override;
+
+    bool stopVideoStreamImpl_locked(ndk::ScopedAStatus& status,
+                                    std::unique_lock<std::mutex>& lck) override;
+
+    bool postVideoStreamStop_locked(ndk::ScopedAStatus& status,
+                                    std::unique_lock<std::mutex>& lck) override;
+
+    void initializeParameters();
 
     CameraDesc mDescription = {};  // The properties of this camera
 
@@ -114,28 +109,6 @@
     // Bytes per line in the buffers
     uint32_t mStride = 0;
 
-    struct BufferRecord {
-        buffer_handle_t handle;
-        bool inUse;
-
-        explicit BufferRecord(buffer_handle_t h) : handle(h), inUse(false){};
-    };
-
-    std::vector<BufferRecord> mBuffers;  // Graphics buffers to transfer images
-    unsigned mFramesAllowed;             // How many buffers are we currently using
-    unsigned mFramesInUse;               // How many buffers are currently outstanding
-
-    enum StreamStateValues {
-        STOPPED,
-        RUNNING,
-        STOPPING,
-        DEAD,
-    };
-    StreamStateValues mStreamState;
-
-    // Synchronization necessary to deconflict mCaptureThread from the main service thread
-    std::mutex mAccessLock;
-
     // Static camera module information
     std::unique_ptr<ConfigManager::CameraInfo>& mCameraInfo;
 
@@ -155,7 +128,6 @@
         int32_t value;
     };
     std::unordered_map<CameraParam, std::shared_ptr<CameraParameterDesc>> mParams;
-    void initializeParameters();
 };
 
 }  // namespace aidl::android::hardware::automotive::evs::implementation
diff --git a/automotive/evs/aidl/impl/default/include/EvsVideoEmulatedCamera.h b/automotive/evs/aidl/impl/default/include/EvsVideoEmulatedCamera.h
index 356a42a..a850d65 100644
--- a/automotive/evs/aidl/impl/default/include/EvsVideoEmulatedCamera.h
+++ b/automotive/evs/aidl/impl/default/include/EvsVideoEmulatedCamera.h
@@ -26,20 +26,27 @@
 #include <aidl/android/hardware/automotive/evs/IEvsDisplay.h>
 #include <aidl/android/hardware/automotive/evs/ParameterRange.h>
 #include <aidl/android/hardware/automotive/evs/Stream.h>
+#include <media/NdkMediaExtractor.h>
+
+#include <ui/GraphicBuffer.h>
 
 #include <cstdint>
 #include <memory>
+#include <thread>
 #include <unordered_map>
 #include <vector>
 
 namespace aidl::android::hardware::automotive::evs::implementation {
 
 class EvsVideoEmulatedCamera : public EvsCamera {
+  private:
+    using Base = EvsCamera;
+
   public:
     EvsVideoEmulatedCamera(Sigil sigil, const char* deviceName,
                            std::unique_ptr<ConfigManager::CameraInfo>& camInfo);
 
-    ~EvsVideoEmulatedCamera() override;
+    ~EvsVideoEmulatedCamera() override = default;
 
     // Methods from ::android::hardware::automotive::evs::IEvsCamera follow.
     ndk::ScopedAStatus forcePrimaryClient(
@@ -60,6 +67,9 @@
     ndk::ScopedAStatus setPrimaryClient() override;
     ndk::ScopedAStatus unsetPrimaryClient() override;
 
+    // Methods from EvsCameraBase follow.
+    void shutdown() override;
+
     const evs::CameraDesc& getDesc() { return mDescription; }
 
     static std::shared_ptr<EvsVideoEmulatedCamera> Create(const char* deviceName);
@@ -81,8 +91,18 @@
         int32_t value;
     };
 
+    bool initialize();
+
+    void generateFrames();
+
+    void renderOneFrame();
+
     void initializeParameters();
 
+    void onCodecInputAvailable(const int32_t index);
+
+    void onCodecOutputAvailable(const int32_t index, const AMediaCodecBufferInfo& info);
+
     ::android::status_t allocateOneFrame(buffer_handle_t* handle) override;
 
     bool startVideoStreamImpl_locked(const std::shared_ptr<evs::IEvsCameraStream>& receiver,
@@ -92,9 +112,42 @@
     bool stopVideoStreamImpl_locked(ndk::ScopedAStatus& status,
                                     std::unique_lock<std::mutex>& lck) override;
 
+    bool postVideoStreamStop_locked(ndk::ScopedAStatus& status,
+                                    std::unique_lock<std::mutex>& lck) override;
+
     // The properties of this camera.
     CameraDesc mDescription = {};
 
+    std::thread mCaptureThread;
+
+    // The callback used to deliver each frame
+    std::shared_ptr<evs::IEvsCameraStream> mStream;
+
+    std::string mVideoFileName;
+    // Media decoder resources - Owned by mDecoderThead when thread is running.
+    int mVideoFd = 0;
+
+    struct AMediaExtractorDeleter {
+        void operator()(AMediaExtractor* extractor) const { AMediaExtractor_delete(extractor); }
+    };
+    struct AMediaCodecDeleter {
+        void operator()(AMediaCodec* codec) const { AMediaCodec_delete(codec); }
+    };
+
+    std::unique_ptr<AMediaExtractor, AMediaExtractorDeleter> mVideoExtractor;
+    std::unique_ptr<AMediaCodec, AMediaCodecDeleter> mVideoCodec;
+
+    // Horizontal pixel count in the buffers
+    int32_t mWidth = 0;
+    // Vertical pixel count in the buffers
+    int32_t mHeight = 0;
+    // Values from android_pixel_format_t
+    uint32_t mFormat = 0;
+    // Values from from Gralloc.h
+    uint64_t mUsage = 0;
+    // Bytes per line in the buffers
+    uint32_t mStride = 0;
+
     // Camera parameters.
     std::unordered_map<CameraParam, std::shared_ptr<CameraParameterDesc>> mParams;
 
diff --git a/automotive/evs/aidl/impl/default/src/EvsCamera.cpp b/automotive/evs/aidl/impl/default/src/EvsCamera.cpp
index 208a1a4..bc3bfdd 100644
--- a/automotive/evs/aidl/impl/default/src/EvsCamera.cpp
+++ b/automotive/evs/aidl/impl/default/src/EvsCamera.cpp
@@ -292,7 +292,7 @@
 std::pair<std::size_t, buffer_handle_t> EvsCamera::useBuffer_unsafe() {
     if (mFramesInUse >= mAvailableFrames) {
         DCHECK_EQ(mFramesInUse, mAvailableFrames);
-        return {static_cast<std::size_t>(-1), nullptr};
+        return {kInvalidBufferID, nullptr};
     }
     const std::size_t pos = mFramesInUse++;
     auto& buffer = mBuffers[pos];
diff --git a/automotive/evs/aidl/impl/default/src/EvsMockCamera.cpp b/automotive/evs/aidl/impl/default/src/EvsMockCamera.cpp
index 797b221..ef43925 100644
--- a/automotive/evs/aidl/impl/default/src/EvsMockCamera.cpp
+++ b/automotive/evs/aidl/impl/default/src/EvsMockCamera.cpp
@@ -15,28 +15,25 @@
  */
 
 #include "EvsMockCamera.h"
-#include "ConfigManager.h"
-#include "EvsEnumerator.h"
+
+#include <aidl/android/hardware/automotive/evs/EvsResult.h>
 
 #include <aidlcommonsupport/NativeHandle.h>
+#include <android-base/logging.h>
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/GraphicBufferMapper.h>
 #include <utils/SystemClock.h>
 
+#include <cstddef>
+#include <cstdint>
 #include <memory>
+#include <tuple>
 
 namespace {
 
 using ::aidl::android::hardware::graphics::common::BufferUsage;
 using ::ndk::ScopedAStatus;
 
-// Arbitrary limit on number of graphics buffers allowed to be allocated
-// Safeguards against unreasonable resource consumption and provides a testable limit
-constexpr unsigned kMaxBuffersInFlight = 100;
-
-// Minimum number of buffers to run a video stream
-constexpr int kMinimumBuffersInFlight = 1;
-
 // Colors for the colorbar test pattern in ABGR format
 constexpr uint32_t kColors[] = {
         0xFFFFFFFF,  // white
@@ -56,7 +53,7 @@
 
 EvsMockCamera::EvsMockCamera([[maybe_unused]] Sigil sigil, const char* id,
                              std::unique_ptr<ConfigManager::CameraInfo>& camInfo)
-    : mFramesAllowed(0), mFramesInUse(0), mStreamState(STOPPED), mCameraInfo(camInfo) {
+    : mCameraInfo(camInfo) {
     LOG(DEBUG) << __FUNCTION__;
 
     /* set a camera id */
@@ -73,11 +70,6 @@
     initializeParameters();
 }
 
-EvsMockCamera::~EvsMockCamera() {
-    LOG(DEBUG) << __FUNCTION__;
-    shutdown();
-}
-
 void EvsMockCamera::initializeParameters() {
     mParams.emplace(
             CameraParam::BRIGHTNESS,
@@ -90,35 +82,6 @@
             new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255));
 }
 
-// This gets called if another caller "steals" ownership of the camera
-void EvsMockCamera::shutdown() {
-    LOG(DEBUG) << __FUNCTION__;
-
-    // Make sure our output stream is cleaned up
-    // (It really should be already)
-    stopVideoStream_impl();
-
-    // Claim the lock while we work on internal state
-    std::lock_guard lock(mAccessLock);
-
-    // Drop all the graphics buffers we've been using
-    if (mBuffers.size() > 0) {
-        ::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
-        for (auto&& rec : mBuffers) {
-            if (rec.inUse) {
-                LOG(WARNING) << "WARNING: releasing a buffer remotely owned.";
-            }
-            alloc.free(rec.handle);
-            rec.handle = nullptr;
-        }
-        mBuffers.clear();
-    }
-
-    // Put this object into an unrecoverable error state since somebody else
-    // is going to own the underlying camera now
-    mStreamState = DEAD;
-}
-
 // Methods from ::aidl::android::hardware::automotive::evs::IEvsCamera follow.
 ScopedAStatus EvsMockCamera::getCameraInfo(CameraDesc* _aidl_return) {
     LOG(DEBUG) << __FUNCTION__;
@@ -128,115 +91,6 @@
     return ScopedAStatus::ok();
 }
 
-ScopedAStatus EvsMockCamera::setMaxFramesInFlight(int32_t bufferCount) {
-    LOG(DEBUG) << __FUNCTION__ << ", bufferCount = " << bufferCount;
-    ;
-
-    std::lock_guard lock(mAccessLock);
-
-    // If we've been displaced by another owner of the camera, then we can't do anything else
-    if (mStreamState == DEAD) {
-        LOG(ERROR) << "Ignoring setMaxFramesInFlight call when camera has been lost.";
-        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
-    }
-
-    // We cannot function without at least one video buffer to send data
-    if (bufferCount < 1) {
-        LOG(ERROR) << "Ignoring setMaxFramesInFlight with less than one buffer requested.";
-        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
-    }
-
-    // Update our internal state
-    if (!setAvailableFrames_Locked(bufferCount)) {
-        LOG(ERROR) << "Failed to adjust the maximum number of frames in flight.";
-        return ScopedAStatus::fromServiceSpecificError(
-                static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
-    }
-
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus EvsMockCamera::startVideoStream(const std::shared_ptr<IEvsCameraStream>& cb) {
-    LOG(DEBUG) << __FUNCTION__;
-
-    if (!cb) {
-        LOG(ERROR) << "A given stream callback is invalid.";
-        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
-    }
-
-    std::lock_guard lock(mAccessLock);
-
-    // If we've been displaced by another owner of the camera, then we can't do anything else
-    if (mStreamState == DEAD) {
-        LOG(ERROR) << "Ignoring startVideoStream call when camera has been lost.";
-        return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
-    }
-
-    if (mStreamState != STOPPED) {
-        LOG(ERROR) << "Ignoring startVideoStream call when a stream is already running.";
-        return ScopedAStatus::fromServiceSpecificError(
-                static_cast<int>(EvsResult::STREAM_ALREADY_RUNNING));
-    }
-
-    // If the client never indicated otherwise, configure ourselves for a single streaming buffer
-    if (mFramesAllowed < kMinimumBuffersInFlight &&
-        !setAvailableFrames_Locked(kMinimumBuffersInFlight)) {
-        LOG(ERROR) << "Failed to start stream because we couldn't get a graphics buffer";
-        return ScopedAStatus::fromServiceSpecificError(
-                static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
-    }
-
-    // Record the user's callback for use when we have a frame ready
-    mStream = cb;
-
-    // Start the frame generation thread
-    mStreamState = RUNNING;
-    mCaptureThread = std::thread([this]() { generateFrames(); });
-
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus EvsMockCamera::doneWithFrame(const std::vector<BufferDesc>& list) {
-    std::lock_guard lock(mAccessLock);
-    for (const auto& desc : list) {
-        returnBufferLocked(desc.bufferId);
-    }
-
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus EvsMockCamera::stopVideoStream() {
-    LOG(DEBUG) << __FUNCTION__;
-    return stopVideoStream_impl();
-}
-
-ScopedAStatus EvsMockCamera::stopVideoStream_impl() {
-    std::unique_lock lock(mAccessLock);
-
-    if (mStreamState != RUNNING) {
-        // Safely return here because a stream is not running.
-        return ScopedAStatus::ok();
-    }
-
-    // Tell the GenerateFrames loop we want it to stop
-    mStreamState = STOPPING;
-
-    // Block outside the mutex until the "stop" flag has been acknowledged
-    // We won't send any more frames, but the client might still get some already in flight
-    LOG(DEBUG) << "Waiting for stream thread to end...";
-    lock.unlock();
-    if (mCaptureThread.joinable()) {
-        mCaptureThread.join();
-    }
-    lock.lock();
-
-    mStreamState = STOPPED;
-    mStream = nullptr;
-    LOG(DEBUG) << "Stream marked STOPPED.";
-
-    return ScopedAStatus::ok();
-}
-
 ScopedAStatus EvsMockCamera::getExtendedInfo(int32_t opaqueIdentifier,
                                              std::vector<uint8_t>* opaqueValue) {
     const auto it = mExtInfo.find(opaqueIdentifier);
@@ -264,14 +118,6 @@
     return ScopedAStatus::ok();
 }
 
-ScopedAStatus EvsMockCamera::pauseVideoStream() {
-    return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
-}
-
-ScopedAStatus EvsMockCamera::resumeVideoStream() {
-    return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
-}
-
 ScopedAStatus EvsMockCamera::setPrimaryClient() {
     /* Because EVS HW module reference implementation expects a single client at
      * a time, this returns a success code always.
@@ -346,232 +192,27 @@
     return ScopedAStatus::ok();
 }
 
-ScopedAStatus EvsMockCamera::importExternalBuffers(const std::vector<BufferDesc>& buffers,
-                                                   int32_t* _aidl_return) {
-    size_t numBuffersToAdd = buffers.size();
-    if (numBuffersToAdd < 1) {
-        LOG(DEBUG) << "Ignoring a request to import external buffers with an empty list.";
-        return ScopedAStatus::ok();
-    }
-
-    std::lock_guard lock(mAccessLock);
-    if (numBuffersToAdd > (kMaxBuffersInFlight - mFramesAllowed)) {
-        numBuffersToAdd -= (kMaxBuffersInFlight - mFramesAllowed);
-        LOG(WARNING) << "Exceed the limit on the number of buffers. " << numBuffersToAdd
-                     << " buffers will be imported only.";
-    }
-
-    ::android::GraphicBufferMapper& mapper = ::android::GraphicBufferMapper::get();
-    const size_t before = mFramesAllowed;
-    for (size_t i = 0; i < numBuffersToAdd; ++i) {
-        auto& b = buffers[i];
-        const AHardwareBuffer_Desc* pDesc =
-                reinterpret_cast<const AHardwareBuffer_Desc*>(&b.buffer.description);
-
-        buffer_handle_t handleToImport = ::android::dupFromAidl(b.buffer.handle);
-        buffer_handle_t handleToStore = nullptr;
-        if (handleToImport == nullptr) {
-            LOG(WARNING) << "Failed to duplicate a memory handle. Ignoring a buffer " << b.bufferId;
-            continue;
-        }
-
-        ::android::status_t result =
-                mapper.importBuffer(handleToImport, pDesc->width, pDesc->height, pDesc->layers,
-                                    pDesc->format, pDesc->usage, pDesc->stride, &handleToStore);
-        if (result != ::android::NO_ERROR || handleToStore == nullptr) {
-            LOG(WARNING) << "Failed to import a buffer " << b.bufferId;
-            continue;
-        }
-
-        bool stored = false;
-        for (auto&& rec : mBuffers) {
-            if (rec.handle != nullptr) {
-                continue;
-            }
-
-            // Use this existing entry.
-            rec.handle = handleToStore;
-            rec.inUse = false;
-            stored = true;
-            break;
-        }
-
-        if (!stored) {
-            // Add a BufferRecord wrapping this handle to our set of available buffers.
-            mBuffers.push_back(BufferRecord(handleToStore));
-        }
-        ++mFramesAllowed;
-    }
-
-    *_aidl_return = mFramesAllowed - before;
-    return ScopedAStatus::ok();
-}
-
-bool EvsMockCamera::setAvailableFrames_Locked(unsigned bufferCount) {
-    if (bufferCount < 1) {
-        LOG(ERROR) << "Ignoring request to set buffer count to zero";
-        return false;
-    }
-    if (bufferCount > kMaxBuffersInFlight) {
-        LOG(ERROR) << "Rejecting buffer request in excess of internal limit";
-        return false;
-    }
-
-    // Is an increase required?
-    if (mFramesAllowed < bufferCount) {
-        // An increase is required
-        auto needed = bufferCount - mFramesAllowed;
-        LOG(INFO) << "Allocating " << needed << " buffers for camera frames";
-
-        auto added = increaseAvailableFrames_Locked(needed);
-        if (added != needed) {
-            // If we didn't add all the frames we needed, then roll back to the previous state
-            LOG(ERROR) << "Rolling back to previous frame queue size";
-            decreaseAvailableFrames_Locked(added);
-            return false;
-        }
-    } else if (mFramesAllowed > bufferCount) {
-        // A decrease is required
-        auto framesToRelease = mFramesAllowed - bufferCount;
-        LOG(INFO) << "Returning " << framesToRelease << " camera frame buffers";
-
-        auto released = decreaseAvailableFrames_Locked(framesToRelease);
-        if (released != framesToRelease) {
-            // This shouldn't happen with a properly behaving client because the client
-            // should only make this call after returning sufficient outstanding buffers
-            // to allow a clean resize.
-            LOG(ERROR) << "Buffer queue shrink failed -- too many buffers currently in use?";
-        }
-    }
-
-    return true;
-}
-
-unsigned EvsMockCamera::increaseAvailableFrames_Locked(unsigned numToAdd) {
-    // Acquire the graphics buffer allocator
-    ::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
-
-    unsigned added = 0;
-    while (added < numToAdd) {
-        unsigned pixelsPerLine = 0;
-        buffer_handle_t memHandle = nullptr;
-        auto result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage, &memHandle,
-                                     &pixelsPerLine, 0, "EvsMockCamera");
-        if (result != ::android::NO_ERROR) {
-            LOG(ERROR) << "Error " << result << " allocating " << mWidth << " x " << mHeight
-                       << " graphics buffer";
-            break;
-        }
-        if (memHandle == nullptr) {
-            LOG(ERROR) << "We didn't get a buffer handle back from the allocator";
-            break;
-        }
-        if (mStride > 0) {
-            if (mStride != pixelsPerLine) {
-                LOG(ERROR) << "We did not expect to get buffers with different strides!";
-            }
-        } else {
-            // Gralloc defines stride in terms of pixels per line
-            mStride = pixelsPerLine;
-        }
-
-        // Find a place to store the new buffer
-        auto stored = false;
-        for (auto&& rec : mBuffers) {
-            if (rec.handle == nullptr) {
-                // Use this existing entry
-                rec.handle = memHandle;
-                rec.inUse = false;
-                stored = true;
-                break;
-            }
-        }
-        if (!stored) {
-            // Add a BufferRecord wrapping this handle to our set of available buffers
-            mBuffers.push_back(BufferRecord(memHandle));
-        }
-
-        ++mFramesAllowed;
-        ++added;
-    }
-
-    return added;
-}
-
-unsigned EvsMockCamera::decreaseAvailableFrames_Locked(unsigned numToRemove) {
-    // Acquire the graphics buffer allocator
-    ::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
-
-    unsigned removed = 0;
-    for (auto&& rec : mBuffers) {
-        // Is this record not in use, but holding a buffer that we can free?
-        if ((rec.inUse == false) && (rec.handle != nullptr)) {
-            // Release buffer and update the record so we can recognize it as "empty"
-            alloc.free(rec.handle);
-            rec.handle = nullptr;
-
-            --mFramesAllowed;
-            ++removed;
-
-            if (removed == numToRemove) {
-                break;
-            }
-        }
-    }
-
-    return removed;
-}
-
 // This is the asynchronous frame generation thread that runs in parallel with the
 // main serving thread.  There is one for each active camera instance.
 void EvsMockCamera::generateFrames() {
     LOG(DEBUG) << "Frame generation loop started.";
 
-    unsigned idx = 0;
     while (true) {
-        bool timeForFrame = false;
         const nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
-
-        // Lock scope for updating shared state
+        std::size_t bufferId = kInvalidBufferID;
+        buffer_handle_t bufferHandle = nullptr;
         {
-            std::lock_guard lock(mAccessLock);
-
-            if (mStreamState != RUNNING) {
-                // Break out of our main thread loop
+            std::lock_guard lock(mMutex);
+            if (mStreamState != StreamState::RUNNING) {
                 break;
             }
-
-            // Are we allowed to issue another buffer?
-            if (mFramesInUse >= mFramesAllowed) {
-                // Can't do anything right now -- skip this frame
-                LOG(WARNING) << "Skipped a frame because too many are in flight.";
-            } else {
-                // Identify an available buffer to fill
-                for (idx = 0; idx < mBuffers.size(); idx++) {
-                    if (!mBuffers[idx].inUse) {
-                        if (mBuffers[idx].handle != nullptr) {
-                            // Found an available record, so stop looking
-                            break;
-                        }
-                    }
-                }
-                if (idx >= mBuffers.size()) {
-                    // This shouldn't happen since we already checked mFramesInUse vs mFramesAllowed
-                    ALOGE("Failed to find an available buffer slot\n");
-                } else {
-                    // We're going to make the frame busy
-                    mBuffers[idx].inUse = true;
-                    mFramesInUse++;
-                    timeForFrame = true;
-                }
-            }
+            std::tie(bufferId, bufferHandle) = useBuffer_unsafe();
         }
 
-        if (timeForFrame) {
+        if (bufferHandle != nullptr) {
             using AidlPixelFormat = ::aidl::android::hardware::graphics::common::PixelFormat;
 
             // Assemble the buffer description we'll transmit below
-            buffer_handle_t memHandle = mBuffers[idx].handle;
             BufferDesc newBuffer = {
                     .buffer =
                             {
@@ -584,39 +225,31 @@
                                                     .usage = static_cast<BufferUsage>(mUsage),
                                                     .stride = static_cast<int32_t>(mStride),
                                             },
-                                    .handle = ::android::dupToAidl(memHandle),
+                                    .handle = ::android::dupToAidl(bufferHandle),
                             },
-                    .bufferId = static_cast<int32_t>(idx),
+                    .bufferId = static_cast<int32_t>(bufferId),
                     .deviceId = mDescription.id,
                     .timestamp = static_cast<int64_t>(::android::elapsedRealtimeNano() *
                                                       1e+3),  // timestamps is in microseconds
             };
 
             // Write test data into the image buffer
-            fillMockFrame(memHandle, reinterpret_cast<const AHardwareBuffer_Desc*>(
-                                             &newBuffer.buffer.description));
+            fillMockFrame(bufferHandle, reinterpret_cast<const AHardwareBuffer_Desc*>(
+                                                &newBuffer.buffer.description));
+
+            std::vector<BufferDesc> frames;
+            frames.push_back(std::move(newBuffer));
 
             // Issue the (asynchronous) callback to the client -- can't be holding the lock
-            auto flag = false;
-            if (mStream) {
-                std::vector<BufferDesc> frames;
-                frames.push_back(std::move(newBuffer));
-                flag = mStream->deliverFrame(frames).isOk();
-            }
-
-            if (flag) {
-                LOG(DEBUG) << "Delivered " << memHandle << ", id = " << mBuffers[idx].handle;
+            if (mStream && mStream->deliverFrame(frames).isOk()) {
+                LOG(DEBUG) << "Delivered " << bufferHandle << ", id = " << bufferId;
             } else {
                 // This can happen if the client dies and is likely unrecoverable.
                 // To avoid consuming resources generating failing calls, we stop sending
                 // frames.  Note, however, that the stream remains in the "STREAMING" state
                 // until cleaned up on the main thread.
                 LOG(ERROR) << "Frame delivery call failed in the transport layer.";
-
-                // Since we didn't actually deliver it, mark the frame as available
-                std::lock_guard<std::mutex> lock(mAccessLock);
-                mBuffers[idx].inUse = false;
-                mFramesInUse--;
+                doneWithFrame(frames);
             }
         }
 
@@ -671,34 +304,45 @@
     mapper.unlock(handle);
 }
 
-void EvsMockCamera::returnBufferLocked(const uint32_t bufferId) {
-    if (bufferId >= mBuffers.size()) {
-        ALOGE("ignoring doneWithFrame called with invalid bufferId %d (max is %zu)", bufferId,
-              mBuffers.size() - 1);
-        return;
+::android::status_t EvsMockCamera::allocateOneFrame(buffer_handle_t* handle) {
+    static auto& alloc = ::android::GraphicBufferAllocator::get();
+    unsigned pixelsPerLine = 0;
+    const auto result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage, handle, &pixelsPerLine,
+                                       0, "EvsMockCamera");
+    if (mStride < mWidth) {
+        // Gralloc defines stride in terms of pixels per line
+        mStride = pixelsPerLine;
+    } else if (mStride != pixelsPerLine) {
+        LOG(ERROR) << "We did not expect to get buffers with different strides!";
     }
+    return result;
+}
 
-    if (!mBuffers[bufferId].inUse) {
-        ALOGE("ignoring doneWithFrame called on frame %d which is already free", bufferId);
-        return;
+bool EvsMockCamera::startVideoStreamImpl_locked(
+        const std::shared_ptr<evs::IEvsCameraStream>& receiver, ndk::ScopedAStatus& /* status */,
+        std::unique_lock<std::mutex>& /* lck */) {
+    mStream = receiver;
+    mCaptureThread = std::thread([this]() { generateFrames(); });
+    return true;
+}
+
+bool EvsMockCamera::stopVideoStreamImpl_locked(ndk::ScopedAStatus& /* status */,
+                                               std::unique_lock<std::mutex>& lck) {
+    lck.unlock();
+    if (mCaptureThread.joinable()) {
+        mCaptureThread.join();
     }
+    lck.lock();
+    return true;
+}
 
-    // Mark the frame as available
-    mBuffers[bufferId].inUse = false;
-    mFramesInUse--;
-
-    // If this frame's index is high in the array, try to move it down
-    // to improve locality after mFramesAllowed has been reduced.
-    if (bufferId >= mFramesAllowed) {
-        // Find an empty slot lower in the array (which should always exist in this case)
-        for (auto&& rec : mBuffers) {
-            if (rec.handle == nullptr) {
-                rec.handle = mBuffers[bufferId].handle;
-                mBuffers[bufferId].handle = nullptr;
-                break;
-            }
-        }
+bool EvsMockCamera::postVideoStreamStop_locked(ndk::ScopedAStatus& status,
+                                               std::unique_lock<std::mutex>& lck) {
+    if (!Base::postVideoStreamStop_locked(status, lck)) {
+        return false;
     }
+    mStream = nullptr;
+    return true;
 }
 
 std::shared_ptr<EvsMockCamera> EvsMockCamera::Create(const char* deviceName) {
diff --git a/automotive/evs/aidl/impl/default/src/EvsVideoEmulatedCamera.cpp b/automotive/evs/aidl/impl/default/src/EvsVideoEmulatedCamera.cpp
index f198a64..8181e47 100644
--- a/automotive/evs/aidl/impl/default/src/EvsVideoEmulatedCamera.cpp
+++ b/automotive/evs/aidl/impl/default/src/EvsVideoEmulatedCamera.cpp
@@ -18,17 +18,35 @@
 
 #include <aidl/android/hardware/automotive/evs/EvsResult.h>
 
+#include <aidlcommonsupport/NativeHandle.h>
 #include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <media/stagefright/MediaCodecConstants.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <utils/SystemClock.h>
 
+#include <fcntl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <chrono>
 #include <cstddef>
 #include <cstdint>
+#include <tuple>
+#include <utility>
 
 namespace aidl::android::hardware::automotive::evs::implementation {
 
+namespace {
+struct FormatDeleter {
+    void operator()(AMediaFormat* format) const { AMediaFormat_delete(format); }
+};
+}  // namespace
+
 EvsVideoEmulatedCamera::EvsVideoEmulatedCamera(Sigil, const char* deviceName,
                                                std::unique_ptr<ConfigManager::CameraInfo>& camInfo)
-    : mCameraInfo(camInfo) {
-    mDescription.id = deviceName;
+    : mVideoFileName(deviceName), mCameraInfo(camInfo) {
+    mDescription.id = mVideoFileName;
 
     /* set camera metadata */
     if (camInfo) {
@@ -40,6 +58,256 @@
     initializeParameters();
 }
 
+bool EvsVideoEmulatedCamera::initialize() {
+    // Open file.
+    mVideoFd = open(mVideoFileName.c_str(), 0, O_RDONLY);
+    if (mVideoFd < 0) {
+        PLOG(ERROR) << __func__ << ": Failed to open video file \"" << mVideoFileName << "\".";
+        return false;
+    }
+
+    // Initialize Media Extractor.
+    {
+        mVideoExtractor.reset(AMediaExtractor_new());
+        off64_t filesize = lseek64(mVideoFd, 0, SEEK_END);
+        lseek(mVideoFd, 0, SEEK_SET);
+        const media_status_t status =
+                AMediaExtractor_setDataSourceFd(mVideoExtractor.get(), mVideoFd, 0, filesize);
+        if (status != AMEDIA_OK) {
+            LOG(ERROR) << __func__
+                       << ": Received error when initializing media extractor. Error code: "
+                       << status << ".";
+            return false;
+        }
+    }
+
+    // Initialize Media Codec and file format.
+    std::unique_ptr<AMediaFormat, FormatDeleter> format;
+    const char* mime;
+    bool selected = false;
+    int numTracks = AMediaExtractor_getTrackCount(mVideoExtractor.get());
+    for (int i = 0; i < numTracks; i++) {
+        format.reset(AMediaExtractor_getTrackFormat(mVideoExtractor.get(), i));
+        if (!AMediaFormat_getString(format.get(), AMEDIAFORMAT_KEY_MIME, &mime)) {
+            LOG(ERROR) << __func__ << ": Error in fetching format string";
+            continue;
+        }
+        if (!::android::base::StartsWith(mime, "video/")) {
+            continue;
+        }
+        const media_status_t status = AMediaExtractor_selectTrack(mVideoExtractor.get(), i);
+        if (status != AMEDIA_OK) {
+            LOG(ERROR) << __func__
+                       << ": Media extractor returned error to select track. Error Code: " << status
+                       << ".";
+            return false;
+        }
+        selected = true;
+        break;
+    }
+    if (!selected) {
+        LOG(ERROR) << __func__ << ": No video track in video file \"" << mVideoFileName << "\".";
+        return false;
+    }
+
+    mVideoCodec.reset(AMediaCodec_createDecoderByType(mime));
+    if (!mVideoCodec) {
+        LOG(ERROR) << __func__ << ": Unable to create decoder.";
+        return false;
+    }
+
+    mDescription.vendorFlags = 0xFFFFFFFF;  // Arbitrary test value
+    mUsage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_CAMERA_WRITE |
+             GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY;
+    mFormat = HAL_PIXEL_FORMAT_YCBCR_420_888;
+    AMediaFormat_setInt32(format.get(), AMEDIAFORMAT_KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible);
+    {
+        const media_status_t status =
+                AMediaCodec_configure(mVideoCodec.get(), format.get(), nullptr, nullptr, 0);
+        if (status != AMEDIA_OK) {
+            LOG(ERROR) << __func__
+                       << ": Received error in configuring mCodec. Error code: " << status << ".";
+            return false;
+        }
+    }
+    format.reset(AMediaCodec_getOutputFormat(mVideoCodec.get()));
+    AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_WIDTH, &mWidth);
+    AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_HEIGHT, &mHeight);
+    return true;
+}
+
+void EvsVideoEmulatedCamera::generateFrames() {
+    while (true) {
+        {
+            std::lock_guard lock(mMutex);
+            if (mStreamState != StreamState::RUNNING) {
+                return;
+            }
+        }
+        renderOneFrame();
+    }
+}
+
+void EvsVideoEmulatedCamera::onCodecInputAvailable(const int32_t index) {
+    const size_t sampleSize = AMediaExtractor_getSampleSize(mVideoExtractor.get());
+    const int64_t presentationTime = AMediaExtractor_getSampleTime(mVideoExtractor.get());
+    size_t bufferSize = 0;
+    uint8_t* const codecInputBuffer =
+            AMediaCodec_getInputBuffer(mVideoCodec.get(), index, &bufferSize);
+    if (sampleSize > bufferSize) {
+        LOG(ERROR) << __func__ << ": Buffer is not large enough.";
+    }
+    if (presentationTime < 0) {
+        AMediaCodec_queueInputBuffer(mVideoCodec.get(), index, /* offset = */ 0,
+                                     /* size = */ 0, presentationTime,
+                                     AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM);
+        LOG(INFO) << __func__ << ": Reaching the end of stream.";
+        return;
+    }
+    const size_t readSize =
+            AMediaExtractor_readSampleData(mVideoExtractor.get(), codecInputBuffer, sampleSize);
+    const media_status_t status = AMediaCodec_queueInputBuffer(
+            mVideoCodec.get(), index, /*offset = */ 0, readSize, presentationTime, /* flags = */ 0);
+    if (status != AMEDIA_OK) {
+        LOG(ERROR) << __func__
+                   << ": Received error in queueing input buffer. Error code: " << status;
+    }
+}
+
+void EvsVideoEmulatedCamera::onCodecOutputAvailable(const int32_t index,
+                                                    const AMediaCodecBufferInfo& info) {
+    using std::chrono::duration_cast;
+    using std::chrono::microseconds;
+    using std::chrono::nanoseconds;
+    using AidlPixelFormat = ::aidl::android::hardware::graphics::common::PixelFormat;
+    using ::aidl::android::hardware::graphics::common::BufferUsage;
+
+    size_t decodedOutSize = 0;
+    uint8_t* const codecOutputBuffer =
+            AMediaCodec_getOutputBuffer(mVideoCodec.get(), index, &decodedOutSize) + info.offset;
+
+    std::size_t renderBufferId = static_cast<std::size_t>(-1);
+    buffer_handle_t renderBufferHandle = nullptr;
+    {
+        std::lock_guard lock(mMutex);
+        if (mStreamState != StreamState::RUNNING) {
+            return;
+        }
+        std::tie(renderBufferId, renderBufferHandle) = useBuffer_unsafe();
+    }
+    if (!renderBufferHandle) {
+        LOG(ERROR) << __func__ << ": Camera failed to get an available render buffer.";
+        return;
+    }
+    std::vector<BufferDesc> renderBufferDescs;
+    renderBufferDescs.push_back({
+            .buffer =
+                    {
+                            .description =
+                                    {
+                                            .width = static_cast<int32_t>(mWidth),
+                                            .height = static_cast<int32_t>(mHeight),
+                                            .layers = 1,
+                                            .format = static_cast<AidlPixelFormat>(mFormat),
+                                            .usage = static_cast<BufferUsage>(mUsage),
+                                            .stride = static_cast<int32_t>(mStride),
+                                    },
+                            .handle = ::android::dupToAidl(renderBufferHandle),
+                    },
+            .bufferId = static_cast<int32_t>(renderBufferId),
+            .deviceId = mDescription.id,
+            .timestamp = duration_cast<microseconds>(nanoseconds(::android::elapsedRealtimeNano()))
+                                 .count(),
+    });
+
+    // Lock our output buffer for writing
+    uint8_t* pixels = nullptr;
+    int32_t bytesPerStride = 0;
+    auto& mapper = ::android::GraphicBufferMapper::get();
+    mapper.lock(renderBufferHandle, GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER,
+                ::android::Rect(mWidth, mHeight), (void**)&pixels, nullptr, &bytesPerStride);
+
+    // If we failed to lock the pixel buffer, we're about to crash, but log it first
+    if (!pixels) {
+        LOG(ERROR) << __func__ << ": Camera failed to gain access to image buffer for writing";
+        return;
+    }
+
+    std::size_t ySize = mHeight * mStride;
+    std::size_t uvSize = ySize / 4;
+
+    std::memcpy(pixels, codecOutputBuffer, ySize);
+    pixels += ySize;
+
+    uint8_t* u_head = codecOutputBuffer + ySize;
+    uint8_t* v_head = u_head + uvSize;
+
+    for (size_t i = 0; i < uvSize; ++i) {
+        *(pixels++) = *(u_head++);
+        *(pixels++) = *(v_head++);
+    }
+
+    const auto status =
+            AMediaCodec_releaseOutputBuffer(mVideoCodec.get(), index, /* render = */ false);
+    if (status != AMEDIA_OK) {
+        LOG(ERROR) << __func__
+                   << ": Received error in releasing output buffer. Error code: " << status;
+    }
+
+    // Release our output buffer
+    mapper.unlock(renderBufferHandle);
+
+    // Issue the (asynchronous) callback to the client -- can't be holding the lock
+    if (mStream && mStream->deliverFrame(renderBufferDescs).isOk()) {
+        LOG(DEBUG) << __func__ << ": Delivered " << renderBufferHandle
+                   << ", id = " << renderBufferId;
+    } else {
+        // This can happen if the client dies and is likely unrecoverable.
+        // To avoid consuming resources generating failing calls, we stop sending
+        // frames.  Note, however, that the stream remains in the "STREAMING" state
+        // until cleaned up on the main thread.
+        LOG(ERROR) << __func__ << ": Frame delivery call failed in the transport layer.";
+        doneWithFrame(renderBufferDescs);
+    }
+}
+
+void EvsVideoEmulatedCamera::renderOneFrame() {
+    using std::chrono::duration_cast;
+    using std::chrono::microseconds;
+    using namespace std::chrono_literals;
+
+    // push to codec input
+    while (true) {
+        int codecInputBufferIdx =
+                AMediaCodec_dequeueInputBuffer(mVideoCodec.get(), /* timeoutUs = */ 0);
+        if (codecInputBufferIdx < 0) {
+            if (codecInputBufferIdx != AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+                LOG(ERROR) << __func__
+                           << ": Received error in AMediaCodec_dequeueInputBuffer. Error code: "
+                           << codecInputBufferIdx;
+            }
+            break;
+        }
+        onCodecInputAvailable(codecInputBufferIdx);
+        AMediaExtractor_advance(mVideoExtractor.get());
+    }
+
+    // pop from codec output
+
+    AMediaCodecBufferInfo info;
+    int codecOutputputBufferIdx = AMediaCodec_dequeueOutputBuffer(
+            mVideoCodec.get(), &info, /* timeoutUs = */ duration_cast<microseconds>(1ms).count());
+    if (codecOutputputBufferIdx < 0) {
+        if (codecOutputputBufferIdx != AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+            LOG(ERROR) << __func__
+                       << ": Received error in AMediaCodec_dequeueOutputBuffer. Error code: "
+                       << codecOutputputBufferIdx;
+        }
+        return;
+    }
+    onCodecOutputAvailable(codecOutputputBufferIdx, info);
+}
+
 void EvsVideoEmulatedCamera::initializeParameters() {
     mParams.emplace(
             CameraParam::BRIGHTNESS,
@@ -52,22 +320,54 @@
             new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255));
 }
 
-::android::status_t EvsVideoEmulatedCamera::allocateOneFrame(buffer_handle_t* /* handle */) {
-    LOG(FATAL) << __func__ << ": Not implemented yet.";
-    return ::android::UNKNOWN_ERROR;
+::android::status_t EvsVideoEmulatedCamera::allocateOneFrame(buffer_handle_t* handle) {
+    static auto& alloc = ::android::GraphicBufferAllocator::get();
+    unsigned pixelsPerLine = 0;
+    const auto result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage, handle, &pixelsPerLine,
+                                       0, "EvsVideoEmulatedCamera");
+    if (mStride == 0) {
+        // Gralloc defines stride in terms of pixels per line
+        mStride = pixelsPerLine;
+    } else if (mStride != pixelsPerLine) {
+        LOG(ERROR) << "We did not expect to get buffers with different strides!";
+    }
+    return result;
 }
 
 bool EvsVideoEmulatedCamera::startVideoStreamImpl_locked(
-        const std::shared_ptr<evs::IEvsCameraStream>& /* receiver */,
-        ndk::ScopedAStatus& /* status */, std::unique_lock<std::mutex>& /* lck */) {
-    LOG(FATAL) << __func__ << ": Not implemented yet.";
-    return false;
+        const std::shared_ptr<evs::IEvsCameraStream>& receiver, ndk::ScopedAStatus& /* status */,
+        std::unique_lock<std::mutex>& /* lck */) {
+    mStream = receiver;
+
+    const media_status_t status = AMediaCodec_start(mVideoCodec.get());
+    if (status != AMEDIA_OK) {
+        LOG(ERROR) << __func__ << ": Received error in starting decoder. Error code: " << status
+                   << ".";
+        return false;
+    }
+    mCaptureThread = std::thread([this]() { generateFrames(); });
+
+    return true;
 }
 
 bool EvsVideoEmulatedCamera::stopVideoStreamImpl_locked(ndk::ScopedAStatus& /* status */,
-                                                        std::unique_lock<std::mutex>& /* lck */) {
-    LOG(FATAL) << __func__ << ": Not implemented yet.";
-    return false;
+                                                        std::unique_lock<std::mutex>& lck) {
+    const media_status_t status = AMediaCodec_stop(mVideoCodec.get());
+    lck.unlock();
+    if (mCaptureThread.joinable()) {
+        mCaptureThread.join();
+    }
+    lck.lock();
+    return status == AMEDIA_OK;
+}
+
+bool EvsVideoEmulatedCamera::postVideoStreamStop_locked(ndk::ScopedAStatus& status,
+                                                        std::unique_lock<std::mutex>& lck) {
+    if (!Base::postVideoStreamStop_locked(status, lck)) {
+        return false;
+    }
+    mStream = nullptr;
+    return true;
 }
 
 ndk::ScopedAStatus EvsVideoEmulatedCamera::forcePrimaryClient(
@@ -189,10 +489,19 @@
         LOG(ERROR) << "Failed to instantiate EvsVideoEmulatedCamera.";
         return nullptr;
     }
-    c->mDescription.vendorFlags = 0xFFFFFFFF;  // Arbitrary test value
+    if (!c->initialize()) {
+        LOG(ERROR) << "Failed to initialize EvsVideoEmulatedCamera.";
+        return nullptr;
+    }
     return c;
 }
 
-EvsVideoEmulatedCamera::~EvsVideoEmulatedCamera() {}
+void EvsVideoEmulatedCamera::shutdown() {
+    mVideoCodec.reset();
+    mVideoExtractor.reset();
+    close(mVideoFd);
+    mVideoFd = 0;
+    Base::shutdown();
+}
 
 }  // namespace aidl::android::hardware::automotive::evs::implementation
diff --git a/automotive/evs/aidl/impl/default/tests/EvsCameraBufferTest.cpp b/automotive/evs/aidl/impl/default/tests/EvsCameraBufferTest.cpp
index 3cbc04a..8b4676e 100644
--- a/automotive/evs/aidl/impl/default/tests/EvsCameraBufferTest.cpp
+++ b/automotive/evs/aidl/impl/default/tests/EvsCameraBufferTest.cpp
@@ -134,6 +134,7 @@
         for (std::size_t i = 0; i < kNumOfHandles; ++i) {
             const auto [id, handle] = evsCam->useBuffer_unsafe();
             const std::size_t handleInt = reinterpret_cast<std::size_t>(handle);
+            EXPECT_TRUE(EvsCamera::IsBufferIDValid(id));
             EXPECT_NE(handle, nullptr);
             EXPECT_LT(id, kNumOfHandles);
 
@@ -160,6 +161,7 @@
         for (std::size_t i = 0; i < kNumOfHandles; ++i) {
             const auto [id, handle] = evsCam->useBuffer_unsafe();
             const std::size_t handleInt = reinterpret_cast<std::size_t>(handle);
+            EXPECT_TRUE(EvsCamera::IsBufferIDValid(id));
             EXPECT_NE(handle, nullptr);
             EXPECT_LT(id, kNumOfHandles);
 
@@ -189,6 +191,7 @@
         for (std::size_t i = 0; i < kNumOfHandles; ++i) {
             const auto [id, handle] = evsCam->useBuffer_unsafe();
             const std::size_t handleInt = reinterpret_cast<std::size_t>(handle);
+            EXPECT_TRUE(EvsCamera::IsBufferIDValid(id));
             EXPECT_NE(handle, nullptr);
             EXPECT_LT(id, kNumOfHandles);
 
diff --git a/automotive/remoteaccess/hal/default/Android.bp b/automotive/remoteaccess/hal/default/Android.bp
index 70dba30..97ed2c1 100644
--- a/automotive/remoteaccess/hal/default/Android.bp
+++ b/automotive/remoteaccess/hal/default/Android.bp
@@ -47,17 +47,6 @@
     ],
 }
 
-// TODO(b/295393732): remove this once we finish the migration from V1 to V2.
-cc_binary {
-    name: "android.hardware.automotive.remoteaccess@V1-default-service",
-    defaults: ["remote-access-hal-defaults"],
-    vintf_fragments: ["remoteaccess-default-service-v1.xml"],
-    init_rc: ["remoteaccess-default-service-v1.rc"],
-    cflags: [
-        "-DGRPC_SERVICE_ADDRESS=\"10.0.2.2:50051\"",
-    ],
-}
-
 cc_binary {
     name: "android.hardware.automotive.remoteaccess@V2-default-service",
     defaults: ["remote-access-hal-defaults"],
@@ -68,18 +57,6 @@
     ],
 }
 
-// TODO(b/295393732): remove this once we finish the migration from V1 to V2.
-cc_binary {
-    name: "android.hardware.automotive.remoteaccess@V1-tcu-test-service",
-    defaults: ["remote-access-hal-defaults"],
-    vintf_fragments: ["remoteaccess-default-service-v1.xml"],
-    init_rc: ["remoteaccess-tcu-test-service-v1.rc"],
-    cflags: [
-        "-DGRPC_SERVICE_ADDRESS=\"10.10.10.1:50051\"",
-        "-DGRPC_SERVICE_IFNAME=\"eth1\"",
-    ],
-}
-
 cc_binary {
     name: "android.hardware.automotive.remoteaccess@V2-tcu-test-service",
     defaults: ["remote-access-hal-defaults"],
diff --git a/automotive/remoteaccess/hal/default/fuzzer/fuzzer.cpp b/automotive/remoteaccess/hal/default/fuzzer/fuzzer.cpp
index 292c80e..9224ebc 100644
--- a/automotive/remoteaccess/hal/default/fuzzer/fuzzer.cpp
+++ b/automotive/remoteaccess/hal/default/fuzzer/fuzzer.cpp
@@ -55,6 +55,31 @@
         return Status::OK;
     }
 
+    Status ScheduleTask(ClientContext* context, const ScheduleTaskRequest& request,
+                        ScheduleTaskResponse* response) {
+        return Status::OK;
+    }
+
+    Status UnscheduleTask(ClientContext* context, const UnscheduleTaskRequest& request,
+                          UnscheduleTaskResponse* response) {
+        return Status::OK;
+    }
+
+    Status UnscheduleAllTasks(ClientContext* context, const UnscheduleAllTasksRequest& request,
+                              UnscheduleAllTasksResponse* response) {
+        return Status::OK;
+    }
+
+    Status IsTaskScheduled(ClientContext* context, const IsTaskScheduledRequest& request,
+                           IsTaskScheduledResponse* response) {
+        return Status::OK;
+    }
+
+    Status GetAllScheduledTasks(ClientContext* context, const GetAllScheduledTasksRequest& request,
+                                GetAllScheduledTasksResponse* response) {
+        return Status::OK;
+    }
+
     // Async methods which we do not care.
     ClientAsyncReaderInterface<GetRemoteTasksResponse>* AsyncGetRemoteTasksRaw(
             [[maybe_unused]] ClientContext* context,
@@ -83,6 +108,76 @@
                                         [[maybe_unused]] CompletionQueue* c) {
         return nullptr;
     }
+
+    ClientAsyncResponseReaderInterface<ScheduleTaskResponse>* AsyncScheduleTaskRaw(
+            [[maybe_unused]] ClientContext* context,
+            [[maybe_unused]] const ScheduleTaskRequest& request,
+            [[maybe_unused]] CompletionQueue* cq) {
+        return nullptr;
+    }
+
+    ClientAsyncResponseReaderInterface<ScheduleTaskResponse>* PrepareAsyncScheduleTaskRaw(
+            [[maybe_unused]] ClientContext* context,
+            [[maybe_unused]] const ScheduleTaskRequest& request,
+            [[maybe_unused]] CompletionQueue* c) {
+        return nullptr;
+    }
+
+    ClientAsyncResponseReaderInterface<UnscheduleTaskResponse>* AsyncUnscheduleTaskRaw(
+            [[maybe_unused]] ClientContext* context,
+            [[maybe_unused]] const UnscheduleTaskRequest& request,
+            [[maybe_unused]] CompletionQueue* cq) {
+        return nullptr;
+    }
+
+    ClientAsyncResponseReaderInterface<UnscheduleTaskResponse>* PrepareAsyncUnscheduleTaskRaw(
+            [[maybe_unused]] ClientContext* context,
+            [[maybe_unused]] const UnscheduleTaskRequest& request,
+            [[maybe_unused]] CompletionQueue* c) {
+        return nullptr;
+    }
+
+    ClientAsyncResponseReaderInterface<UnscheduleAllTasksResponse>* AsyncUnscheduleAllTasksRaw(
+            [[maybe_unused]] ClientContext* context,
+            [[maybe_unused]] const UnscheduleAllTasksRequest& request,
+            [[maybe_unused]] CompletionQueue* cq) {
+        return nullptr;
+    }
+
+    ClientAsyncResponseReaderInterface<UnscheduleAllTasksResponse>*
+    PrepareAsyncUnscheduleAllTasksRaw([[maybe_unused]] ClientContext* context,
+                                      [[maybe_unused]] const UnscheduleAllTasksRequest& request,
+                                      [[maybe_unused]] CompletionQueue* c) {
+        return nullptr;
+    }
+
+    ClientAsyncResponseReaderInterface<IsTaskScheduledResponse>* AsyncIsTaskScheduledRaw(
+            [[maybe_unused]] ClientContext* context,
+            [[maybe_unused]] const IsTaskScheduledRequest& request,
+            [[maybe_unused]] CompletionQueue* cq) {
+        return nullptr;
+    }
+
+    ClientAsyncResponseReaderInterface<IsTaskScheduledResponse>* PrepareAsyncIsTaskScheduledRaw(
+            [[maybe_unused]] ClientContext* context,
+            [[maybe_unused]] const IsTaskScheduledRequest& request,
+            [[maybe_unused]] CompletionQueue* c) {
+        return nullptr;
+    }
+
+    ClientAsyncResponseReaderInterface<GetAllScheduledTasksResponse>* AsyncGetAllScheduledTasksRaw(
+            [[maybe_unused]] ClientContext* context,
+            [[maybe_unused]] const GetAllScheduledTasksRequest& request,
+            [[maybe_unused]] CompletionQueue* cq) {
+        return nullptr;
+    }
+
+    ClientAsyncResponseReaderInterface<GetAllScheduledTasksResponse>*
+    PrepareAsyncGetAllScheduledTasksRaw([[maybe_unused]] ClientContext* context,
+                                        [[maybe_unused]] const GetAllScheduledTasksRequest& request,
+                                        [[maybe_unused]] CompletionQueue* c) {
+        return nullptr;
+    }
 };
 
 }  // namespace remoteaccess
diff --git a/automotive/remoteaccess/hal/default/proto/wakeup_client.proto b/automotive/remoteaccess/hal/default/proto/wakeup_client.proto
index 4fe0d01..e061016 100644
--- a/automotive/remoteaccess/hal/default/proto/wakeup_client.proto
+++ b/automotive/remoteaccess/hal/default/proto/wakeup_client.proto
@@ -18,6 +18,12 @@
 
 package android.hardware.automotive.remoteaccess;
 
+enum ErrorCode {
+    OK = 0;
+    UNSPECIFIED = 1;
+    INVALID_ARG = 2;
+}
+
 /**
  * Service provided by a wakeup client running on TCU.
  */
@@ -50,6 +56,50 @@
      * to wake up AP.
      */
     rpc NotifyWakeupRequired(NotifyWakeupRequiredRequest) returns (NotifyWakeupRequiredResponse) {}
+
+    /**
+     * Schedules a task to be executed later even when the vehicle is off.
+     *
+     * <p>This sends a scheduled task message to a device external to Android so that the device
+     * can wake up Android and deliver the task through {@link IRemoteTaskCallback}.
+     *
+     * <p>Note that the scheduled task execution is on a best-effort basis. Multiple situations
+     * might cause the task not to execute successfully:
+     *
+     * <ul>
+     * <li>The vehicle is low on battery and the other device decides not to wake up Android.
+     * <li>User turns off vehicle while the task is executing.
+     * <li>The task logic itself fails.
+     *
+     * <p>Must return a response with error code: {@code INVALID_ARG} if a pending schedule with the
+     * same {@code scheduleId} for this client exists.
+     */
+    rpc ScheduleTask(ScheduleTaskRequest) returns (ScheduleTaskResponse) {}
+
+    /**
+     * Unschedules a scheduled task.
+     *
+     * <p>Does nothing if a pending schedule with {@code clientId} and {@code scheduleId} does not
+     * exist.
+     */
+    rpc UnscheduleTask(UnscheduleTaskRequest) returns (UnscheduleTaskResponse) {}
+
+    /**
+     * Unschedules all scheduled tasks for the client.
+     */
+    rpc UnscheduleAllTasks(UnscheduleAllTasksRequest) returns (UnscheduleAllTasksResponse) {}
+
+    /**
+     * Returns whether the specified task is scheduled.
+     */
+    rpc IsTaskScheduled(IsTaskScheduledRequest) returns (IsTaskScheduledResponse) {}
+
+    /**
+     * Gets all pending scheduled tasks for the client.
+     *
+     * <p>The finished scheduled tasks will not be included.
+     */
+    rpc GetAllScheduledTasks(GetAllScheduledTasksRequest) returns (GetAllScheduledTasksResponse) {}
 }
 
 message GetRemoteTasksRequest {}
@@ -64,3 +114,50 @@
 }
 
 message NotifyWakeupRequiredResponse {}
+
+message ScheduleTaskRequest {
+    GrpcScheduleInfo scheduleInfo = 1;
+}
+
+message ScheduleTaskResponse {
+    ErrorCode errorCode = 1;
+}
+
+message GrpcScheduleInfo {
+    string clientId = 1;
+    string scheduleId = 2;
+    bytes data = 3;
+    int32 count = 4;
+    int64 startTimeInEpochSeconds = 5;
+    int64 periodicInSeconds = 6;
+}
+
+message UnscheduleTaskRequest {
+    string clientId = 1;
+    string scheduleId = 2;
+}
+
+message UnscheduleTaskResponse {}
+
+message UnscheduleAllTasksRequest {
+    string clientId = 1;
+}
+
+message UnscheduleAllTasksResponse {}
+
+message IsTaskScheduledRequest {
+    string clientId = 1;
+    string scheduleId = 2;
+}
+
+message IsTaskScheduledResponse {
+    bool isTaskScheduled = 1;
+}
+
+message GetAllScheduledTasksRequest {
+    string clientId = 1;
+}
+
+message GetAllScheduledTasksResponse {
+    repeated GrpcScheduleInfo allScheduledTasks = 1;
+}
diff --git a/automotive/remoteaccess/hal/default/remoteaccess-default-service-v1.rc b/automotive/remoteaccess/hal/default/remoteaccess-default-service-v1.rc
deleted file mode 100644
index b7a9cdc..0000000
--- a/automotive/remoteaccess/hal/default/remoteaccess-default-service-v1.rc
+++ /dev/null
@@ -1,4 +0,0 @@
-service vendor.remoteaccess-default /vendor/bin/hw/android.hardware.automotive.remoteaccess@V1-default-service
-    class hal
-    user vehicle_network
-    group system inet
diff --git a/automotive/remoteaccess/hal/default/remoteaccess-default-service-v1.xml b/automotive/remoteaccess/hal/default/remoteaccess-default-service-v1.xml
deleted file mode 100644
index d050a1b..0000000
--- a/automotive/remoteaccess/hal/default/remoteaccess-default-service-v1.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<manifest version="1.0" type="device">
-    <hal format="aidl">
-        <name>android.hardware.automotive.remoteaccess</name>
-        <version>1</version>
-        <fqname>IRemoteAccess/default</fqname>
-    </hal>
-</manifest>
diff --git a/automotive/remoteaccess/hal/default/remoteaccess-tcu-test-service-v1.rc b/automotive/remoteaccess/hal/default/remoteaccess-tcu-test-service-v1.rc
deleted file mode 100644
index 59315eb..0000000
--- a/automotive/remoteaccess/hal/default/remoteaccess-tcu-test-service-v1.rc
+++ /dev/null
@@ -1,5 +0,0 @@
-service vendor.remoteaccess-default /vendor/bin/hw/android.hardware.automotive.remoteaccess@V1-tcu-test-service
-    class hal
-    user vehicle_network
-    group system inet
-    capabilities NET_RAW
diff --git a/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp b/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
index 7721bf4..0944d86 100644
--- a/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
+++ b/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
@@ -314,38 +314,106 @@
     return ScopedAStatus::ok();
 }
 
-ScopedAStatus RemoteAccessService::isTaskScheduleSupported([[maybe_unused]] bool* out) {
-    // TODO(b/297271235): implement this.
+ScopedAStatus RemoteAccessService::isTaskScheduleSupported(bool* out) {
+    *out = true;
     return ScopedAStatus::ok();
 }
 
-ScopedAStatus RemoteAccessService::scheduleTask([[maybe_unused]] const ScheduleInfo& scheduleInfo) {
-    // TODO(b/297271235): implement this.
+ScopedAStatus RemoteAccessService::scheduleTask(const ScheduleInfo& scheduleInfo) {
+    ClientContext context;
+    ScheduleTaskRequest request = {};
+    ScheduleTaskResponse response = {};
+    request.mutable_scheduleinfo()->set_clientid(scheduleInfo.clientId);
+    request.mutable_scheduleinfo()->set_scheduleid(scheduleInfo.scheduleId);
+    request.mutable_scheduleinfo()->set_data(scheduleInfo.taskData.data(),
+                                             scheduleInfo.taskData.size());
+    request.mutable_scheduleinfo()->set_count(scheduleInfo.count);
+    request.mutable_scheduleinfo()->set_starttimeinepochseconds(
+            scheduleInfo.startTimeInEpochSeconds);
+    request.mutable_scheduleinfo()->set_periodicinseconds(scheduleInfo.periodicInSeconds);
+    Status status = mGrpcStub->ScheduleTask(&context, request, &response);
+    if (!status.ok()) {
+        return rpcStatusToScopedAStatus(status, "Failed to call ScheduleTask");
+    }
+    int errorCode = response.errorcode();
+    switch (errorCode) {
+        case ErrorCode::OK:
+            return ScopedAStatus::ok();
+        case ErrorCode::INVALID_ARG:
+            return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+        default:
+            // Should not happen.
+            return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                    -1, ("Got unknown error code: " + ErrorCode_Name(errorCode) +
+                         " from remote access HAL")
+                                .c_str());
+    }
+}
+
+ScopedAStatus RemoteAccessService::unscheduleTask(const std::string& clientId,
+                                                  const std::string& scheduleId) {
+    ClientContext context;
+    UnscheduleTaskRequest request = {};
+    UnscheduleTaskResponse response = {};
+    request.set_clientid(clientId);
+    request.set_scheduleid(scheduleId);
+    Status status = mGrpcStub->UnscheduleTask(&context, request, &response);
+    if (!status.ok()) {
+        return rpcStatusToScopedAStatus(status, "Failed to call UnscheduleTask");
+    }
     return ScopedAStatus::ok();
 }
 
-ScopedAStatus RemoteAccessService::unscheduleTask([[maybe_unused]] const std::string& clientId,
-                                                  [[maybe_unused]] const std::string& scheduleId) {
-    // TODO(b/297271235): implement this.
+ScopedAStatus RemoteAccessService::unscheduleAllTasks(const std::string& clientId) {
+    ClientContext context;
+    UnscheduleAllTasksRequest request = {};
+    UnscheduleAllTasksResponse response = {};
+    request.set_clientid(clientId);
+    Status status = mGrpcStub->UnscheduleAllTasks(&context, request, &response);
+    if (!status.ok()) {
+        return rpcStatusToScopedAStatus(status, "Failed to call UnscheduleAllTasks");
+    }
     return ScopedAStatus::ok();
 }
 
-ScopedAStatus RemoteAccessService::unscheduleAllTasks(
-        [[maybe_unused]] const std::string& clientId) {
-    // TODO(b/297271235): implement this.
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus RemoteAccessService::isTaskScheduled([[maybe_unused]] const std::string& clientId,
-                                                   [[maybe_unused]] const std::string& scheduleId,
-                                                   [[maybe_unused]] bool* out) {
-    // TODO(b/297271235): implement this.
+ScopedAStatus RemoteAccessService::isTaskScheduled(const std::string& clientId,
+                                                   const std::string& scheduleId, bool* out) {
+    ClientContext context;
+    IsTaskScheduledRequest request = {};
+    IsTaskScheduledResponse response = {};
+    request.set_clientid(clientId);
+    request.set_scheduleid(scheduleId);
+    Status status = mGrpcStub->IsTaskScheduled(&context, request, &response);
+    if (!status.ok()) {
+        return rpcStatusToScopedAStatus(status, "Failed to call isTaskScheduled");
+    }
+    *out = response.istaskscheduled();
     return ScopedAStatus::ok();
 }
 
 ScopedAStatus RemoteAccessService::getAllScheduledTasks(const std::string& clientId,
                                                         std::vector<ScheduleInfo>* out) {
-    // TODO(b/297271235): implement this.
+    ClientContext context;
+    GetAllScheduledTasksRequest request = {};
+    GetAllScheduledTasksResponse response = {};
+    request.set_clientid(clientId);
+    Status status = mGrpcStub->GetAllScheduledTasks(&context, request, &response);
+    if (!status.ok()) {
+        return rpcStatusToScopedAStatus(status, "Failed to call isTaskScheduled");
+    }
+    out->clear();
+    for (int i = 0; i < response.allscheduledtasks_size(); i++) {
+        const GrpcScheduleInfo& rpcScheduleInfo = response.allscheduledtasks(i);
+        ScheduleInfo scheduleInfo = {
+                .clientId = rpcScheduleInfo.clientid(),
+                .scheduleId = rpcScheduleInfo.scheduleid(),
+                .taskData = stringToBytes(rpcScheduleInfo.data()),
+                .count = rpcScheduleInfo.count(),
+                .startTimeInEpochSeconds = rpcScheduleInfo.starttimeinepochseconds(),
+                .periodicInSeconds = rpcScheduleInfo.periodicinseconds(),
+        };
+        out->push_back(std::move(scheduleInfo));
+    }
     return ScopedAStatus::ok();
 }
 
diff --git a/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp b/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp
index c5afd63..c0038c2 100644
--- a/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp
+++ b/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp
@@ -21,6 +21,7 @@
 #include <aidl/android/hardware/automotive/remoteaccess/ApState.h>
 #include <aidl/android/hardware/automotive/remoteaccess/BnRemoteTaskCallback.h>
 #include <aidl/android/hardware/automotive/vehicle/VehiclePropValue.h>
+#include <android/binder_status.h>
 #include <gmock/gmock.h>
 #include <grpcpp/test/mock_stream.h>
 #include <gtest/gtest.h>
@@ -46,6 +47,7 @@
 
 using ::aidl::android::hardware::automotive::remoteaccess::ApState;
 using ::aidl::android::hardware::automotive::remoteaccess::BnRemoteTaskCallback;
+using ::aidl::android::hardware::automotive::remoteaccess::ScheduleInfo;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
 
 using ::grpc::ClientAsyncReaderInterface;
@@ -63,6 +65,12 @@
 using ::testing::SetArgPointee;
 
 constexpr char kTestVin[] = "test_VIN";
+const std::string kTestClientId = "test client id";
+const std::string kTestScheduleId = "test schedule id";
+const std::vector<uint8_t> kTestData = {0xde, 0xad, 0xbe, 0xef};
+constexpr int32_t kTestCount = 1234;
+constexpr int64_t kTestStartTimeInEpochSeconds = 2345;
+constexpr int64_t kTestPeriodicInSeconds = 123;
 
 }  // namespace
 
@@ -73,6 +81,21 @@
     MOCK_METHOD(Status, NotifyWakeupRequired,
                 (ClientContext * context, const NotifyWakeupRequiredRequest& request,
                  NotifyWakeupRequiredResponse* response));
+    MOCK_METHOD(Status, ScheduleTask,
+                (ClientContext * context, const ScheduleTaskRequest& request,
+                 ScheduleTaskResponse* response));
+    MOCK_METHOD(Status, UnscheduleTask,
+                (ClientContext * context, const UnscheduleTaskRequest& request,
+                 UnscheduleTaskResponse* response));
+    MOCK_METHOD(Status, UnscheduleAllTasks,
+                (ClientContext * context, const UnscheduleAllTasksRequest& request,
+                 UnscheduleAllTasksResponse* response));
+    MOCK_METHOD(Status, IsTaskScheduled,
+                (ClientContext * context, const IsTaskScheduledRequest& request,
+                 IsTaskScheduledResponse* response));
+    MOCK_METHOD(Status, GetAllScheduledTasks,
+                (ClientContext * context, const GetAllScheduledTasksRequest& request,
+                 GetAllScheduledTasksResponse* response));
     // Async methods which we do not care.
     MOCK_METHOD(ClientAsyncReaderInterface<GetRemoteTasksResponse>*, AsyncGetRemoteTasksRaw,
                 (ClientContext * context, const GetRemoteTasksRequest& request, CompletionQueue* cq,
@@ -88,6 +111,42 @@
                 PrepareAsyncNotifyWakeupRequiredRaw,
                 (ClientContext * context, const NotifyWakeupRequiredRequest& request,
                  CompletionQueue* cq));
+    MOCK_METHOD(ClientAsyncResponseReaderInterface<ScheduleTaskResponse>*, AsyncScheduleTaskRaw,
+                (ClientContext * context, const ScheduleTaskRequest& request, CompletionQueue* cq));
+    MOCK_METHOD(ClientAsyncResponseReaderInterface<ScheduleTaskResponse>*,
+                PrepareAsyncScheduleTaskRaw,
+                (ClientContext * context, const ScheduleTaskRequest& request, CompletionQueue* cq));
+    MOCK_METHOD(ClientAsyncResponseReaderInterface<UnscheduleTaskResponse>*, AsyncUnscheduleTaskRaw,
+                (ClientContext * context, const UnscheduleTaskRequest& request,
+                 CompletionQueue* cq));
+    MOCK_METHOD(ClientAsyncResponseReaderInterface<UnscheduleTaskResponse>*,
+                PrepareAsyncUnscheduleTaskRaw,
+                (ClientContext * context, const UnscheduleTaskRequest& request,
+                 CompletionQueue* cq));
+    MOCK_METHOD(ClientAsyncResponseReaderInterface<UnscheduleAllTasksResponse>*,
+                AsyncUnscheduleAllTasksRaw,
+                (ClientContext * context, const UnscheduleAllTasksRequest& request,
+                 CompletionQueue* cq));
+    MOCK_METHOD(ClientAsyncResponseReaderInterface<UnscheduleAllTasksResponse>*,
+                PrepareAsyncUnscheduleAllTasksRaw,
+                (ClientContext * context, const UnscheduleAllTasksRequest& request,
+                 CompletionQueue* cq));
+    MOCK_METHOD(ClientAsyncResponseReaderInterface<IsTaskScheduledResponse>*,
+                AsyncIsTaskScheduledRaw,
+                (ClientContext * context, const IsTaskScheduledRequest& request,
+                 CompletionQueue* cq));
+    MOCK_METHOD(ClientAsyncResponseReaderInterface<IsTaskScheduledResponse>*,
+                PrepareAsyncIsTaskScheduledRaw,
+                (ClientContext * context, const IsTaskScheduledRequest& request,
+                 CompletionQueue* cq));
+    MOCK_METHOD(ClientAsyncResponseReaderInterface<GetAllScheduledTasksResponse>*,
+                AsyncGetAllScheduledTasksRaw,
+                (ClientContext * context, const GetAllScheduledTasksRequest& request,
+                 CompletionQueue* cq));
+    MOCK_METHOD(ClientAsyncResponseReaderInterface<GetAllScheduledTasksResponse>*,
+                PrepareAsyncGetAllScheduledTasksRaw,
+                (ClientContext * context, const GetAllScheduledTasksRequest& request,
+                 CompletionQueue* cq));
 };
 
 class FakeVhalClient final : public android::frameworks::automotive::vhal::IVhalClient {
@@ -367,6 +426,174 @@
     ASSERT_EQ(vehicleId, kTestVin);
 }
 
+TEST_F(RemoteAccessServiceUnitTest, TestIsTaskScheduleSupported) {
+    bool out = false;
+    ScopedAStatus status = getService()->isTaskScheduleSupported(&out);
+
+    EXPECT_TRUE(status.isOk());
+    EXPECT_TRUE(out);
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestScheduleTask) {
+    ScheduleTaskRequest grpcRequest = {};
+    EXPECT_CALL(*getGrpcWakeupClientStub(), ScheduleTask)
+            .WillOnce([&grpcRequest]([[maybe_unused]] ClientContext* context,
+                                     const ScheduleTaskRequest& request,
+                                     [[maybe_unused]] ScheduleTaskResponse* response) {
+                grpcRequest = request;
+                return Status();
+            });
+    ScheduleInfo scheduleInfo = {
+            .clientId = kTestClientId,
+            .scheduleId = kTestScheduleId,
+            .taskData = kTestData,
+            .count = kTestCount,
+            .startTimeInEpochSeconds = kTestStartTimeInEpochSeconds,
+            .periodicInSeconds = kTestPeriodicInSeconds,
+    };
+
+    ScopedAStatus status = getService()->scheduleTask(scheduleInfo);
+
+    ASSERT_TRUE(status.isOk());
+    EXPECT_EQ(grpcRequest.scheduleinfo().clientid(), kTestClientId);
+    EXPECT_EQ(grpcRequest.scheduleinfo().scheduleid(), kTestScheduleId);
+    EXPECT_EQ(grpcRequest.scheduleinfo().data(), std::string(kTestData.begin(), kTestData.end()));
+    EXPECT_EQ(grpcRequest.scheduleinfo().count(), kTestCount);
+    EXPECT_EQ(grpcRequest.scheduleinfo().starttimeinepochseconds(), kTestStartTimeInEpochSeconds);
+    EXPECT_EQ(grpcRequest.scheduleinfo().periodicinseconds(), kTestPeriodicInSeconds);
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestScheduleTask_InvalidArg) {
+    EXPECT_CALL(*getGrpcWakeupClientStub(), ScheduleTask)
+            .WillOnce([]([[maybe_unused]] ClientContext* context,
+                         [[maybe_unused]] const ScheduleTaskRequest& request,
+                         ScheduleTaskResponse* response) {
+                response->set_errorcode(ErrorCode::INVALID_ARG);
+                return Status();
+            });
+    ScheduleInfo scheduleInfo = {
+            .clientId = kTestClientId,
+            .scheduleId = kTestScheduleId,
+            .taskData = kTestData,
+            .count = kTestCount,
+            .startTimeInEpochSeconds = kTestStartTimeInEpochSeconds,
+            .periodicInSeconds = kTestPeriodicInSeconds,
+    };
+
+    ScopedAStatus status = getService()->scheduleTask(scheduleInfo);
+
+    ASSERT_FALSE(status.isOk());
+    ASSERT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestScheduleTask_UnspecifiedError) {
+    EXPECT_CALL(*getGrpcWakeupClientStub(), ScheduleTask)
+            .WillOnce([]([[maybe_unused]] ClientContext* context,
+                         [[maybe_unused]] const ScheduleTaskRequest& request,
+                         ScheduleTaskResponse* response) {
+                response->set_errorcode(ErrorCode::UNSPECIFIED);
+                return Status();
+            });
+    ScheduleInfo scheduleInfo = {
+            .clientId = kTestClientId,
+            .scheduleId = kTestScheduleId,
+            .taskData = kTestData,
+            .count = kTestCount,
+            .startTimeInEpochSeconds = kTestStartTimeInEpochSeconds,
+            .periodicInSeconds = kTestPeriodicInSeconds,
+    };
+
+    ScopedAStatus status = getService()->scheduleTask(scheduleInfo);
+
+    ASSERT_FALSE(status.isOk());
+    ASSERT_EQ(status.getExceptionCode(), EX_SERVICE_SPECIFIC);
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestUnscheduleTask) {
+    UnscheduleTaskRequest grpcRequest = {};
+    EXPECT_CALL(*getGrpcWakeupClientStub(), UnscheduleTask)
+            .WillOnce([&grpcRequest]([[maybe_unused]] ClientContext* context,
+                                     const UnscheduleTaskRequest& request,
+                                     [[maybe_unused]] UnscheduleTaskResponse* response) {
+                grpcRequest = request;
+                return Status();
+            });
+
+    ScopedAStatus status = getService()->unscheduleTask(kTestClientId, kTestScheduleId);
+
+    ASSERT_TRUE(status.isOk());
+    EXPECT_EQ(grpcRequest.clientid(), kTestClientId);
+    EXPECT_EQ(grpcRequest.scheduleid(), kTestScheduleId);
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestUnscheduleAllTasks) {
+    UnscheduleAllTasksRequest grpcRequest = {};
+    EXPECT_CALL(*getGrpcWakeupClientStub(), UnscheduleAllTasks)
+            .WillOnce([&grpcRequest]([[maybe_unused]] ClientContext* context,
+                                     const UnscheduleAllTasksRequest& request,
+                                     [[maybe_unused]] UnscheduleAllTasksResponse* response) {
+                grpcRequest = request;
+                return Status();
+            });
+
+    ScopedAStatus status = getService()->unscheduleAllTasks(kTestClientId);
+
+    ASSERT_TRUE(status.isOk());
+    EXPECT_EQ(grpcRequest.clientid(), kTestClientId);
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestIsTaskScheduled) {
+    bool isTaskScheduled = false;
+    IsTaskScheduledRequest grpcRequest = {};
+    EXPECT_CALL(*getGrpcWakeupClientStub(), IsTaskScheduled)
+            .WillOnce([&grpcRequest]([[maybe_unused]] ClientContext* context,
+                                     const IsTaskScheduledRequest& request,
+                                     IsTaskScheduledResponse* response) {
+                grpcRequest = request;
+                response->set_istaskscheduled(true);
+                return Status();
+            });
+
+    ScopedAStatus status =
+            getService()->isTaskScheduled(kTestClientId, kTestScheduleId, &isTaskScheduled);
+
+    ASSERT_TRUE(status.isOk());
+    EXPECT_TRUE(isTaskScheduled);
+    EXPECT_EQ(grpcRequest.clientid(), kTestClientId);
+    EXPECT_EQ(grpcRequest.scheduleid(), kTestScheduleId);
+}
+
+TEST_F(RemoteAccessServiceUnitTest, testGetAllScheduledTasks) {
+    std::vector<ScheduleInfo> result;
+    GetAllScheduledTasksRequest grpcRequest = {};
+    EXPECT_CALL(*getGrpcWakeupClientStub(), GetAllScheduledTasks)
+            .WillOnce([&grpcRequest]([[maybe_unused]] ClientContext* context,
+                                     const GetAllScheduledTasksRequest& request,
+                                     GetAllScheduledTasksResponse* response) {
+                grpcRequest = request;
+                GrpcScheduleInfo* newInfo = response->add_allscheduledtasks();
+                newInfo->set_clientid(kTestClientId);
+                newInfo->set_scheduleid(kTestScheduleId);
+                newInfo->set_data(kTestData.data(), kTestData.size());
+                newInfo->set_count(kTestCount);
+                newInfo->set_starttimeinepochseconds(kTestStartTimeInEpochSeconds);
+                newInfo->set_periodicinseconds(kTestPeriodicInSeconds);
+                return Status();
+            });
+
+    ScopedAStatus status = getService()->getAllScheduledTasks(kTestClientId, &result);
+
+    ASSERT_TRUE(status.isOk());
+    EXPECT_EQ(grpcRequest.clientid(), kTestClientId);
+    ASSERT_EQ(result.size(), 1u);
+    ASSERT_EQ(result[0].clientId, kTestClientId);
+    ASSERT_EQ(result[0].scheduleId, kTestScheduleId);
+    ASSERT_EQ(result[0].taskData, kTestData);
+    ASSERT_EQ(result[0].count, kTestCount);
+    ASSERT_EQ(result[0].startTimeInEpochSeconds, kTestStartTimeInEpochSeconds);
+    ASSERT_EQ(result[0].periodicInSeconds, kTestPeriodicInSeconds);
+}
+
 }  // namespace remoteaccess
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/remoteaccess/test_grpc_server/impl/Android.bp b/automotive/remoteaccess/test_grpc_server/impl/Android.bp
index 74c810e..fd174bf 100644
--- a/automotive/remoteaccess/test_grpc_server/impl/Android.bp
+++ b/automotive/remoteaccess/test_grpc_server/impl/Android.bp
@@ -61,3 +61,27 @@
         "-DHOST",
     ],
 }
+
+cc_test_host {
+    name: "TestWakeupClientServerHostUnitTest",
+    srcs: [
+        "test/*.cpp",
+        "src/TestWakeupClientServiceImpl.cpp",
+    ],
+    local_include_dirs: ["include"],
+    shared_libs: [
+        "libbase",
+        "libutils",
+        "libgrpc++",
+        "libprotobuf-cpp-full",
+    ],
+    static_libs: [
+        "libgtest",
+    ],
+    whole_static_libs: [
+        "wakeup_client_protos",
+    ],
+    cflags: [
+        "-Wno-unused-parameter",
+    ],
+}
diff --git a/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h b/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h
index 4159e83..2aab904 100644
--- a/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h
+++ b/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h
@@ -71,30 +71,41 @@
 // TaskQueue is thread-safe.
 class TaskQueue final {
   public:
-    TaskQueue();
-    ~TaskQueue();
+    TaskQueue(android::sp<Looper> looper);
 
     void add(const GetRemoteTasksResponse& response);
     std::optional<GetRemoteTasksResponse> maybePopOne();
     void waitForTask();
     void stopWait();
-    void handleTaskTimeout();
     bool isEmpty();
 
   private:
-    std::thread mCheckTaskTimeoutThread;
+    friend class TaskTimeoutMessageHandler;
+
     std::mutex mLock;
     std::priority_queue<TaskInfo, std::vector<TaskInfo>, TaskInfoComparator> mTasks
             GUARDED_BY(mLock);
     // A variable to notify mTasks is not empty.
     std::condition_variable mTasksNotEmptyCv;
-    bool mStopped GUARDED_BY(mLock);
+    std::atomic<bool> mStopped;
     android::sp<Looper> mLooper;
     android::sp<TaskTimeoutMessageHandler> mTaskTimeoutMessageHandler;
     std::atomic<int> mTaskIdCounter = 0;
 
-    void checkForTestTimeoutLoop();
-    void waitForTaskWithLock(std::unique_lock<std::mutex>& lock);
+    void loop();
+    void handleTaskTimeout();
+};
+
+// forward-declaration
+class TestWakeupClientServiceImpl;
+
+class TaskScheduleMsgHandler final : public android::MessageHandler {
+  public:
+    TaskScheduleMsgHandler(TestWakeupClientServiceImpl* mImpl);
+    void handleMessage(const android::Message& message) override;
+
+  private:
+    TestWakeupClientServiceImpl* mImpl;
 };
 
 class TestWakeupClientServiceImpl : public WakeupClient::Service {
@@ -103,6 +114,9 @@
 
     ~TestWakeupClientServiceImpl();
 
+    // Stop the handling for all income requests. Prepare for shutdown.
+    void stopServer();
+
     grpc::Status GetRemoteTasks(grpc::ServerContext* context, const GetRemoteTasksRequest* request,
                                 grpc::ServerWriter<GetRemoteTasksResponse>* writer) override;
 
@@ -110,6 +124,24 @@
                                       const NotifyWakeupRequiredRequest* request,
                                       NotifyWakeupRequiredResponse* response) override;
 
+    grpc::Status ScheduleTask(grpc::ServerContext* context, const ScheduleTaskRequest* request,
+                              ScheduleTaskResponse* response) override;
+
+    grpc::Status UnscheduleTask(grpc::ServerContext* context, const UnscheduleTaskRequest* request,
+                                UnscheduleTaskResponse* response) override;
+
+    grpc::Status UnscheduleAllTasks(grpc::ServerContext* context,
+                                    const UnscheduleAllTasksRequest* request,
+                                    UnscheduleAllTasksResponse* response) override;
+
+    grpc::Status IsTaskScheduled(grpc::ServerContext* context,
+                                 const IsTaskScheduledRequest* request,
+                                 IsTaskScheduledResponse* response) override;
+
+    grpc::Status GetAllScheduledTasks(grpc::ServerContext* context,
+                                      const GetAllScheduledTasksRequest* request,
+                                      GetAllScheduledTasksResponse* response) override;
+
     /**
      * Starts generating fake tasks for the specific client repeatedly.
      *
@@ -117,22 +149,27 @@
      * to the client every 5s.
      */
     void startGeneratingFakeTask(const std::string& clientId);
+
     /**
      * stops generating fake tasks.
      */
     void stopGeneratingFakeTask();
+
     /**
      * Returns whether we need to wakeup the target device to send remote tasks.
      */
     bool isWakeupRequired();
+
     /**
      * Returns whether we have an active connection with the target device.
      */
     bool isRemoteTaskConnectionAlive();
+
     /**
      * Injects a fake task with taskData to be sent to the specific client.
      */
     void injectTask(const std::string& taskData, const std::string& clientId);
+
     /**
      * Wakes up the target device.
      *
@@ -141,10 +178,34 @@
      */
     virtual void wakeupApplicationProcessor() = 0;
 
+    /**
+     * Cleans up a scheduled task info.
+     */
+    void cleanupScheduledTaskLocked(const std::string& clientId, const std::string& scheduleId)
+            REQUIRES(mLock);
+
   private:
-    // This is a thread for communicating with remote wakeup server (via network) and receive tasks
-    // from it.
-    std::thread mThread;
+    friend class TaskScheduleMsgHandler;
+
+    struct ScheduleInfo {
+        std::unique_ptr<GrpcScheduleInfo> grpcScheduleInfo;
+        // This is a unique ID to represent this schedule. Each repeated tasks will have different
+        // task ID but will have the same scheduleMsgId so that we can use to unschedule. This has
+        // to be an int so we cannot use the scheduleId provided by the client.
+        int scheduleMsgId;
+        int64_t periodicInSeconds;
+        int32_t currentCount;
+        int32_t totalCount;
+    };
+
+    std::atomic<int> mScheduleMsgCounter = 0;
+    // This is a looper for scheduling tasks to be executed in the future.
+    android::sp<Looper> mLooper;
+    android::sp<TaskScheduleMsgHandler> mTaskScheduleMsgHandler;
+    // This is a thread for generating fake tasks.
+    std::thread mFakeTaskThread;
+    // This is a thread for the looper.
+    std::thread mLooperThread;
     // A variable to notify server is stopping.
     std::condition_variable mTaskLoopStoppedCv;
     // Whether wakeup AP is required for executing tasks.
@@ -153,14 +214,21 @@
     std::atomic<bool> mRemoteTaskConnectionAlive = false;
     std::mutex mLock;
     bool mGeneratingFakeTask GUARDED_BY(mLock);
+    std::atomic<bool> mServerStopped;
+    std::unordered_map<std::string, std::unordered_map<std::string, ScheduleInfo>>
+            mInfoByScheduleIdByClientId GUARDED_BY(mLock);
 
     // Thread-safe. For test impl only.
     FakeTaskGenerator mFakeTaskGenerator;
-    // Thread-sfae.
-    TaskQueue mTaskQueue;
+    // Thread-safe.
+    std::unique_ptr<TaskQueue> mTaskQueue;
 
     void fakeTaskGenerateLoop(const std::string& clientId);
     void injectTaskResponse(const GetRemoteTasksResponse& response);
+    bool getScheduleInfoLocked(int scheduleMsgId, ScheduleInfo** outScheduleInfoPtr)
+            REQUIRES(mLock);
+    void handleAddTask(int scheduleMsgId);
+    void loop();
 };
 
 }  // namespace remoteaccess
diff --git a/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp b/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp
index eb3871b..1db991c 100644
--- a/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp
+++ b/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp
@@ -37,8 +37,17 @@
 using ::grpc::ServerWriter;
 using ::grpc::Status;
 
-constexpr int kTaskIntervalInMs = 5'000;
-constexpr int64_t KTaskTimeoutInMs = 20'000;
+constexpr int64_t kTaskIntervalInMs = 5'000;
+constexpr int64_t kTaskTimeoutInMs = 20'000;
+
+int64_t msToNs(int64_t ms) {
+    return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::milliseconds(ms))
+            .count();
+}
+
+int64_t sToNs(int64_t s) {
+    return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(s)).count();
+}
 
 }  // namespace
 
@@ -56,26 +65,9 @@
     mTaskQueue->handleTaskTimeout();
 }
 
-TaskQueue::TaskQueue() {
+TaskQueue::TaskQueue(android::sp<Looper> looper) {
     mTaskTimeoutMessageHandler = android::sp<TaskTimeoutMessageHandler>::make(this);
-    mLooper = Looper::prepare(/*opts=*/0);
-    mCheckTaskTimeoutThread = std::thread([this] { checkForTestTimeoutLoop(); });
-}
-
-TaskQueue::~TaskQueue() {
-    {
-        std::lock_guard<std::mutex> lockGuard(mLock);
-        mStopped = true;
-    }
-    while (true) {
-        // Remove all pending timeout handlers from queue.
-        if (!maybePopOne().has_value()) {
-            break;
-        }
-    }
-    if (mCheckTaskTimeoutThread.joinable()) {
-        mCheckTaskTimeoutThread.join();
-    }
+    mLooper = looper;
 }
 
 std::optional<GetRemoteTasksResponse> TaskQueue::maybePopOne() {
@@ -101,16 +93,12 @@
             .taskData = task,
     });
     android::Message message(taskId);
-    mLooper->sendMessageDelayed(KTaskTimeoutInMs * 1000, mTaskTimeoutMessageHandler, message);
+    mLooper->sendMessageDelayed(msToNs(kTaskTimeoutInMs), mTaskTimeoutMessageHandler, message);
     mTasksNotEmptyCv.notify_all();
 }
 
 void TaskQueue::waitForTask() {
     std::unique_lock<std::mutex> lock(mLock);
-    waitForTaskWithLock(lock);
-}
-
-void TaskQueue::waitForTaskWithLock(std::unique_lock<std::mutex>& lock) {
     mTasksNotEmptyCv.wait(lock, [this] {
         ScopedLockAssertion lockAssertion(mLock);
         return mTasks.size() > 0 || mStopped;
@@ -118,9 +106,11 @@
 }
 
 void TaskQueue::stopWait() {
-    std::lock_guard<std::mutex> lockGuard(mLock);
     mStopped = true;
-    mTasksNotEmptyCv.notify_all();
+    {
+        std::lock_guard<std::mutex> lockGuard(mLock);
+        mTasksNotEmptyCv.notify_all();
+    }
 }
 
 bool TaskQueue::isEmpty() {
@@ -128,21 +118,6 @@
     return mTasks.size() == 0 || mStopped;
 }
 
-void TaskQueue::checkForTestTimeoutLoop() {
-    Looper::setForThread(mLooper);
-
-    while (true) {
-        {
-            std::unique_lock<std::mutex> lock(mLock);
-            if (mStopped) {
-                return;
-            }
-        }
-
-        mLooper->pollAll(/*timeoutMillis=*/-1);
-    }
-}
-
 void TaskQueue::handleTaskTimeout() {
     // We know which task timed-out from the taskId in the message. However, there is no easy way
     // to remove a specific task with the task ID from the priority_queue, so we just check from
@@ -151,22 +126,50 @@
     int64_t now = uptimeMillis();
     while (mTasks.size() > 0) {
         const TaskInfo& taskInfo = mTasks.top();
-        if (taskInfo.timestampInMs + KTaskTimeoutInMs > now) {
+        if (taskInfo.timestampInMs + kTaskTimeoutInMs > now) {
             break;
         }
         // In real implementation, this should report task failure to remote wakeup server.
-        printf("Task for client ID: %s timed-out, added at %" PRId64 " ms, now %" PRId64 " ms",
+        printf("Task for client ID: %s timed-out, added at %" PRId64 " ms, now %" PRId64 " ms\n",
                taskInfo.taskData.clientid().c_str(), taskInfo.timestampInMs, now);
         mTasks.pop();
     }
 }
 
-TestWakeupClientServiceImpl::TestWakeupClientServiceImpl() {}
+TestWakeupClientServiceImpl::TestWakeupClientServiceImpl() {
+    mTaskScheduleMsgHandler = android::sp<TaskScheduleMsgHandler>::make(this);
+    mLooper = android::sp<Looper>::make(/*opts=*/0);
+    mLooperThread = std::thread([this] { loop(); });
+    mTaskQueue = std::make_unique<TaskQueue>(mLooper);
+}
 
 TestWakeupClientServiceImpl::~TestWakeupClientServiceImpl() {
-    { std::lock_guard<std::mutex> lockGuard(mLock); }
-    mTaskQueue.stopWait();
+    if (mServerStopped) {
+        return;
+    }
+    stopServer();
+}
+
+void TestWakeupClientServiceImpl::stopServer() {
+    mTaskQueue->stopWait();
     stopGeneratingFakeTask();
+    // Set the flag so that the loop thread will exit.
+    mServerStopped = true;
+    mLooper->wake();
+    if (mLooperThread.joinable()) {
+        mLooperThread.join();
+    }
+}
+
+void TestWakeupClientServiceImpl::loop() {
+    Looper::setForThread(mLooper);
+
+    while (true) {
+        mLooper->pollAll(/*timeoutMillis=*/-1);
+        if (mServerStopped) {
+            return;
+        }
+    }
 }
 
 void TestWakeupClientServiceImpl::injectTask(const std::string& taskData,
@@ -178,8 +181,8 @@
 }
 
 void TestWakeupClientServiceImpl::injectTaskResponse(const GetRemoteTasksResponse& response) {
-    printf("Received a new task\n");
-    mTaskQueue.add(response);
+    printf("Receive a new task\n");
+    mTaskQueue->add(response);
     if (mWakeupRequired) {
         wakeupApplicationProcessor();
     }
@@ -192,7 +195,7 @@
         return;
     }
     mGeneratingFakeTask = true;
-    mThread = std::thread([this, clientId] { fakeTaskGenerateLoop(clientId); });
+    mFakeTaskThread = std::thread([this, clientId] { fakeTaskGenerateLoop(clientId); });
     printf("Started generating fake tasks\n");
 }
 
@@ -206,8 +209,8 @@
         mTaskLoopStoppedCv.notify_all();
         mGeneratingFakeTask = false;
     }
-    if (mThread.joinable()) {
-        mThread.join();
+    if (mFakeTaskThread.joinable()) {
+        mFakeTaskThread.join();
     }
     printf("Stopped generating fake tasks\n");
 }
@@ -217,7 +220,7 @@
     // from it. Here we simulate receiving one remote task every {kTaskIntervalInMs}ms.
     while (true) {
         injectTaskResponse(mFakeTaskGenerator.generateTask(clientId));
-        printf("Sleeping for %d seconds until next task\n", kTaskIntervalInMs);
+        printf("Sleeping for %" PRId64 " seconds until next task\n", kTaskIntervalInMs);
 
         std::unique_lock lk(mLock);
         if (mTaskLoopStoppedCv.wait_for(lk, std::chrono::milliseconds(kTaskIntervalInMs), [this] {
@@ -236,10 +239,16 @@
     printf("GetRemoteTasks called\n");
     mRemoteTaskConnectionAlive = true;
     while (true) {
-        mTaskQueue.waitForTask();
+        mTaskQueue->waitForTask();
+
+        if (mServerStopped) {
+            // Server stopped, exit the loop.
+            printf("Server stopped exit loop\n");
+            break;
+        }
 
         while (true) {
-            auto maybeTask = mTaskQueue.maybePopOne();
+            auto maybeTask = mTaskQueue->maybePopOne();
             if (!maybeTask.has_value()) {
                 // No task left, loop again and wait for another task(s).
                 break;
@@ -252,21 +261,21 @@
                 printf("Failed to deliver remote task to remote access HAL\n");
                 // The task failed to be sent, add it back to the queue. The order might change, but
                 // it is okay.
-                mTaskQueue.add(response);
+                mTaskQueue->add(response);
                 mRemoteTaskConnectionAlive = false;
                 return Status::CANCELLED;
             }
         }
     }
-    mRemoteTaskConnectionAlive = false;
-    return Status::OK;
+    // Server stopped, exit the loop.
+    return Status::CANCELLED;
 }
 
 Status TestWakeupClientServiceImpl::NotifyWakeupRequired(ServerContext* context,
                                                          const NotifyWakeupRequiredRequest* request,
                                                          NotifyWakeupRequiredResponse* response) {
     printf("NotifyWakeupRequired called\n");
-    if (request->iswakeuprequired() && !mWakeupRequired && !mTaskQueue.isEmpty()) {
+    if (request->iswakeuprequired() && !mWakeupRequired && !mTaskQueue->isEmpty()) {
         // If wakeup is now required and previously not required, this means we have finished
         // shutting down the device. If there are still pending tasks, try waking up AP again
         // to finish executing those tasks.
@@ -281,6 +290,203 @@
     return Status::OK;
 }
 
+void TestWakeupClientServiceImpl::cleanupScheduledTaskLocked(const std::string& clientId,
+                                                             const std::string& scheduleId) {
+    mInfoByScheduleIdByClientId[clientId].erase(scheduleId);
+    if (mInfoByScheduleIdByClientId[clientId].size() == 0) {
+        mInfoByScheduleIdByClientId.erase(clientId);
+    }
+}
+
+TaskScheduleMsgHandler::TaskScheduleMsgHandler(TestWakeupClientServiceImpl* impl) : mImpl(impl) {}
+
+void TaskScheduleMsgHandler::handleMessage(const android::Message& message) {
+    mImpl->handleAddTask(message.what);
+}
+
+Status TestWakeupClientServiceImpl::ScheduleTask(ServerContext* context,
+                                                 const ScheduleTaskRequest* request,
+                                                 ScheduleTaskResponse* response) {
+    std::lock_guard<std::mutex> lockGuard(mLock);
+
+    const GrpcScheduleInfo& grpcScheduleInfo = request->scheduleinfo();
+    const std::string& scheduleId = grpcScheduleInfo.scheduleid();
+    const std::string& clientId = grpcScheduleInfo.clientid();
+    response->set_errorcode(ErrorCode::OK);
+
+    if (mInfoByScheduleIdByClientId.find(clientId) != mInfoByScheduleIdByClientId.end() &&
+        mInfoByScheduleIdByClientId[clientId].find(scheduleId) !=
+                mInfoByScheduleIdByClientId[clientId].end()) {
+        printf("Duplicate schedule Id: %s for client Id: %s\n", scheduleId.c_str(),
+               clientId.c_str());
+        response->set_errorcode(ErrorCode::INVALID_ARG);
+        return Status::OK;
+    }
+
+    int64_t startTimeInEpochSeconds = grpcScheduleInfo.starttimeinepochseconds();
+    int64_t periodicInSeconds = grpcScheduleInfo.periodicinseconds();
+    int32_t count = grpcScheduleInfo.count();
+
+    int scheduleMsgId = mScheduleMsgCounter++;
+    mInfoByScheduleIdByClientId[clientId][scheduleId] = {
+            .grpcScheduleInfo = std::make_unique<GrpcScheduleInfo>(grpcScheduleInfo),
+            .scheduleMsgId = scheduleMsgId,
+            .periodicInSeconds = periodicInSeconds,
+            .currentCount = 0,
+            .totalCount = count,
+    };
+
+    int64_t delayInSeconds =
+            startTimeInEpochSeconds - std::chrono::duration_cast<std::chrono::seconds>(
+                                              std::chrono::system_clock::now().time_since_epoch())
+                                              .count();
+    if (delayInSeconds < 0) {
+        delayInSeconds = 0;
+    }
+
+    printf("ScheduleTask called with client Id: %s, schedule Id: %s, delay: %" PRId64 " s\n",
+           clientId.c_str(), scheduleId.c_str(), delayInSeconds);
+
+    mLooper->sendMessageDelayed(sToNs(delayInSeconds), mTaskScheduleMsgHandler,
+                                android::Message(scheduleMsgId));
+
+    return Status::OK;
+}
+
+bool TestWakeupClientServiceImpl::getScheduleInfoLocked(int scheduleMsgId,
+                                                        ScheduleInfo** outScheduleInfoPtr) {
+    for (auto& [_, infoByScheduleId] : mInfoByScheduleIdByClientId) {
+        for (auto& [_, scheduleInfo] : infoByScheduleId) {
+            if (scheduleInfo.scheduleMsgId == scheduleMsgId) {
+                *outScheduleInfoPtr = &scheduleInfo;
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+void TestWakeupClientServiceImpl::handleAddTask(int scheduleMsgId) {
+    std::lock_guard<std::mutex> lockGuard(mLock);
+
+    ScheduleInfo* scheduleInfoPtr;
+    bool found = getScheduleInfoLocked(scheduleMsgId, &scheduleInfoPtr);
+    if (!found) {
+        printf("The schedule msg Id: %d is not found\n", scheduleMsgId);
+        return;
+    }
+
+    const GrpcScheduleInfo& grpcScheduleInfo = *scheduleInfoPtr->grpcScheduleInfo;
+    const std::string scheduleId = grpcScheduleInfo.scheduleid();
+    const std::string clientId = grpcScheduleInfo.clientid();
+
+    GetRemoteTasksResponse injectResponse;
+    injectResponse.set_data(grpcScheduleInfo.data().data(), grpcScheduleInfo.data().size());
+    injectResponse.set_clientid(clientId);
+    injectTaskResponse(injectResponse);
+    scheduleInfoPtr->currentCount++;
+
+    printf("Sending scheduled tasks for scheduleId: %s, clientId: %s, taskCount: %d\n",
+           scheduleId.c_str(), clientId.c_str(), scheduleInfoPtr->currentCount);
+
+    if (scheduleInfoPtr->totalCount != 0 &&
+        scheduleInfoPtr->currentCount == scheduleInfoPtr->totalCount) {
+        // This schedule is finished.
+        cleanupScheduledTaskLocked(clientId, scheduleId);
+        return;
+    }
+
+    // Schedule the task for the next period.
+    mLooper->sendMessageDelayed(sToNs(scheduleInfoPtr->periodicInSeconds), mTaskScheduleMsgHandler,
+                                android::Message(scheduleMsgId));
+}
+
+Status TestWakeupClientServiceImpl::UnscheduleTask(ServerContext* context,
+                                                   const UnscheduleTaskRequest* request,
+                                                   UnscheduleTaskResponse* response) {
+    std::lock_guard<std::mutex> lockGuard(mLock);
+
+    const std::string& clientId = request->clientid();
+    const std::string& scheduleId = request->scheduleid();
+    printf("UnscheduleTask called with client Id: %s, schedule Id: %s\n", clientId.c_str(),
+           scheduleId.c_str());
+
+    if (mInfoByScheduleIdByClientId.find(clientId) == mInfoByScheduleIdByClientId.end() ||
+        mInfoByScheduleIdByClientId[clientId].find(scheduleId) ==
+                mInfoByScheduleIdByClientId[clientId].end()) {
+        printf("UnscheduleTask: no task associated with clientId: %s, scheduleId: %s\n",
+               clientId.c_str(), scheduleId.c_str());
+        return Status::OK;
+    }
+
+    mLooper->removeMessages(mTaskScheduleMsgHandler,
+                            mInfoByScheduleIdByClientId[clientId][scheduleId].scheduleMsgId);
+    cleanupScheduledTaskLocked(clientId, scheduleId);
+    return Status::OK;
+}
+
+Status TestWakeupClientServiceImpl::UnscheduleAllTasks(ServerContext* context,
+                                                       const UnscheduleAllTasksRequest* request,
+                                                       UnscheduleAllTasksResponse* response) {
+    std::lock_guard<std::mutex> lockGuard(mLock);
+
+    const std::string& clientId = request->clientid();
+    printf("UnscheduleAllTasks called with client Id: %s\n", clientId.c_str());
+    if (mInfoByScheduleIdByClientId.find(clientId) == mInfoByScheduleIdByClientId.end()) {
+        printf("UnscheduleTask: no task associated with clientId: %s\n", clientId.c_str());
+        return Status::OK;
+    }
+    const auto& infoByScheduleId = mInfoByScheduleIdByClientId[clientId];
+    std::vector<int> scheduleMsgIds;
+    for (const auto& [_, scheduleInfo] : infoByScheduleId) {
+        mLooper->removeMessages(mTaskScheduleMsgHandler, /*what=*/scheduleInfo.scheduleMsgId);
+    }
+
+    mInfoByScheduleIdByClientId.erase(clientId);
+    return Status::OK;
+}
+
+Status TestWakeupClientServiceImpl::IsTaskScheduled(ServerContext* context,
+                                                    const IsTaskScheduledRequest* request,
+                                                    IsTaskScheduledResponse* response) {
+    std::lock_guard<std::mutex> lockGuard(mLock);
+
+    const std::string& clientId = request->clientid();
+    const std::string& scheduleId = request->scheduleid();
+    printf("IsTaskScheduled called with client Id: %s, scheduleId: %s\n", clientId.c_str(),
+           scheduleId.c_str());
+
+    if (mInfoByScheduleIdByClientId.find(clientId) == mInfoByScheduleIdByClientId.end()) {
+        response->set_istaskscheduled(false);
+        return Status::OK;
+    }
+    if (mInfoByScheduleIdByClientId[clientId].find(scheduleId) ==
+        mInfoByScheduleIdByClientId[clientId].end()) {
+        response->set_istaskscheduled(false);
+        return Status::OK;
+    }
+    response->set_istaskscheduled(true);
+    return Status::OK;
+}
+
+Status TestWakeupClientServiceImpl::GetAllScheduledTasks(ServerContext* context,
+                                                         const GetAllScheduledTasksRequest* request,
+                                                         GetAllScheduledTasksResponse* response) {
+    const std::string& clientId = request->clientid();
+    printf("GetAllScheduledTasks called with client Id: %s\n", clientId.c_str());
+    response->clear_allscheduledtasks();
+    {
+        std::unique_lock lk(mLock);
+        if (mInfoByScheduleIdByClientId.find(clientId) == mInfoByScheduleIdByClientId.end()) {
+            return Status::OK;
+        }
+        for (const auto& [_, scheduleInfo] : mInfoByScheduleIdByClientId[clientId]) {
+            (*response->add_allscheduledtasks()) = *scheduleInfo.grpcScheduleInfo;
+        }
+    }
+    return Status::OK;
+}
+
 bool TestWakeupClientServiceImpl::isWakeupRequired() {
     return mWakeupRequired;
 }
diff --git a/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp b/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp
index be285a8..5443ad9 100644
--- a/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp
+++ b/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp
@@ -101,13 +101,17 @@
 #endif
 }
 
-const char* getSetPropCommand(int propId) {
-    int size = snprintf(nullptr, 0, COMMAND_SET_VHAL_PROP, propId, 1);
+const char* getSetPropCommand(int propId, int value) {
+    int size = snprintf(nullptr, 0, COMMAND_SET_VHAL_PROP, propId, value);
     char* command = new char[size + 1];
-    snprintf(command, size + 1, COMMAND_SET_VHAL_PROP, propId, 1);
+    snprintf(command, size + 1, COMMAND_SET_VHAL_PROP, propId, value);
     return command;
 }
 
+const char* getSetPropCommand(int propId) {
+    return getSetPropCommand(propId, /*value=*/1);
+}
+
 void powerOffEmu() {
     updateEmuStatus();
     if (emuPid == 0) {
@@ -136,7 +140,7 @@
     if (vehicleInUse) {
         value = 1;
     }
-    const char* command = getSetPropCommand(VEHICLE_IN_USE);
+    const char* command = getSetPropCommand(VEHICLE_IN_USE, value);
     runCommand(command);
     delete[] command;
 #else
diff --git a/automotive/remoteaccess/test_grpc_server/impl/test/TestWakeupClientServiceImplUnitTest.cpp b/automotive/remoteaccess/test_grpc_server/impl/test/TestWakeupClientServiceImplUnitTest.cpp
new file mode 100644
index 0000000..960020d
--- /dev/null
+++ b/automotive/remoteaccess/test_grpc_server/impl/test/TestWakeupClientServiceImplUnitTest.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestWakeupClientServiceImpl.h"
+
+#include <grpcpp/channel.h>
+#include <grpcpp/create_channel.h>
+#include <grpcpp/security/credentials.h>
+#include <grpcpp/security/server_credentials.h>
+#include <grpcpp/server.h>
+#include <grpcpp/server_builder.h>
+#include <gtest/gtest.h>
+#include <chrono>
+
+namespace android::hardware::automotive::remoteaccess::test {
+
+using ::android::base::ScopedLockAssertion;
+
+using ::grpc::Channel;
+using ::grpc::ClientContext;
+using ::grpc::Server;
+using ::grpc::ServerBuilder;
+using ::grpc::Status;
+
+const std::string kTestClientId = "test client id";
+const std::string kTestScheduleId = "test schedule id";
+const std::vector<uint8_t> kTestData = {0xde, 0xad, 0xbe, 0xef};
+constexpr int32_t kTestCount = 1234;
+constexpr int64_t kTestStartTimeInEpochSeconds = 2345;
+constexpr int64_t kTestPeriodicInSeconds = 123;
+const std::string kTestGrpcAddr = "localhost:50051";
+
+class MyTestWakeupClientServiceImpl final : public TestWakeupClientServiceImpl {
+  public:
+    void wakeupApplicationProcessor() override {
+        // Do nothing.
+    }
+};
+
+class TestWakeupClientServiceImplUnitTest : public ::testing::Test {
+  public:
+    virtual void SetUp() override {
+        mServerThread = std::thread([this] {
+            {
+                std::unique_lock<std::mutex> lock(mLock);
+                mService = std::make_unique<MyTestWakeupClientServiceImpl>();
+                ServerBuilder builder;
+                builder.AddListeningPort(kTestGrpcAddr, grpc::InsecureServerCredentials());
+                builder.RegisterService(mService.get());
+                mServer = builder.BuildAndStart();
+                mServerStartCv.notify_one();
+            }
+            mServer->Wait();
+        });
+        {
+            std::unique_lock<std::mutex> lock(mLock);
+            mServerStartCv.wait(lock, [this] {
+                ScopedLockAssertion lockAssertion(mLock);
+                return mServer != nullptr;
+            });
+        }
+        mChannel = grpc::CreateChannel(kTestGrpcAddr, grpc::InsecureChannelCredentials());
+        mStub = WakeupClient::NewStub(mChannel);
+    }
+
+    virtual void TearDown() override {
+        printf("Start server shutdown\n");
+        mService->stopServer();
+        mServer->Shutdown();
+        printf("Server shutdown complete\n");
+        mServerThread.join();
+        printf("Server thread exits\n");
+        mServer.reset();
+        mService.reset();
+        printf("Server and service classes reset\n");
+    }
+
+    WakeupClient::Stub* getStub() { return mStub.get(); }
+
+    size_t waitForRemoteTasks(size_t count) {
+        ClientContext context = {};
+        GetRemoteTasksResponse response;
+        auto reader = mStub->GetRemoteTasks(&context, GetRemoteTasksRequest{});
+        size_t got = 0;
+        while (reader->Read(&response)) {
+            got++;
+            mRemoteTaskResponses.push_back(response);
+            if (got == count) {
+                break;
+            }
+        }
+        // If there is more messages to be read in the reader, cancel them all so that we can
+        // finish.
+        context.TryCancel();
+        reader->Finish();
+        return got;
+    }
+
+    std::vector<GetRemoteTasksResponse> getRemoteTaskResponses() { return mRemoteTaskResponses; }
+
+    Status scheduleTask(int32_t count, int64_t startTimeInEpochSeconds, int64_t periodicInSeconds) {
+        return scheduleTask(kTestScheduleId, count, startTimeInEpochSeconds, periodicInSeconds);
+    }
+
+    Status scheduleTask(const std::string& scheduleId, int32_t count,
+                        int64_t startTimeInEpochSeconds, int64_t periodicInSeconds) {
+        ClientContext context;
+        ScheduleTaskRequest request;
+        ScheduleTaskResponse response;
+        int64_t now = std::chrono::duration_cast<std::chrono::seconds>(
+                              std::chrono::system_clock::now().time_since_epoch())
+                              .count();
+        request.mutable_scheduleinfo()->set_clientid(kTestClientId);
+        request.mutable_scheduleinfo()->set_scheduleid(scheduleId);
+        request.mutable_scheduleinfo()->set_data(kTestData.data(), kTestData.size());
+        request.mutable_scheduleinfo()->set_count(count);
+        request.mutable_scheduleinfo()->set_starttimeinepochseconds(startTimeInEpochSeconds);
+        request.mutable_scheduleinfo()->set_periodicinseconds(periodicInSeconds);
+
+        return getStub()->ScheduleTask(&context, request, &response);
+    }
+
+    int64_t getNow() {
+        return std::chrono::duration_cast<std::chrono::seconds>(
+                       std::chrono::system_clock::now().time_since_epoch())
+                .count();
+    }
+
+  private:
+    std::condition_variable mServerStartCv;
+    std::mutex mLock;
+    std::thread mServerThread;
+    std::unique_ptr<MyTestWakeupClientServiceImpl> mService;
+    std::unique_ptr<Server> mServer;
+    std::shared_ptr<Channel> mChannel;
+    std::unique_ptr<WakeupClient::Stub> mStub;
+    std::vector<GetRemoteTasksResponse> mRemoteTaskResponses;
+};
+
+TEST_F(TestWakeupClientServiceImplUnitTest, TestScheduleTask) {
+    ClientContext context = {};
+    ScheduleTaskRequest request = {};
+    ScheduleTaskResponse response = {};
+
+    request.mutable_scheduleinfo()->set_clientid(kTestClientId);
+    request.mutable_scheduleinfo()->set_scheduleid(kTestScheduleId);
+    request.mutable_scheduleinfo()->set_data(kTestData.data(), kTestData.size());
+    request.mutable_scheduleinfo()->set_count(2);
+    // Schedule the task to be executed 1s later.
+    request.mutable_scheduleinfo()->set_starttimeinepochseconds(getNow() + 1);
+    request.mutable_scheduleinfo()->set_periodicinseconds(1);
+
+    Status status = getStub()->ScheduleTask(&context, request, &response);
+
+    ASSERT_TRUE(status.ok());
+    ASSERT_EQ(response.errorcode(), ErrorCode::OK);
+
+    size_t gotTaskCount = waitForRemoteTasks(/*count=*/2);
+
+    EXPECT_EQ(gotTaskCount, 2);
+    auto responses = getRemoteTaskResponses();
+    for (const auto& response : responses) {
+        EXPECT_EQ(response.clientid(), kTestClientId);
+        EXPECT_EQ(response.data(), std::string(kTestData.begin(), kTestData.end()));
+    }
+}
+
+TEST_F(TestWakeupClientServiceImplUnitTest, TestScheduleTask_conflictScheduleId) {
+    Status status = scheduleTask(/*count=*/2, /*startTimeInEpochSeconds=*/getNow() + 1,
+                                 /*periodicInSeconds=*/1);
+
+    ASSERT_TRUE(status.ok());
+
+    // Schedule the same task again.
+    ClientContext context = {};
+    ScheduleTaskRequest request = {};
+    ScheduleTaskResponse response = {};
+
+    request.mutable_scheduleinfo()->set_clientid(kTestClientId);
+    request.mutable_scheduleinfo()->set_scheduleid(kTestScheduleId);
+    request.mutable_scheduleinfo()->set_data(kTestData.data(), kTestData.size());
+    request.mutable_scheduleinfo()->set_count(2);
+    request.mutable_scheduleinfo()->set_starttimeinepochseconds(getNow() + 1);
+    request.mutable_scheduleinfo()->set_periodicinseconds(1);
+
+    status = getStub()->ScheduleTask(&context, request, &response);
+
+    ASSERT_TRUE(status.ok());
+    ASSERT_EQ(response.errorcode(), ErrorCode::INVALID_ARG);
+}
+
+TEST_F(TestWakeupClientServiceImplUnitTest, TestUnscheduleTask) {
+    Status status = scheduleTask(/*count=*/2, /*startTimeInEpochSeconds=*/getNow() + 1,
+                                 /*periodicInSeconds=*/1);
+
+    ASSERT_TRUE(status.ok());
+
+    ClientContext context;
+    UnscheduleTaskRequest request;
+    UnscheduleTaskResponse response;
+    request.set_clientid(kTestClientId);
+    request.set_scheduleid(kTestScheduleId);
+    status = getStub()->UnscheduleTask(&context, request, &response);
+
+    ASSERT_TRUE(status.ok());
+
+    sleep(2);
+
+    // There should be no remote tasks received after 2s because the task was unscheduled.
+    EXPECT_EQ(getRemoteTaskResponses().size(), 0);
+}
+
+TEST_F(TestWakeupClientServiceImplUnitTest, TestIsTaskScheduled) {
+    int64_t startTimeInEpochSeconds = getNow() + 1;
+    int64_t periodicInSeconds = 1234;
+
+    Status status = scheduleTask(/*count=*/2, startTimeInEpochSeconds, periodicInSeconds);
+
+    ASSERT_TRUE(status.ok());
+
+    ClientContext context;
+    IsTaskScheduledRequest request;
+    IsTaskScheduledResponse response;
+    request.set_clientid(kTestClientId);
+    request.set_scheduleid(kTestScheduleId);
+    status = getStub()->IsTaskScheduled(&context, request, &response);
+
+    ASSERT_TRUE(status.ok());
+    EXPECT_TRUE(response.istaskscheduled());
+
+    ClientContext context2;
+    IsTaskScheduledRequest request2;
+    IsTaskScheduledResponse response2;
+    request.set_clientid(kTestClientId);
+    request.set_scheduleid("invalid id");
+    status = getStub()->IsTaskScheduled(&context2, request2, &response2);
+
+    ASSERT_TRUE(status.ok());
+    EXPECT_FALSE(response2.istaskscheduled());
+}
+
+TEST_F(TestWakeupClientServiceImplUnitTest, TestUnscheduleAllTasks) {
+    std::string scheduleId1 = "scheduleId1";
+    std::string scheduleId2 = "scheduleId2";
+    int64_t time1 = getNow();
+    int64_t time2 = getNow() + 1;
+    int64_t periodicInSeconds1 = 1;
+    int64_t periodicInSeconds2 = 1;
+    int32_t count1 = 2;
+    int64_t count2 = 5;
+
+    Status status = scheduleTask(scheduleId1, count1, time1, periodicInSeconds1);
+    ASSERT_TRUE(status.ok());
+    status = scheduleTask(scheduleId2, count2, time2, periodicInSeconds2);
+    ASSERT_TRUE(status.ok());
+
+    ClientContext context;
+    UnscheduleAllTasksRequest request;
+    UnscheduleAllTasksResponse response;
+    request.set_clientid(kTestClientId);
+    status = getStub()->UnscheduleAllTasks(&context, request, &response);
+    ASSERT_TRUE(status.ok());
+
+    sleep(2);
+
+    // There should be no remote tasks received after 2s because the tasks were unscheduled.
+    EXPECT_EQ(getRemoteTaskResponses().size(), 0);
+}
+
+TEST_F(TestWakeupClientServiceImplUnitTest, TestGetAllScheduledTasks) {
+    std::string scheduleId1 = "scheduleId1";
+    std::string scheduleId2 = "scheduleId2";
+    int64_t time1 = getNow();
+    int64_t time2 = getNow() + 1;
+    int64_t periodicInSeconds1 = 1;
+    int64_t periodicInSeconds2 = 1;
+    int32_t count1 = 2;
+    int64_t count2 = 5;
+
+    Status status = scheduleTask(scheduleId1, count1, time1, periodicInSeconds1);
+    ASSERT_TRUE(status.ok());
+    status = scheduleTask(scheduleId2, count2, time2, periodicInSeconds2);
+    ASSERT_TRUE(status.ok());
+
+    ClientContext context;
+    GetAllScheduledTasksRequest request;
+    GetAllScheduledTasksResponse response;
+    request.set_clientid("invalid client Id");
+    status = getStub()->GetAllScheduledTasks(&context, request, &response);
+
+    ASSERT_TRUE(status.ok());
+    EXPECT_EQ(response.allscheduledtasks_size(), 0);
+
+    ClientContext context2;
+    GetAllScheduledTasksRequest request2;
+    GetAllScheduledTasksResponse response2;
+    request2.set_clientid(kTestClientId);
+    status = getStub()->GetAllScheduledTasks(&context2, request2, &response2);
+
+    ASSERT_TRUE(status.ok());
+    ASSERT_EQ(response2.allscheduledtasks_size(), 2);
+    for (int i = 0; i < 2; i++) {
+        EXPECT_EQ(response2.allscheduledtasks(i).clientid(), kTestClientId);
+        if (response2.allscheduledtasks(i).scheduleid() == scheduleId1) {
+            EXPECT_EQ(response2.allscheduledtasks(i).data(),
+                      std::string(kTestData.begin(), kTestData.end()));
+            EXPECT_EQ(response2.allscheduledtasks(i).count(), count1);
+            EXPECT_EQ(response2.allscheduledtasks(i).starttimeinepochseconds(), time1);
+            EXPECT_EQ(response2.allscheduledtasks(i).periodicinseconds(), periodicInSeconds1);
+        } else {
+            EXPECT_EQ(response2.allscheduledtasks(i).scheduleid(), scheduleId2);
+            EXPECT_EQ(response2.allscheduledtasks(i).data(),
+                      std::string(kTestData.begin(), kTestData.end()));
+            EXPECT_EQ(response2.allscheduledtasks(i).count(), count2);
+            EXPECT_EQ(response2.allscheduledtasks(i).starttimeinepochseconds(), time2);
+            EXPECT_EQ(response2.allscheduledtasks(i).periodicinseconds(), periodicInSeconds2);
+        }
+    }
+}
+
+}  // namespace android::hardware::automotive::remoteaccess::test
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/automotive/sv/1.0/default/tests/fuzzer/AutomotiveSvV1_0Fuzzer.cpp b/automotive/sv/1.0/default/tests/fuzzer/AutomotiveSvV1_0Fuzzer.cpp
index 98834f5..4c4cc70 100644
--- a/automotive/sv/1.0/default/tests/fuzzer/AutomotiveSvV1_0Fuzzer.cpp
+++ b/automotive/sv/1.0/default/tests/fuzzer/AutomotiveSvV1_0Fuzzer.cpp
@@ -41,35 +41,32 @@
 
 void SurroundViewFuzzer::invoke2dSessionAPI() {
     sp<ISurroundView2dSession> surroundView2dSession;
+    sp<SurroundViewStream> handler;
+    mSurroundViewService->start2dSession(
+            [&surroundView2dSession](const sp<ISurroundView2dSession>& session, SvResult result) {
+                if (result == SvResult::OK) {
+                    surroundView2dSession = session;
+                }
+            });
+
+    if (surroundView2dSession && !mIs2dStreamStarted) {
+        handler = sp<SurroundViewStream>::make(surroundView2dSession);
+        if (surroundView2dSession->startStream(handler) == SvResult::OK) {
+            mIs2dStreamStarted = true;
+        }
+    }
 
     while (mFuzzedDataProvider.remaining_bytes() > 0) {
         auto surroundView2dFunc = mFuzzedDataProvider.PickValueInArray<
                 const std::function<void()>>({
                 [&]() {
-                    mSurroundViewService->start2dSession(
-                            [&surroundView2dSession](const sp<ISurroundView2dSession>& session,
-                                                     SvResult result) {
-                                if (result == SvResult::OK) {
-                                    surroundView2dSession = session;
-                                }
-                            });
-                },
-                [&]() {
-                    if (surroundView2dSession) {
-                        sp<SurroundViewStream> handler =
-                                sp<SurroundViewStream>::make(surroundView2dSession);
-                        surroundView2dSession->startStream(handler);
-                        mIs2dStreamStarted = true;
-                    }
-                },
-                [&]() {
                     if (surroundView2dSession) {
                         surroundView2dSession->get2dMappingInfo(
                                 []([[maybe_unused]] Sv2dMappingInfo info) {});
                     }
                 },
                 [&]() {
-                    if (surroundView2dSession) {
+                    if (surroundView2dSession && mIs2dStreamStarted) {
                         Sv2dConfig config;
                         config.width = mFuzzedDataProvider.ConsumeIntegralInRange<uint32_t>(
                                 kMinConfigDimension, kMaxConfigDimension);
@@ -149,8 +146,11 @@
                     }
                 },
                 [&]() {
-                    mSurroundViewService->stop2dSession(
+                    SvResult result = mSurroundViewService->stop2dSession(
                             mFuzzedDataProvider.ConsumeBool() ? surroundView2dSession : nullptr);
+                    if (result == SvResult::OK) {
+                        mIs2dStreamStarted = false;
+                    }
                 },
         });
         surroundView2dFunc();
@@ -159,31 +159,40 @@
     if (surroundView2dSession && mIs2dStreamStarted) {
         surroundView2dSession->stopStream();
     }
+
+    if (surroundView2dSession) {
+        mSurroundViewService->stop2dSession(surroundView2dSession);
+    }
 }
 
 void SurroundViewFuzzer::invoke3dSessionAPI() {
     sp<ISurroundView3dSession> surroundView3dSession;
+    sp<SurroundViewStream> handler;
+    mSurroundViewService->start3dSession(
+            [&surroundView3dSession](const sp<ISurroundView3dSession>& session, SvResult result) {
+                if (result == SvResult::OK) {
+                    surroundView3dSession = session;
+                }
+            });
+
+    const size_t numViews = mFuzzedDataProvider.ConsumeIntegralInRange<size_t>(1, kMaxViews);
+    std::vector<View3d> views(numViews);
+    for (size_t i = 0; i < numViews; ++i) {
+        views[i].viewId = mFuzzedDataProvider.ConsumeIntegral<uint32_t>();
+    }
+    surroundView3dSession->setViews(views);
+
+    if (surroundView3dSession) {
+        handler = sp<SurroundViewStream>::make(surroundView3dSession);
+
+        if (surroundView3dSession->startStream(handler) == SvResult::OK) {
+            mIs3dStreamStarted = true;
+        }
+    }
     while (mFuzzedDataProvider.remaining_bytes() > 0) {
         auto surroundView3dFunc = mFuzzedDataProvider.PickValueInArray<
                 const std::function<void()>>({
                 [&]() {
-                    mSurroundViewService->start3dSession(
-                            [&surroundView3dSession](const sp<ISurroundView3dSession>& session,
-                                                     SvResult result) {
-                                if (result == SvResult::OK) {
-                                    surroundView3dSession = session;
-                                }
-                            });
-                },
-                [&]() {
-                    if (surroundView3dSession) {
-                        sp<SurroundViewStream> handler =
-                                sp<SurroundViewStream>::make(surroundView3dSession);
-                        surroundView3dSession->startStream(handler);
-                        mIs3dStreamStarted = true;
-                    }
-                },
-                [&]() {
                     if (surroundView3dSession) {
                         const size_t numViews =
                                 mFuzzedDataProvider.ConsumeIntegralInRange<size_t>(1, kMaxViews);
@@ -195,7 +204,7 @@
                     }
                 },
                 [&]() {
-                    if (surroundView3dSession) {
+                    if (surroundView3dSession && mIs3dStreamStarted) {
                         Sv3dConfig config;
                         config.width = mFuzzedDataProvider.ConsumeIntegralInRange<uint32_t>(
                                 kMinConfigDimension, kMaxConfigDimension);
@@ -306,8 +315,11 @@
                     }
                 },
                 [&]() {
-                    mSurroundViewService->stop3dSession(
+                    SvResult result = mSurroundViewService->stop3dSession(
                             mFuzzedDataProvider.ConsumeBool() ? surroundView3dSession : nullptr);
+                    if (result == SvResult::OK) {
+                        mIs3dStreamStarted = false;
+                    }
                 },
         });
         surroundView3dFunc();
@@ -315,6 +327,10 @@
     if (surroundView3dSession && mIs3dStreamStarted) {
         surroundView3dSession->stopStream();
     }
+
+    if (surroundView3dSession) {
+        mSurroundViewService->stop3dSession(surroundView3dSession);
+    }
 }
 
 void SurroundViewFuzzer::process() {
diff --git a/automotive/vehicle/2.0/default/tests/fuzzer/VehicleManager_fuzzer.cpp b/automotive/vehicle/2.0/default/tests/fuzzer/VehicleManager_fuzzer.cpp
index 8a085e5..4d0995d 100644
--- a/automotive/vehicle/2.0/default/tests/fuzzer/VehicleManager_fuzzer.cpp
+++ b/automotive/vehicle/2.0/default/tests/fuzzer/VehicleManager_fuzzer.cpp
@@ -76,30 +76,20 @@
 using ::android::hardware::automotive::vehicle::V2_0::vms::VmsLayerOffering;
 using ::android::hardware::automotive::vehicle::V2_0::vms::VmsOffers;
 
-constexpr const char kCarMake[] = "Default Car";
-constexpr VehicleProperty kVehicleProp[] = {VehicleProperty::INVALID,
-                                            VehicleProperty::HVAC_FAN_SPEED,
-                                            VehicleProperty::INFO_MAKE,
-                                            VehicleProperty::DISPLAY_BRIGHTNESS,
-                                            VehicleProperty::INFO_FUEL_CAPACITY,
-                                            VehicleProperty::HVAC_SEAT_TEMPERATURE};
-constexpr DiagnosticIntegerSensorIndex kDiagnosticIntIndex[] = {
-        DiagnosticIntegerSensorIndex::FUEL_SYSTEM_STATUS,
-        DiagnosticIntegerSensorIndex::MALFUNCTION_INDICATOR_LIGHT_ON,
-        DiagnosticIntegerSensorIndex::NUM_OXYGEN_SENSORS_PRESENT,
-        DiagnosticIntegerSensorIndex::FUEL_TYPE};
-constexpr DiagnosticFloatSensorIndex kDiagnosticFloatIndex[] = {
-        DiagnosticFloatSensorIndex::CALCULATED_ENGINE_LOAD,
-        DiagnosticFloatSensorIndex::SHORT_TERM_FUEL_TRIM_BANK1,
-        DiagnosticFloatSensorIndex::LONG_TERM_FUEL_TRIM_BANK1,
-        DiagnosticFloatSensorIndex::THROTTLE_POSITION};
-constexpr size_t kVehiclePropArrayLength = std::size(kVehicleProp);
-constexpr size_t kIntSensorArrayLength = std::size(kDiagnosticIntIndex);
-constexpr size_t kFloatSensorArrayLength = std::size(kDiagnosticFloatIndex);
-constexpr VmsMessageType kAvailabilityMessageType[] = {VmsMessageType::AVAILABILITY_CHANGE,
-                                                       VmsMessageType::AVAILABILITY_RESPONSE};
-constexpr VmsMessageType kSubscriptionMessageType[] = {VmsMessageType::SUBSCRIPTIONS_CHANGE,
-                                                       VmsMessageType::SUBSCRIPTIONS_RESPONSE};
+std::string kCarMake;
+constexpr int32_t kMaxCaseMessage = 8;
+constexpr int32_t kMaxRuns = 20;
+constexpr int32_t kMaxSize = 1000;
+constexpr int32_t kMinSize = 0;
+constexpr int32_t kMaxFileSize = 100;
+float kFloatValue;
+std::vector<int32_t> kVec32;
+std::vector<int64_t> kVec64;
+std::vector<uint8_t> kVec8;
+std::vector<float> kVecFloat;
+static const std::vector<std::string> kSampleDtcs = {"P0070",
+                                                     "P0102"
+                                                     "P0123"};
 
 MockedVehicleHal::VehiclePropValuePtr MockedVehicleHal::get(
         const VehiclePropValue& requestedPropValue, StatusCode* outStatus) {
@@ -113,23 +103,23 @@
 
     switch (property) {
         case VehicleProperty::INFO_MAKE:
-            pValue = getValuePool()->obtainString(kCarMake);
+            pValue = getValuePool()->obtainString(kCarMake.c_str());
             break;
         case VehicleProperty::INFO_FUEL_CAPACITY:
             if (mFuelCapacityAttemptsLeft-- > 0) {
                 *outStatus = StatusCode::TRY_AGAIN;
             } else {
-                pValue = getValuePool()->obtainFloat(42.42);
+                pValue = getValuePool()->obtainFloat(kFloatValue);
             }
             break;
         default:
             if (requestedPropValue.prop == kCustomComplexProperty) {
                 pValue = getValuePool()->obtainComplex();
-                pValue->value.int32Values = hidl_vec<int32_t>{10, 20};
-                pValue->value.int64Values = hidl_vec<int64_t>{30, 40};
-                pValue->value.floatValues = hidl_vec<float_t>{1.1, 2.2};
-                pValue->value.bytes = hidl_vec<uint8_t>{1, 2, 3};
-                pValue->value.stringValue = kCarMake;
+                pValue->value.int32Values = hidl_vec<int32_t>{kVec32};
+                pValue->value.int64Values = hidl_vec<int64_t>{kVec64};
+                pValue->value.floatValues = hidl_vec<float_t>{kVecFloat};
+                pValue->value.bytes = hidl_vec<uint8_t>{kVec8};
+                pValue->value.stringValue = kCarMake.c_str();
                 break;
             }
             auto key = makeKey(toInt(property), areaId);
@@ -145,28 +135,72 @@
     return pValue;
 }
 
+void VehicleHalManagerFuzzer::initValue() {
+    kCarMake = mFuzzedDataProvider->ConsumeRandomLengthString(kMaxFileSize);
+    kFloatValue = mFuzzedDataProvider->ConsumeFloatingPoint<float>();
+    fillParameter<int32_t>(mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize),
+                           kVec32);
+    fillParameter<int64_t>(mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize),
+                           kVec64);
+    fillParameter<uint8_t>(mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize),
+                           kVec8);
+    size_t size = mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize);
+    for (size_t i = 0; i < size; ++i) {
+        kVecFloat.push_back(mFuzzedDataProvider->ConsumeFloatingPoint<float>());
+    }
+}
+
 void VehicleHalManagerFuzzer::process(const uint8_t* data, size_t size) {
     mFuzzedDataProvider = new FuzzedDataProvider(data, size);
-    invokeDebug();
-    invokePropConfigs();
-    invokeSubscribe();
-    invokeSetAndGetValues();
-    invokeObd2SensorStore();
-    invokeVmsUtils();
-    invokeVehiclePropStore();
-    invokeWatchDogClient();
+    initValue();
+    /* Limited while loop runs to prevent timeouts caused
+     * by repeated calls to high-execution-time APIs.
+     */
+    size_t maxRuns = mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(kMinSize, kMaxRuns);
+    size_t itr = 0;
+    while (mFuzzedDataProvider->remaining_bytes() && ++itr <= maxRuns) {
+        auto invokeVehicleHalManagerFuzzer =
+                mFuzzedDataProvider->PickValueInArray<const std::function<void()>>({
+                        [&]() { invokeDebug(); },
+                        [&]() { invokePropConfigs(); },
+                        [&]() { invokeSubscribe(); },
+                        [&]() { invokeSetAndGetValues(); },
+                        [&]() { invokeObd2SensorStore(); },
+                        [&]() { invokeVmsUtils(); },
+                        [&]() { invokeVehiclePropStore(); },
+                        [&]() { invokeWatchDogClient(); },
+                });
+        invokeVehicleHalManagerFuzzer();
+    }
 }
 
 void VehicleHalManagerFuzzer::invokeDebug() {
-    hidl_string debugOption = mFuzzedDataProvider->PickValueInArray(
-            {"--help", "--list", "--get", "--set", "", "invalid"});
     hidl_handle fd = {};
 
     native_handle_t* rawHandle = native_handle_create(/*numFds=*/1, /*numInts=*/0);
     fd.setTo(native_handle_clone(rawHandle), /*shouldOwn=*/true);
+    int32_t size = mFuzzedDataProvider->ConsumeIntegralInRange<int32_t>(kMinSize, kMaxFileSize);
+    hidl_vec<hidl_string> options(size);
 
-    mManager->debug(fd, {});
-    mManager->debug(fd, {debugOption});
+    for (int32_t idx = 0; idx < size; ++idx) {
+        if (idx == 0 && mFuzzedDataProvider->ConsumeBool()) {
+            options[idx] = mFuzzedDataProvider->PickValueInArray(
+                    {"--help", "--list", "--get", "--set", "", "invalid"});
+        } else if (idx == 2 && mFuzzedDataProvider->ConsumeBool()) {
+            options[idx] =
+                    mFuzzedDataProvider->PickValueInArray({"-i", "-i64", "-f", "-s", "-b", "-a"});
+        } else if (mFuzzedDataProvider->ConsumeBool()) {
+            options[idx] = mFuzzedDataProvider->ConsumeRandomLengthString(kMaxSize);
+        } else {
+            options[idx] = std::to_string(mFuzzedDataProvider->ConsumeIntegral<int32_t>());
+        }
+    }
+
+    if (mFuzzedDataProvider->ConsumeBool()) {
+        mManager->debug(fd, {});
+    } else {
+        mManager->debug(fd, options);
+    }
     native_handle_delete(rawHandle);
 }
 
@@ -175,178 +209,245 @@
     int32_t vehicleProp2 = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
 
     hidl_vec<int32_t> properties = {vehicleProp1, vehicleProp2};
+    auto invokePropConfigsAPI = mFuzzedDataProvider->PickValueInArray<const std::function<void()>>({
+            [&]() {
+                mManager->getPropConfigs(
+                        properties, []([[maybe_unused]] StatusCode status,
+                                       [[maybe_unused]] const hidl_vec<VehiclePropConfig>& c) {});
+            },
+            [&]() {
+                mManager->getPropConfigs(
+                        {mFuzzedDataProvider->ConsumeIntegral<int32_t>()},
+                        []([[maybe_unused]] StatusCode status,
+                           [[maybe_unused]] const hidl_vec<VehiclePropConfig>& c) {});
+            },
+            [&]() {
+                mManager->getAllPropConfigs(
+                        []([[maybe_unused]] const hidl_vec<VehiclePropConfig>& propConfigs) {});
+            },
 
-    mManager->getPropConfigs(properties,
-                             []([[maybe_unused]] StatusCode status,
-                                [[maybe_unused]] const hidl_vec<VehiclePropConfig>& c) {});
-
-    mManager->getPropConfigs({toInt(kVehicleProp[abs(vehicleProp1) % kVehiclePropArrayLength])},
-                             []([[maybe_unused]] StatusCode status,
-                                [[maybe_unused]] const hidl_vec<VehiclePropConfig>& c) {});
-
-    mManager->getAllPropConfigs(
-            []([[maybe_unused]] const hidl_vec<VehiclePropConfig>& propConfigs) {});
+    });
+    invokePropConfigsAPI();
 }
 
 void VehicleHalManagerFuzzer::invokeSubscribe() {
-    int32_t vehicleProp1 = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
     int32_t vehicleProp2 = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
     int32_t vehicleProp3 = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
 
-    const auto prop1 = toInt(kVehicleProp[abs(vehicleProp1) % kVehiclePropArrayLength]);
     sp<MockedVehicleCallback> cb = new MockedVehicleCallback();
+    VehiclePropertyType type =
+            static_cast<VehiclePropertyType>(mFuzzedDataProvider->ConsumeIntegral<int32_t>());
 
-    hidl_vec<SubscribeOptions> options = {
-            SubscribeOptions{.propId = prop1, .flags = SubscribeFlags::EVENTS_FROM_CAR}};
+    auto invokeSubscribeAPI = mFuzzedDataProvider->PickValueInArray<const std::function<void()>>({
+            [&]() {
+                size_t size =
+                        mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize);
+                hidl_vec<SubscribeOptions> options(size);
+                for (size_t idx = 0; idx < size; ++idx) {
+                    options[idx] = {SubscribeOptions{
+                            .propId = mFuzzedDataProvider->ConsumeIntegral<int32_t>(),
+                            .flags = static_cast<SubscribeFlags>(
+                                    mFuzzedDataProvider->ConsumeIntegral<int32_t>())}};
+                }
+                mManager->subscribe(cb, options);
+            },
+            [&]() {
+                auto unsubscribedValue = mObjectPool->obtain(type);
+                if (!unsubscribedValue) {
+                    return;
+                }
+                unsubscribedValue->prop = vehicleProp2;
+                unsubscribedValue->value.int32Values[0] = INT32_MAX;
+                mHal->sendPropEvent(std::move(unsubscribedValue));
+                cb->waitForExpectedEvents(mFuzzedDataProvider->ConsumeIntegral<size_t>());
+            },
+            [&]() {
+                const auto prop1 = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
+                mManager->unsubscribe(cb, prop1);
+            },
+            [&]() {
+                mHal->sendHalError(StatusCode::TRY_AGAIN, vehicleProp3,
+                                   mFuzzedDataProvider->ConsumeIntegral<int32_t>() /*areaId=*/);
+            },
 
-    mManager->subscribe(cb, options);
-
-    auto unsubscribedValue = mObjectPool->obtain(VehiclePropertyType::INT32);
-    unsubscribedValue->prop = toInt(kVehicleProp[abs(vehicleProp2) % kVehiclePropArrayLength]);
-
-    mHal->sendPropEvent(std::move(unsubscribedValue));
-    cb->getReceivedEvents();
-    cb->waitForExpectedEvents(0);
-
-    auto subscribedValue = mObjectPool->obtain(VehiclePropertyType::INT32);
-    subscribedValue->prop = toInt(kVehicleProp[abs(vehicleProp2) % kVehiclePropArrayLength]);
-    subscribedValue->value.int32Values[0] = INT32_MAX;
-
-    cb->reset();
-    VehiclePropValue actualValue(*subscribedValue.get());
-    mHal->sendPropEvent(std::move(subscribedValue));
-    cb->waitForExpectedEvents(1);
-    mManager->unsubscribe(cb, prop1);
-
-    sp<MockedVehicleCallback> cb2 = new MockedVehicleCallback();
-
-    hidl_vec<SubscribeOptions> options2 = {
-            SubscribeOptions{
-                    .propId = toInt(kVehicleProp[abs(vehicleProp3) % kVehiclePropArrayLength]),
-                    .flags = SubscribeFlags::EVENTS_FROM_CAR},
-    };
-
-    mManager->subscribe(cb2, options2);
-
-    mHal->sendHalError(StatusCode::TRY_AGAIN,
-                       toInt(kVehicleProp[abs(vehicleProp3) % kVehiclePropArrayLength]),
-                       /*areaId=*/0);
+    });
+    invokeSubscribeAPI();
 }
 
 void VehicleHalManagerFuzzer::invokeSetAndGetValues() {
-    uint32_t vehicleProp1 =
-            mFuzzedDataProvider->ConsumeIntegralInRange<uint32_t>(0, kVehiclePropArrayLength - 1);
-    uint32_t vehicleProp2 =
-            mFuzzedDataProvider->ConsumeIntegralInRange<uint32_t>(0, kVehiclePropArrayLength - 1);
-    uint32_t vehicleProp3 =
-            mFuzzedDataProvider->ConsumeIntegralInRange<uint32_t>(0, kVehiclePropArrayLength - 1);
-
-    invokeGet(kCustomComplexProperty, 0);
-    invokeGet(toInt(kVehicleProp[vehicleProp2]), 0);
-    invokeGet(toInt(kVehicleProp[vehicleProp1]), 0);
-
-    auto expectedValue = mObjectPool->obtainInt32(mFuzzedDataProvider->ConsumeIntegral<int32_t>());
-    mObjectPool->obtainInt64(mFuzzedDataProvider->ConsumeIntegral<int64_t>());
-    mObjectPool->obtainFloat(mFuzzedDataProvider->ConsumeFloatingPoint<float>());
-    mObjectPool->obtainBoolean(mFuzzedDataProvider->ConsumeBool());
-    expectedValue->prop = toInt(kVehicleProp[vehicleProp2]);
-    expectedValue->areaId = 0;
-
-    mManager->set(*expectedValue.get());
-    invokeGet(toInt(kVehicleProp[vehicleProp2]), 0);
-    expectedValue->prop = toInt(kVehicleProp[vehicleProp3]);
-    mManager->set(*expectedValue.get());
-    expectedValue->prop = toInt(VehicleProperty::INVALID);
-    mManager->set(*expectedValue.get());
+    auto invokeSetAndGetAPI = mFuzzedDataProvider->PickValueInArray<const std::function<void()>>({
+            [&]() {
+                invokeGet(mFuzzedDataProvider->ConsumeIntegral<int32_t>(),
+                          mFuzzedDataProvider->ConsumeIntegral<int32_t>());
+            },
+            [&]() { mObjectPool->obtainInt64(mFuzzedDataProvider->ConsumeIntegral<int64_t>()); },
+            [&]() { mObjectPool->obtainFloat(mFuzzedDataProvider->ConsumeFloatingPoint<float>()); },
+            [&]() { mObjectPool->obtainBoolean(mFuzzedDataProvider->ConsumeBool()); },
+            [&]() {
+                int32_t vehicleProp2 = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
+                auto expectedValue =
+                        mObjectPool->obtainInt32(mFuzzedDataProvider->ConsumeIntegral<int32_t>());
+                expectedValue->prop = vehicleProp2;
+                expectedValue->areaId = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
+                mManager->set(*expectedValue.get());
+            },
+    });
+    invokeSetAndGetAPI();
 }
 
 void VehicleHalManagerFuzzer::invokeObd2SensorStore() {
-    uint32_t diagnosticIntIndex =
-            mFuzzedDataProvider->ConsumeIntegralInRange<uint32_t>(0, kIntSensorArrayLength - 1);
-    int32_t diagnosticIntValue = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
-    uint32_t diagnosticFloatIndex =
-            mFuzzedDataProvider->ConsumeIntegralInRange<uint32_t>(0, kFloatSensorArrayLength - 1);
-    float diagnosticFloatValue = mFuzzedDataProvider->ConsumeFloatingPoint<float>();
+    size_t diagnosticInt = mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize);
+    size_t diagnosticFloat =
+            mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize);
 
     std::unique_ptr<Obd2SensorStore> sensorStore(
-            new Obd2SensorStore(kIntSensorArrayLength, kFloatSensorArrayLength));
-    if (sensorStore) {
-        sensorStore->setIntegerSensor(kDiagnosticIntIndex[diagnosticIntIndex], diagnosticIntValue);
-        sensorStore->setFloatSensor(kDiagnosticFloatIndex[diagnosticFloatIndex],
-                                    diagnosticFloatValue);
-        sensorStore->getIntegerSensors();
-        sensorStore->getFloatSensors();
-        sensorStore->getSensorsBitmask();
-        static std::vector<std::string> sampleDtcs = {"P0070",
-                                                      "P0102"
-                                                      "P0123"};
-        for (auto&& dtc : sampleDtcs) {
-            auto freezeFrame = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
-            sensorStore->fillPropValue(dtc, freezeFrame.get());
-            freezeFrame->prop = static_cast<int>(VehicleProperty::OBD2_FREEZE_FRAME);
-        }
+            new Obd2SensorStore(diagnosticInt, diagnosticFloat));
+
+    if (!sensorStore.get()) {
+        return;
     }
+
+    auto invokeObd2SensorStoreAPI =
+            mFuzzedDataProvider->PickValueInArray<const std::function<void()>>({
+                    [&]() {
+                        int32_t diagnosticIntValue =
+                                mFuzzedDataProvider->ConsumeIntegral<int32_t>();
+                        int32_t diagnosticIntIndex =
+                                mFuzzedDataProvider->ConsumeIntegralInRange<int32_t>(
+                                        kMinSize,
+                                        toInt(DiagnosticIntegerSensorIndex::LAST_SYSTEM_INDEX) +
+                                                diagnosticInt);
+                        sensorStore->setIntegerSensor(
+                                static_cast<DiagnosticIntegerSensorIndex>(diagnosticIntIndex),
+                                diagnosticIntValue);
+                    },
+                    [&]() {
+                        float diagnosticFloatValue =
+                                mFuzzedDataProvider->ConsumeFloatingPoint<float>();
+                        int32_t diagnosticFloatIndex =
+                                mFuzzedDataProvider->ConsumeIntegralInRange<int32_t>(
+                                        kMinSize,
+                                        toInt(DiagnosticFloatSensorIndex::LAST_SYSTEM_INDEX) +
+                                                diagnosticFloat);
+                        sensorStore->setFloatSensor(
+                                static_cast<DiagnosticFloatSensorIndex>(diagnosticFloatIndex),
+                                diagnosticFloatValue);
+                    },
+                    [&]() { sensorStore->getIntegerSensors(); },
+                    [&]() { sensorStore->getFloatSensors(); },
+                    [&]() { sensorStore->getSensorsBitmask(); },
+                    [&]() {
+                        for (auto&& dtc : kSampleDtcs) {
+                            VehiclePropertyType type = static_cast<VehiclePropertyType>(
+                                    mFuzzedDataProvider->ConsumeIntegral<int32_t>());
+                            auto freezeFrame = createVehiclePropValue(
+                                    type, mFuzzedDataProvider->ConsumeIntegralInRange<int32_t>(
+                                                  kMinSize, kMaxSize));
+                            if (!freezeFrame.get()) {
+                                return;
+                            }
+                            freezeFrame->prop = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
+                            sensorStore->fillPropValue(dtc, freezeFrame.get());
+                        }
+                    },
+            });
+    invokeObd2SensorStoreAPI();
 }
 
 void VehicleHalManagerFuzzer::invokeVmsUtils() {
-    bool availabilityMsgType = mFuzzedDataProvider->ConsumeBool();
-    bool subscriptionMsgType = mFuzzedDataProvider->ConsumeBool();
+    std::unique_ptr<VehiclePropValue> message;
     int32_t intValue = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
+    VmsLayer layer(mFuzzedDataProvider->ConsumeIntegral<int32_t>(),
+                   mFuzzedDataProvider->ConsumeIntegral<int32_t>(),
+                   mFuzzedDataProvider->ConsumeIntegral<int32_t>());
+    VmsOffers offers = {
+            intValue,
+            {VmsLayerOffering(VmsLayer(mFuzzedDataProvider->ConsumeIntegral<int32_t>(),
+                                       mFuzzedDataProvider->ConsumeIntegral<int32_t>(),
+                                       mFuzzedDataProvider->ConsumeIntegral<int32_t>()))}};
+    const VmsLayerAndPublisher layer_and_publisher(
+            VmsLayer(mFuzzedDataProvider->ConsumeIntegral<int32_t>(),
+                     mFuzzedDataProvider->ConsumeIntegral<int32_t>(),
+                     mFuzzedDataProvider->ConsumeIntegral<int32_t>()),
+            intValue);
 
-    VmsLayer layer(1, 0, 2);
-    auto message = createSubscribeMessage(layer);
+    switch (mFuzzedDataProvider->ConsumeIntegralInRange<int32_t>(kMinSize, kMaxCaseMessage)) {
+        case 0: {
+            message = createSubscribeMessage(layer);
+            break;
+        }
+        case 1: {
+            message = createUnsubscribeMessage(layer);
+            break;
+        }
+        case 2: {
+            message = createSubscriptionsRequest();
+            break;
+        }
+        case 3: {
+            message = createOfferingMessage(offers);
+            break;
+        }
+        case 4: {
+            message = createAvailabilityRequest();
+            break;
+        }
+        case 5: {
+            std::string pub_bytes;
+            if (mFuzzedDataProvider->ConsumeBool()) {
+                pub_bytes = "pub_id";
+            } else {
+                pub_bytes = mFuzzedDataProvider->ConsumeRandomLengthString(kMaxFileSize);
+            }
+            message = createPublisherIdRequest(pub_bytes);
+            break;
+        }
+        case 6: {
+            std::string bytes = "placeholder";
+            if (mFuzzedDataProvider->ConsumeBool()) {
+                bytes = "placeholder";
+            } else {
+                bytes = mFuzzedDataProvider->ConsumeRandomLengthString(kMaxFileSize);
+            }
+            message = createDataMessageWithLayerPublisherInfo(layer_and_publisher, bytes);
+            break;
+        }
+        case 7: {
+            message = createBaseVmsMessage(
+                    mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize));
+            break;
+        }
+        case 8: {
+            message = createStartSessionMessage(intValue, intValue + 1);
+            break;
+        }
+    }
+
     isValidVmsMessage(*message);
-    message = createUnsubscribeMessage(layer);
-
-    VmsOffers offers = {intValue, {VmsLayerOffering(VmsLayer(1, 0, 2))}};
-    message = createOfferingMessage(offers);
-    std::vector<VmsLayer> dependencies = {VmsLayer(2, 0, 2), VmsLayer(3, 0, 3)};
-    std::vector<VmsLayerOffering> offering = {VmsLayerOffering(layer, dependencies)};
-    offers = {intValue, offering};
-    message = createOfferingMessage(offers);
-
-    message = createAvailabilityRequest();
-    message = createSubscriptionsRequest();
-
-    std::string bytes = "placeholder";
-    const VmsLayerAndPublisher layer_and_publisher(VmsLayer(2, 0, 1), intValue);
-    message = createDataMessageWithLayerPublisherInfo(layer_and_publisher, bytes);
-    parseData(*message);
-    createSubscribeToPublisherMessage(layer_and_publisher);
-    createUnsubscribeToPublisherMessage(layer_and_publisher);
-
-    std::string pub_bytes = "pub_id";
-    message = createPublisherIdRequest(pub_bytes);
-    message = createBaseVmsMessage(2);
     message->value.int32Values =
-            hidl_vec<int32_t>{toInt(VmsMessageType::PUBLISHER_ID_RESPONSE), intValue};
-    parsePublisherIdResponse(*message);
+            hidl_vec<int32_t>{mFuzzedDataProvider->ConsumeIntegral<int32_t>(), intValue};
 
-    message->value.int32Values =
-            hidl_vec<int32_t>{toInt(kSubscriptionMessageType[subscriptionMsgType]), intValue};
-    getSequenceNumberForSubscriptionsState(*message);
-
-    message->value.int32Values = hidl_vec<int32_t>{toInt(kSubscriptionMessageType[0]), intValue};
-    isSequenceNumberNewer(*message, intValue + 1);
-    invokeGetSubscribedLayers(kSubscriptionMessageType[subscriptionMsgType]);
-
-    message->value.int32Values =
-            hidl_vec<int32_t>{toInt(kAvailabilityMessageType[availabilityMsgType]), 0};
-    hasServiceNewlyStarted(*message);
-    message = createStartSessionMessage(intValue, intValue + 1);
-    parseMessageType(*message);
-
-    message->value.int32Values =
-            hidl_vec<int32_t>{toInt(kAvailabilityMessageType[availabilityMsgType]), intValue};
-    isAvailabilitySequenceNumberNewer(*message, intValue + 1);
-
-    message->value.int32Values =
-            hidl_vec<int32_t>{toInt(kAvailabilityMessageType[availabilityMsgType]), intValue};
-    getSequenceNumberForAvailabilityState(*message);
-    message = createBaseVmsMessage(3);
-    int new_service_id;
-    message->value.int32Values = hidl_vec<int32_t>{toInt(VmsMessageType::START_SESSION), 0, -1};
-    parseStartSessionMessage(*message, -1, 0, &new_service_id);
+    auto invokeVmsUtilsAPI = mFuzzedDataProvider->PickValueInArray<const std::function<void()>>({
+            [&]() { parseData(*message); },
+            [&]() { createSubscribeToPublisherMessage(layer_and_publisher); },
+            [&]() { createUnsubscribeToPublisherMessage(layer_and_publisher); },
+            [&]() { parsePublisherIdResponse(*message); },
+            [&]() { getSequenceNumberForSubscriptionsState(*message); },
+            [&]() { isSequenceNumberNewer(*message, intValue + 1); },
+            [&]() {
+                invokeGetSubscribedLayers(
+                        (VmsMessageType)mFuzzedDataProvider->ConsumeIntegral<int32_t>());
+            },
+            [&]() { hasServiceNewlyStarted(*message); },
+            [&]() { parseMessageType(*message); },
+            [&]() { isAvailabilitySequenceNumberNewer(*message, intValue + 1); },
+            [&]() { getSequenceNumberForAvailabilityState(*message); },
+            [&]() {
+                int32_t new_service_id;
+                parseStartSessionMessage(*message, -1, 0, &new_service_id);
+            },
+    });
+    invokeVmsUtilsAPI();
 }
 
 void VehicleHalManagerFuzzer::invokeGet(int32_t property, int32_t areaId) {
@@ -367,27 +468,31 @@
     mActualStatusCode = refStatus;
 }
 
-void VehicleHalManagerFuzzer::invokeGetSubscribedLayers(VmsMessageType type) {
-    VmsOffers offers = {123,
-                        {VmsLayerOffering(VmsLayer(1, 0, 1), {VmsLayer(4, 1, 1)}),
-                         VmsLayerOffering(VmsLayer(2, 0, 1))}};
-    auto message = createBaseVmsMessage(16);
-    message->value.int32Values = hidl_vec<int32_t>{toInt(type),
-                                                   1234,  // sequence number
-                                                   2,     // number of layers
-                                                   1,     // number of associated layers
-                                                   1,     // layer 1
-                                                   0,           1,
-                                                   4,  // layer 2
-                                                   1,           1,
-                                                   2,  // associated layer
-                                                   0,           1,
-                                                   2,    // number of publisher IDs
-                                                   111,  // publisher IDs
-                                                   123};
-    isValidVmsMessage(*message);
-    getSubscribedLayers(*message, offers);
-    getAvailableLayers(*message);
+void VehicleHalManagerFuzzer::invokeGetSubscribedLayers(VmsMessageType /*type*/) {
+    int32_t intValue = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
+    VmsOffers offers = {
+            intValue,
+            {VmsLayerOffering(VmsLayer(mFuzzedDataProvider->ConsumeIntegral<int32_t>(),
+                                       mFuzzedDataProvider->ConsumeIntegral<int32_t>(),
+                                       mFuzzedDataProvider->ConsumeIntegral<int32_t>()))}};
+    auto message = createBaseVmsMessage(
+            mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(kMinSize, kMaxFileSize));
+    std::vector<int32_t> v;
+    size_t size = mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize);
+    for (size_t i = 0; i < size; i++) {
+        v.push_back(mFuzzedDataProvider->ConsumeIntegralInRange<int32_t>(kMinSize, kMaxSize));
+    }
+
+    message->value.int32Values = hidl_vec<int32_t>(v);
+    if (!isValidVmsMessage(*message)) {
+        return;
+    }
+
+    if (mFuzzedDataProvider->ConsumeBool()) {
+        getSubscribedLayers(*message, offers);
+    } else {
+        getAvailableLayers(*message);
+    }
 }
 
 void VehicleHalManagerFuzzer::invokeVehiclePropStore() {
@@ -398,33 +503,49 @@
             .prop = vehicleProp,
             .access = VehiclePropertyAccess::READ,
             .changeMode = VehiclePropertyChangeMode::STATIC,
-            .areaConfigs = {VehicleAreaConfig{.areaId = (0)}},
+            .areaConfigs = {VehicleAreaConfig{
+                    .areaId = (mFuzzedDataProvider->ConsumeIntegral<int32_t>())}},
     };
-    store->registerProperty(config);
     VehiclePropValue propValue{};
     propValue.prop = vehicleProp;
-    propValue.areaId = 0;
-    store->writeValue(propValue, shouldWriteStatus);
-    store->readAllValues();
-    store->getAllConfigs();
-    store->getConfigOrNull(vehicleProp);
-    store->readValuesForProperty(vehicleProp);
-    store->readValueOrNull(propValue);
-    store->readValueOrNull(propValue.prop, propValue.areaId, 0);
-    store->removeValuesForProperty(vehicleProp);
-    store->removeValue(propValue);
-    store->getConfigOrDie(vehicleProp);
+    propValue.areaId = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
+
+    auto invokeVehiclePropStoreAPI =
+            mFuzzedDataProvider->PickValueInArray<const std::function<void()>>({
+                    [&]() { store->registerProperty(config); },
+                    [&]() { store->writeValue(propValue, shouldWriteStatus); },
+                    [&]() { store->readAllValues(); },
+                    [&]() { store->getAllConfigs(); },
+                    [&]() { store->getConfigOrNull(vehicleProp); },
+                    [&]() { store->readValuesForProperty(vehicleProp); },
+                    [&]() { store->readValueOrNull(propValue); },
+                    [&]() {
+                        store->readValueOrNull(propValue.prop, propValue.areaId,
+                                               mFuzzedDataProvider->ConsumeIntegralInRange<int64_t>(
+                                                       kMinSize, kMaxFileSize));
+                    },
+                    [&]() { store->removeValuesForProperty(vehicleProp); },
+                    [&]() { store->removeValue(propValue); },
+                    [&]() {
+                        if (store->getConfigOrNull(vehicleProp)) {
+                            store->getConfigOrDie(vehicleProp);
+                        }
+                    },
+            });
+    invokeVehiclePropStoreAPI();
 }
 
 void VehicleHalManagerFuzzer::invokeWatchDogClient() {
-    auto service = new VehicleHalManager(mHal.get());
     sp<Looper> looper(Looper::prepare(/*opts=*/mFuzzedDataProvider->ConsumeBool()));
-    if (auto watchdogClient = ndk::SharedRefBase::make<WatchdogClient>(looper, service);
+    if (auto watchdogClient = ndk::SharedRefBase::make<WatchdogClient>(looper, mManager.get());
         watchdogClient->initialize()) {
-        watchdogClient->checkIfAlive(-1, TimeoutLength::TIMEOUT_NORMAL);
+        if (mFuzzedDataProvider->ConsumeBool()) {
+            watchdogClient->checkIfAlive(
+                    mFuzzedDataProvider->ConsumeIntegral<int32_t>(),
+                    (TimeoutLength)mFuzzedDataProvider->ConsumeIntegral<int32_t>());
+        }
         watchdogClient->prepareProcessTermination();
     }
-    delete service;
 }
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
diff --git a/automotive/vehicle/2.0/default/tests/fuzzer/VehicleManager_fuzzer.h b/automotive/vehicle/2.0/default/tests/fuzzer/VehicleManager_fuzzer.h
index e9335d3..26ac11e 100644
--- a/automotive/vehicle/2.0/default/tests/fuzzer/VehicleManager_fuzzer.h
+++ b/automotive/vehicle/2.0/default/tests/fuzzer/VehicleManager_fuzzer.h
@@ -98,6 +98,13 @@
     }
     void process(const uint8_t* data, size_t size);
 
+    template <typename T>
+    void fillParameter(size_t size, std::vector<T>& data) {
+        for (size_t i = 0; i < size; ++i) {
+            data.push_back(mFuzzedDataProvider->ConsumeIntegral<T>());
+        }
+    }
+
   private:
     FuzzedDataProvider* mFuzzedDataProvider = nullptr;
     VehiclePropValue mActualValue = VehiclePropValue{};
@@ -108,6 +115,7 @@
     std::unique_ptr<VehicleHalManager> mManager;
 
     void invokeDebug();
+    void initValue();
     void invokePropConfigs();
     void invokeSubscribe();
     void invokeSetAndGetValues();
diff --git a/automotive/vehicle/Android.bp b/automotive/vehicle/Android.bp
index f0abbcf..3549519 100644
--- a/automotive/vehicle/Android.bp
+++ b/automotive/vehicle/Android.bp
@@ -21,7 +21,7 @@
 cc_defaults {
     name: "VehicleHalInterfaceDefaults",
     static_libs: [
-        "android.hardware.automotive.vehicle-V2-ndk",
+        "android.hardware.automotive.vehicle-V3-ndk",
         "android.hardware.automotive.vehicle.property-V3-ndk",
     ],
 }
diff --git a/automotive/vehicle/OWNERS b/automotive/vehicle/OWNERS
index 429ec39..d6969e5 100644
--- a/automotive/vehicle/OWNERS
+++ b/automotive/vehicle/OWNERS
@@ -1,3 +1,2 @@
 ericjeong@google.com
-keunyoung@google.com
 shanyu@google.com
diff --git a/automotive/vehicle/TEST_MAPPING b/automotive/vehicle/TEST_MAPPING
index 02ad8bb..e1a90cb 100644
--- a/automotive/vehicle/TEST_MAPPING
+++ b/automotive/vehicle/TEST_MAPPING
@@ -51,6 +51,9 @@
           "include-filter": "com.android.car.hal.fakevhal.FakeVehicleStubUnitTest"
         }
       ]
+    },
+    {
+      "name": "VehicleHalProtoMessageConverterTest"
     }
   ]
 }
diff --git a/automotive/vehicle/aidl/Android.bp b/automotive/vehicle/aidl/Android.bp
index 3b93bca..3be0f28 100644
--- a/automotive/vehicle/aidl/Android.bp
+++ b/automotive/vehicle/aidl/Android.bp
@@ -27,7 +27,7 @@
     srcs: [
         "android/hardware/automotive/vehicle/*.aidl",
     ],
-    frozen: true,
+    frozen: false,
     stability: "vintf",
     backend: {
         cpp: {
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/SubscribeOptions.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/SubscribeOptions.aidl
index 91e7c14..1b1696b 100644
--- a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/SubscribeOptions.aidl
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/SubscribeOptions.aidl
@@ -37,4 +37,6 @@
   int propId;
   int[] areaIds;
   float sampleRate;
+  float resolution = 0.0f;
+  boolean enableVariableUpdateRate;
 }
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
index 6960894..08d4ee4 100644
--- a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
@@ -42,4 +42,6 @@
   float minFloatValue;
   float maxFloatValue;
   @nullable long[] supportedEnumValues;
+  android.hardware.automotive.vehicle.VehiclePropertyAccess access = android.hardware.automotive.vehicle.VehiclePropertyAccess.NONE;
+  boolean supportVariableUpdateRate;
 }
diff --git a/automotive/vehicle/aidl/aidl_test/Android.bp b/automotive/vehicle/aidl/aidl_test/Android.bp
index 79ac309..bb976af 100644
--- a/automotive/vehicle/aidl/aidl_test/Android.bp
+++ b/automotive/vehicle/aidl/aidl_test/Android.bp
@@ -51,10 +51,10 @@
         ":IVehicleGeneratedJavaFiles",
     ],
     static_libs: [
-        "android.hardware.automotive.vehicle-V2-java",
+        "android.hardware.automotive.vehicle-V3-java",
         "android.hardware.automotive.vehicle.property-V3-java",
         "androidx.test.runner",
-        "truth-prebuilt",
+        "truth",
     ],
     test_suites: ["general-tests"],
 }
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SubscribeOptions.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SubscribeOptions.aidl
index e68f7e3..69f6190 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SubscribeOptions.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/SubscribeOptions.aidl
@@ -39,4 +39,29 @@
      * This value indicates how many updates per second client wants to receive.
      */
     float sampleRate;
+
+    /**
+     * Requested resolution of property updates.
+     *
+     * This value indicates the resolution at which continuous property updates should be sent to
+     * the platform. For example, if resolution is 0.01, the subscribed property value should be
+     * rounded to two decimal places. If the incoming resolution value is not an integer multiple of
+     * 10, VHAL should return a StatusCode::INVALID_ARG.
+     */
+    float resolution = 0.0f;
+
+    /**
+     * Whether to enable variable update rate.
+     *
+     * This only applies for continuous property. If variable update rate is
+     * enabled, for each given areaId, if VHAL supports variable update rate for
+     * the [propId, areaId], VHAL must ignore duplicate property value events
+     * and only sends changed value events (a.k.a treat continuous as an
+     * on-change property).
+     *
+     * If VHAL does not support variable update rate for the [propId, areaId],
+     * indicated by 'supportVariableUpdateRate' in 'VehicleAreaConfig', or if
+     * this property is not a continuous property, this option must be ignored.
+     */
+    boolean enableVariableUpdateRate;
 }
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
index 20c046d..aab3c46 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleAreaConfig.aidl
@@ -16,6 +16,8 @@
 
 package android.hardware.automotive.vehicle;
 
+import android.hardware.automotive.vehicle.VehiclePropertyAccess;
+
 @VintfStability
 @JavaDerive(equals=true, toString=true)
 parcelable VehicleAreaConfig {
@@ -47,4 +49,55 @@
      * assumed all @data_enum values are supported unless specified through another mechanism.
      */
     @nullable long[] supportedEnumValues;
+
+    /**
+     * Defines if the area ID for this property is READ, WRITE or READ_WRITE. This only applies if
+     * the property is defined in the framework as a READ_WRITE property. Access (if set) should be
+     * equal to, or a superset of, the VehiclePropConfig.access of the property.
+     *
+     * For example, if a property is defined as READ_WRITE, but the OEM wants to specify certain
+     * area Ids as READ-only, the corresponding areaIds should have an access set to READ, while the
+     * others must be set to READ_WRITE. We do not support setting specific area Ids to WRITE-only
+     * when the property is READ-WRITE.
+     *
+     * Exclusively one of VehiclePropConfig and the VehicleAreaConfigs should be specified for a
+     * single property. If VehiclePropConfig.access is populated, none of the
+     * VehicleAreaConfig.access values should be populated. If VehicleAreaConfig.access values are
+     * populated, VehiclePropConfig.access must not be populated.
+     *
+     * VehicleAreaConfigs should not be partially populated with access. If the OEM wants to specify
+     * access for one area Id, all other configs should be populated with their access levels as
+     * well.
+     */
+    VehiclePropertyAccess access = VehiclePropertyAccess.NONE;
+
+    /**
+     * Whether variable update rate is supported.
+     *
+     * This applies for continuous property only.
+     *
+     * It is HIGHLY RECOMMENDED to support variable update rate for all non-heartbeat continuous
+     * properties for better performance unless the property is large.
+     *
+     * If variable update rate is supported and 'enableVariableUpdateRate' is true in subscribe
+     * options, VHAL must only sends property update event when the property's value changes
+     * (a.k.a treat continuous as an on-change property).
+     *
+     * E.g. if the client is subscribing at 5hz at time 0. If the property's value is 0 initially
+     * and becomes 1 after 1 second.
+
+     * If variable update rate is not enabled, VHAL clients will receive 5 property change events
+     * with value 0 and 5 events with value 1 after 2 seconds.
+     *
+     * If variable update rate is enabled, VHAL clients will receive 1 property change event
+     * with value 1 at time 1s. VHAL may/may not send a property event for the initial value (e.g.
+     * a property change event with value 0 at time 0s). VHAL client must not rely on the first
+     * property event, and must use getValues to fetch the initial value. In fact, car service is
+     * using getValues to fetch the initial value, convert it to a property event and deliver to
+     * car service clients.
+     *
+     * NOTE: If this is true, car service may cache the property update event for filtering purpose,
+     * so this should be false if the property is large (e.g. a byte array of 1k in size).
+     */
+    boolean supportVariableUpdateRate;
 }
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehiclePropConfig.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehiclePropConfig.aidl
index 61b9369..1135b26 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehiclePropConfig.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehiclePropConfig.aidl
@@ -28,6 +28,10 @@
 
     /**
      * Defines if the property is read or write or both.
+     *
+     * If populating VehicleAreaConfig.access fields for this property, this field should not be
+     * populated. If the OEM decides to populate this field, none of the VehicleAreaConfig.access
+     * fields should be populated.
      */
     VehiclePropertyAccess access = VehiclePropertyAccess.NONE;
 
diff --git a/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h b/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h
index d0c6e83..c4e2c64 100644
--- a/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h
+++ b/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -247,6 +247,7 @@
         {VehicleProperty::SUPPORTED_PROPERTY_IDS, VehiclePropertyAccess::READ},
         {VehicleProperty::SHUTDOWN_REQUEST, VehiclePropertyAccess::WRITE},
         {VehicleProperty::VEHICLE_IN_USE, VehiclePropertyAccess::READ_WRITE},
+        {VehicleProperty::CLUSTER_HEARTBEAT, VehiclePropertyAccess::WRITE},
         {VehicleProperty::AUTOMATIC_EMERGENCY_BRAKING_ENABLED, VehiclePropertyAccess::READ_WRITE},
         {VehicleProperty::AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyAccess::READ},
         {VehicleProperty::FORWARD_COLLISION_WARNING_ENABLED, VehiclePropertyAccess::READ_WRITE},
diff --git a/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h b/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h
index 48532c9..bd6c705 100644
--- a/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h
+++ b/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -247,6 +247,7 @@
         {VehicleProperty::SUPPORTED_PROPERTY_IDS, VehiclePropertyChangeMode::STATIC},
         {VehicleProperty::SHUTDOWN_REQUEST, VehiclePropertyChangeMode::ON_CHANGE},
         {VehicleProperty::VEHICLE_IN_USE, VehiclePropertyChangeMode::ON_CHANGE},
+        {VehicleProperty::CLUSTER_HEARTBEAT, VehiclePropertyChangeMode::ON_CHANGE},
         {VehicleProperty::AUTOMATIC_EMERGENCY_BRAKING_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
         {VehicleProperty::AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyChangeMode::ON_CHANGE},
         {VehicleProperty::FORWARD_COLLISION_WARNING_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
diff --git a/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java b/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java
index 758670d..6e66632 100644
--- a/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java
+++ b/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -239,6 +239,7 @@
         Map.entry(VehicleProperty.SUPPORTED_PROPERTY_IDS, VehiclePropertyAccess.READ),
         Map.entry(VehicleProperty.SHUTDOWN_REQUEST, VehiclePropertyAccess.WRITE),
         Map.entry(VehicleProperty.VEHICLE_IN_USE, VehiclePropertyAccess.READ_WRITE),
+        Map.entry(VehicleProperty.CLUSTER_HEARTBEAT, VehiclePropertyAccess.WRITE),
         Map.entry(VehicleProperty.AUTOMATIC_EMERGENCY_BRAKING_ENABLED, VehiclePropertyAccess.READ_WRITE),
         Map.entry(VehicleProperty.AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyAccess.READ),
         Map.entry(VehicleProperty.FORWARD_COLLISION_WARNING_ENABLED, VehiclePropertyAccess.READ_WRITE),
diff --git a/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java b/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java
index 29069f8..522c82f 100644
--- a/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java
+++ b/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -239,6 +239,7 @@
         Map.entry(VehicleProperty.SUPPORTED_PROPERTY_IDS, VehiclePropertyChangeMode.STATIC),
         Map.entry(VehicleProperty.SHUTDOWN_REQUEST, VehiclePropertyChangeMode.ON_CHANGE),
         Map.entry(VehicleProperty.VEHICLE_IN_USE, VehiclePropertyChangeMode.ON_CHANGE),
+        Map.entry(VehicleProperty.CLUSTER_HEARTBEAT, VehiclePropertyChangeMode.ON_CHANGE),
         Map.entry(VehicleProperty.AUTOMATIC_EMERGENCY_BRAKING_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
         Map.entry(VehicleProperty.AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyChangeMode.ON_CHANGE),
         Map.entry(VehicleProperty.FORWARD_COLLISION_WARNING_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
diff --git a/automotive/vehicle/aidl/generated_lib/java/EnumForVehicleProperty.java b/automotive/vehicle/aidl/generated_lib/java/EnumForVehicleProperty.java
new file mode 100644
index 0000000..144d0e1
--- /dev/null
+++ b/automotive/vehicle/aidl/generated_lib/java/EnumForVehicleProperty.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * DO NOT EDIT MANUALLY!!!
+ *
+ * Generated by tools/generate_annotation_enums.py.
+ */
+
+// clang-format off
+
+package android.hardware.automotive.vehicle;
+
+import java.util.List;
+import java.util.Map;
+
+public final class EnumForVehicleProperty {
+
+    public static final Map<Integer, List<Class<?>>> values = Map.ofEntries(
+        Map.entry(VehicleProperty.INFO_FUEL_TYPE, List.of(FuelType.class)),
+        Map.entry(VehicleProperty.INFO_EV_CONNECTOR_TYPE, List.of(EvConnectorType.class)),
+        Map.entry(VehicleProperty.INFO_FUEL_DOOR_LOCATION, List.of(PortLocationType.class)),
+        Map.entry(VehicleProperty.INFO_EV_PORT_LOCATION, List.of(PortLocationType.class)),
+        Map.entry(VehicleProperty.INFO_DRIVER_SEAT, List.of(VehicleAreaSeat.class)),
+        Map.entry(VehicleProperty.INFO_MULTI_EV_PORT_LOCATIONS, List.of(PortLocationType.class)),
+        Map.entry(VehicleProperty.ENGINE_OIL_LEVEL, List.of(VehicleOilLevel.class)),
+        Map.entry(VehicleProperty.GEAR_SELECTION, List.of(VehicleGear.class)),
+        Map.entry(VehicleProperty.CURRENT_GEAR, List.of(VehicleGear.class)),
+        Map.entry(VehicleProperty.TURN_SIGNAL_STATE, List.of(VehicleTurnSignal.class)),
+        Map.entry(VehicleProperty.IGNITION_STATE, List.of(VehicleIgnitionState.class)),
+        Map.entry(VehicleProperty.EV_STOPPING_MODE, List.of(EvStoppingMode.class)),
+        Map.entry(VehicleProperty.HVAC_FAN_DIRECTION, List.of(VehicleHvacFanDirection.class)),
+        Map.entry(VehicleProperty.HVAC_TEMPERATURE_DISPLAY_UNITS, List.of(VehicleUnit.class)),
+        Map.entry(VehicleProperty.HVAC_FAN_DIRECTION_AVAILABLE, List.of(VehicleHvacFanDirection.class)),
+        Map.entry(VehicleProperty.DISTANCE_DISPLAY_UNITS, List.of(VehicleUnit.class)),
+        Map.entry(VehicleProperty.FUEL_VOLUME_DISPLAY_UNITS, List.of(VehicleUnit.class)),
+        Map.entry(VehicleProperty.TIRE_PRESSURE_DISPLAY_UNITS, List.of(VehicleUnit.class)),
+        Map.entry(VehicleProperty.EV_BATTERY_DISPLAY_UNITS, List.of(VehicleUnit.class)),
+        Map.entry(VehicleProperty.HW_ROTARY_INPUT, List.of(RotaryInputType.class)),
+        Map.entry(VehicleProperty.HW_CUSTOM_INPUT, List.of(CustomInputType.class)),
+        Map.entry(VehicleProperty.SEAT_FOOTWELL_LIGHTS_STATE, List.of(VehicleLightState.class)),
+        Map.entry(VehicleProperty.SEAT_FOOTWELL_LIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
+        Map.entry(VehicleProperty.SEAT_OCCUPANCY, List.of(VehicleSeatOccupancyState.class)),
+        Map.entry(VehicleProperty.WINDSHIELD_WIPERS_STATE, List.of(WindshieldWipersState.class)),
+        Map.entry(VehicleProperty.WINDSHIELD_WIPERS_SWITCH, List.of(WindshieldWipersSwitch.class)),
+        Map.entry(VehicleProperty.HEADLIGHTS_STATE, List.of(VehicleLightState.class)),
+        Map.entry(VehicleProperty.HIGH_BEAM_LIGHTS_STATE, List.of(VehicleLightState.class)),
+        Map.entry(VehicleProperty.FOG_LIGHTS_STATE, List.of(VehicleLightState.class)),
+        Map.entry(VehicleProperty.HAZARD_LIGHTS_STATE, List.of(VehicleLightState.class)),
+        Map.entry(VehicleProperty.HEADLIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
+        Map.entry(VehicleProperty.HIGH_BEAM_LIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
+        Map.entry(VehicleProperty.FOG_LIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
+        Map.entry(VehicleProperty.HAZARD_LIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
+        Map.entry(VehicleProperty.CABIN_LIGHTS_STATE, List.of(VehicleLightState.class)),
+        Map.entry(VehicleProperty.CABIN_LIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
+        Map.entry(VehicleProperty.READING_LIGHTS_STATE, List.of(VehicleLightState.class)),
+        Map.entry(VehicleProperty.READING_LIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
+        Map.entry(VehicleProperty.STEERING_WHEEL_LIGHTS_STATE, List.of(VehicleLightState.class)),
+        Map.entry(VehicleProperty.STEERING_WHEEL_LIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
+        Map.entry(VehicleProperty.ELECTRONIC_TOLL_COLLECTION_CARD_TYPE, List.of(ElectronicTollCollectionCardType.class)),
+        Map.entry(VehicleProperty.ELECTRONIC_TOLL_COLLECTION_CARD_STATUS, List.of(ElectronicTollCollectionCardStatus.class)),
+        Map.entry(VehicleProperty.FRONT_FOG_LIGHTS_STATE, List.of(VehicleLightState.class)),
+        Map.entry(VehicleProperty.FRONT_FOG_LIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
+        Map.entry(VehicleProperty.REAR_FOG_LIGHTS_STATE, List.of(VehicleLightState.class)),
+        Map.entry(VehicleProperty.REAR_FOG_LIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
+        Map.entry(VehicleProperty.EV_CHARGE_STATE, List.of(EvChargeState.class)),
+        Map.entry(VehicleProperty.EV_REGENERATIVE_BRAKING_STATE, List.of(EvRegenerativeBrakingState.class)),
+        Map.entry(VehicleProperty.TRAILER_PRESENT, List.of(TrailerState.class)),
+        Map.entry(VehicleProperty.GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT, List.of(GsrComplianceRequirementType.class)),
+        Map.entry(VehicleProperty.SHUTDOWN_REQUEST, List.of(VehicleApPowerStateShutdownParam.class)),
+        Map.entry(VehicleProperty.AUTOMATIC_EMERGENCY_BRAKING_STATE, List.of(AutomaticEmergencyBrakingState.class, ErrorState.class)),
+        Map.entry(VehicleProperty.FORWARD_COLLISION_WARNING_STATE, List.of(ForwardCollisionWarningState.class, ErrorState.class)),
+        Map.entry(VehicleProperty.BLIND_SPOT_WARNING_STATE, List.of(BlindSpotWarningState.class, ErrorState.class)),
+        Map.entry(VehicleProperty.LANE_DEPARTURE_WARNING_STATE, List.of(LaneDepartureWarningState.class, ErrorState.class)),
+        Map.entry(VehicleProperty.LANE_KEEP_ASSIST_STATE, List.of(LaneKeepAssistState.class, ErrorState.class)),
+        Map.entry(VehicleProperty.LANE_CENTERING_ASSIST_COMMAND, List.of(LaneCenteringAssistCommand.class)),
+        Map.entry(VehicleProperty.LANE_CENTERING_ASSIST_STATE, List.of(LaneCenteringAssistState.class, ErrorState.class)),
+        Map.entry(VehicleProperty.EMERGENCY_LANE_KEEP_ASSIST_STATE, List.of(EmergencyLaneKeepAssistState.class, ErrorState.class)),
+        Map.entry(VehicleProperty.CRUISE_CONTROL_TYPE, List.of(CruiseControlType.class, ErrorState.class)),
+        Map.entry(VehicleProperty.CRUISE_CONTROL_STATE, List.of(CruiseControlState.class, ErrorState.class)),
+        Map.entry(VehicleProperty.CRUISE_CONTROL_COMMAND, List.of(CruiseControlCommand.class)),
+        Map.entry(VehicleProperty.HANDS_ON_DETECTION_DRIVER_STATE, List.of(HandsOnDetectionDriverState.class, ErrorState.class)),
+        Map.entry(VehicleProperty.HANDS_ON_DETECTION_WARNING, List.of(HandsOnDetectionWarning.class, ErrorState.class))
+    );
+
+}
diff --git a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/src/JsonConfigLoader.cpp b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/src/JsonConfigLoader.cpp
index 39ce10e..82dc8a6 100644
--- a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/src/JsonConfigLoader.cpp
+++ b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/src/JsonConfigLoader.cpp
@@ -275,6 +275,16 @@
 }
 
 template <>
+Result<bool> JsonValueParser::convertValueToType<bool>(const std::string& fieldName,
+                                                       const Json::Value& value) {
+    if (!value.isBool()) {
+        return Error() << "The value: " << value << " for field: " << fieldName
+                       << " is not in correct type, expect bool";
+    }
+    return value.asBool();
+}
+
+template <>
 Result<int32_t> JsonValueParser::convertValueToType<int32_t>(const std::string& fieldName,
                                                              const Json::Value& value) {
     if (!value.isInt()) {
@@ -531,6 +541,12 @@
         tryParseJsonValueToVariable(jsonAreaConfig, "maxFloatValue", /*optional=*/true,
                                     &areaConfig.maxFloatValue, errors);
 
+        // By default we support variable update rate for all properties except it is explicitly
+        // disabled.
+        areaConfig.supportVariableUpdateRate = true;
+        tryParseJsonValueToVariable(jsonAreaConfig, "supportVariableUpdateRate", /*optional=*/true,
+                                    &areaConfig.supportVariableUpdateRate, errors);
+
         std::vector<int64_t> supportedEnumValues;
         tryParseJsonArrayToVariable(jsonAreaConfig, "supportedEnumValues", /*optional=*/true,
                                     &supportedEnumValues, errors);
@@ -585,6 +601,16 @@
     if (errors->size() != initialErrorCount) {
         return std::nullopt;
     }
+
+    // If there is no area config, by default we allow variable update rate, so we have to add
+    // a global area config.
+    if (configDecl.config.areaConfigs.size() == 0) {
+        VehicleAreaConfig areaConfig = {
+                .areaId = 0,
+                .supportVariableUpdateRate = true,
+        };
+        configDecl.config.areaConfigs.push_back(std::move(areaConfig));
+    }
     return configDecl;
 }
 
diff --git a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
index b9f784b..d3bb60c 100644
--- a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
+++ b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
@@ -1899,8 +1899,16 @@
                 }
             ],
             "configArray": [
+                "VehicleProperty::HVAC_ACTUAL_FAN_SPEED_RPM",
+                "VehicleProperty::HVAC_AC_ON",
+                "VehicleProperty::HVAC_AUTO_ON",
+                "VehicleProperty::HVAC_AUTO_RECIRC_ON",
+                "VehicleProperty::HVAC_FAN_DIRECTION",
                 "VehicleProperty::HVAC_FAN_SPEED",
-                "VehicleProperty::HVAC_FAN_DIRECTION"
+                "VehicleProperty::HVAC_MAX_AC_ON",
+                "VehicleProperty::HVAC_RECIRC_ON",
+                "VehicleProperty::HVAC_TEMPERATURE_CURRENT",
+                "VehicleProperty::HVAC_TEMPERATURE_SET"
             ]
         },
         {
@@ -3571,7 +3579,13 @@
             "property": "VehicleProperty::WATCHDOG_TERMINATED_PROCESS"
         },
         {
-            "property": "VehicleProperty::VHAL_HEARTBEAT"
+            "property": "VehicleProperty::VHAL_HEARTBEAT",
+            "areas": [
+                {
+                    "areaId": 0,
+                    "supportVariableUpdateRate": false
+                }
+            ]
         },
         {
             "property": "VehicleProperty::CLUSTER_SWITCH_UI",
@@ -3621,6 +3635,27 @@
             "property": "VehicleProperty::CLUSTER_NAVIGATION_STATE"
         },
         {
+            "property": "VehicleProperty::CLUSTER_HEARTBEAT",
+            "configArray": [
+                0,
+                0,
+                0,
+                0,
+                0,
+                2,
+                0,
+                0,
+                16
+            ],
+            "areas": [
+                {
+                    "areaId": 0,
+                    "supportVariableUpdateRate": false
+                }
+            ],
+            "comment": "configArray specifies it consists of int64[2] and byte[16]."
+        },
+        {
             "property": "VehicleProperty::GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT",
             "defaultValue": {
                 "int32Values": [
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
index e8da43a..26fdee6 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
@@ -36,6 +36,7 @@
 #include <memory>
 #include <mutex>
 #include <unordered_map>
+#include <unordered_set>
 #include <vector>
 
 namespace android {
@@ -90,9 +91,13 @@
     void registerOnPropertySetErrorEvent(
             std::unique_ptr<const PropertySetErrorCallback> callback) override;
 
-    // Update the sample rate for the [propId, areaId] pair.
-    aidl::android::hardware::automotive::vehicle::StatusCode updateSampleRate(
-            int32_t propId, int32_t areaId, float sampleRate) override;
+    // Subscribe to a new [propId, areaId] or change the update rate.
+    aidl::android::hardware::automotive::vehicle::StatusCode subscribe(
+            aidl::android::hardware::automotive::vehicle::SubscribeOptions options) override;
+
+    // Unsubscribe to a [propId, areaId].
+    aidl::android::hardware::automotive::vehicle::StatusCode unsubscribe(int32_t propId,
+                                                                         int32_t areaId) override;
 
   protected:
     // mValuePool is also used in mServerSidePropStore.
@@ -153,6 +158,7 @@
             mRecurrentActions GUARDED_BY(mLock);
     std::unordered_map<PropIdAreaId, VehiclePropValuePool::RecyclableType, PropIdAreaIdHash>
             mSavedProps GUARDED_BY(mLock);
+    std::unordered_set<PropIdAreaId, PropIdAreaIdHash> mSubOnChangePropIdAreaIds GUARDED_BY(mLock);
     // PendingRequestHandler is thread-safe.
     mutable PendingRequestHandler<GetValuesCallback,
                                   aidl::android::hardware::automotive::vehicle::GetValueRequest>
@@ -161,6 +167,9 @@
                                   aidl::android::hardware::automotive::vehicle::SetValueRequest>
             mPendingSetValueRequests;
 
+    // Set of HVAC properties dependent on HVAC_POWER_ON
+    std::unordered_set<int32_t> hvacPowerDependentProps;
+
     const bool mForceOverride;
     bool mAddExtraTestVendorConfigs;
 
@@ -172,7 +181,8 @@
     void storePropInitialValue(const ConfigDeclaration& config);
     // The callback that would be called when a vehicle property value change happens.
     void onValueChangeCallback(
-            const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value);
+            const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value)
+            EXCLUDES(mLock);
     // Load the config files in format '*.json' from the directory and parse the config files
     // into a map from property ID to ConfigDeclarations.
     void loadPropConfigsFromDir(const std::string& dirPath,
@@ -212,6 +222,9 @@
             const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const;
     bool isHvacPropAndHvacNotAvailable(int32_t propId, int32_t areaId) const;
     VhalResult<void> isAdasPropertyAvailable(int32_t adasStatePropertyId) const;
+    VhalResult<void> synchronizeHvacTemp(int32_t hvacDualOnAreaId,
+                                         std::optional<float> newTempC) const;
+    std::optional<int32_t> getSyncedAreaIdIfHvacDualOn(int32_t hvacTemperatureSetAreaId) const;
 
     std::unordered_map<int32_t, ConfigDeclaration> loadConfigDeclarations();
 
@@ -253,11 +266,16 @@
             const aidl::android::hardware::automotive::vehicle::SetValueRequest& request);
 
     std::string genFakeDataCommand(const std::vector<std::string>& options);
-    void sendHvacPropertiesCurrentValues(int32_t areaId);
+    void sendHvacPropertiesCurrentValues(int32_t areaId, int32_t hvacPowerOnVal);
     void sendAdasPropertiesState(int32_t propertyId, int32_t state);
     void generateVendorConfigs(
             std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropConfig>&) const;
 
+    aidl::android::hardware::automotive::vehicle::StatusCode subscribePropIdAreaIdLocked(
+            int32_t propId, int32_t areaId, float sampleRateHz, bool enableVariableUpdateRate,
+            const aidl::android::hardware::automotive::vehicle::VehiclePropConfig&
+                    vehiclePropConfig) REQUIRES(mLock);
+
     static aidl::android::hardware::automotive::vehicle::VehiclePropValue createHwInputKeyProp(
             aidl::android::hardware::automotive::vehicle::VehicleHwKeyInputAction action,
             int32_t keyCode, int32_t targetDisplay);
@@ -271,6 +289,10 @@
 
     static std::string genFakeDataHelp();
     static std::string parseErrMsg(std::string fieldName, std::string value, std::string type);
+    static bool isVariableUpdateRateSupported(
+            const aidl::android::hardware::automotive::vehicle::VehiclePropConfig&
+                    vehiclePropConfig,
+            int32_t areaId);
 };
 
 }  // namespace fake
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
index 13c48c6..cc4fae1 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -60,6 +60,8 @@
 using ::aidl::android::hardware::automotive::vehicle::SetValueRequest;
 using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
 using ::aidl::android::hardware::automotive::vehicle::StatusCode;
+using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
+using ::aidl::android::hardware::automotive::vehicle::toString;
 using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReport;
 using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReq;
 using ::aidl::android::hardware::automotive::vehicle::VehicleArea;
@@ -67,6 +69,7 @@
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
 using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyGroup;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
@@ -200,6 +203,11 @@
     bool globalProp = isGlobalProp(propId);
     size_t numAreas = globalProp ? 1 : vehiclePropConfig.areaConfigs.size();
 
+    if (propId == toInt(VehicleProperty::HVAC_POWER_ON)) {
+        const auto& configArray = vehiclePropConfig.configArray;
+        hvacPowerDependentProps.insert(configArray.begin(), configArray.end());
+    }
+
     for (size_t i = 0; i < numAreas; i++) {
         int32_t curArea = globalProp ? 0 : vehiclePropConfig.areaConfigs[i].areaId;
 
@@ -511,9 +519,7 @@
 }
 
 bool FakeVehicleHardware::isHvacPropAndHvacNotAvailable(int32_t propId, int32_t areaId) const {
-    std::unordered_set<int32_t> powerProps(std::begin(HVAC_POWER_PROPERTIES),
-                                           std::end(HVAC_POWER_PROPERTIES));
-    if (powerProps.count(propId)) {
+    if (hvacPowerDependentProps.count(propId)) {
         auto hvacPowerOnResults =
                 mServerSidePropStore->readValuesForProperty(toInt(VehicleProperty::HVAC_POWER_ON));
         if (!hvacPowerOnResults.ok()) {
@@ -595,6 +601,65 @@
     return {};
 }
 
+VhalResult<void> FakeVehicleHardware::synchronizeHvacTemp(int32_t hvacDualOnAreaId,
+                                                          std::optional<float> newTempC) const {
+    auto hvacTemperatureSetResults = mServerSidePropStore->readValuesForProperty(
+            toInt(VehicleProperty::HVAC_TEMPERATURE_SET));
+    if (!hvacTemperatureSetResults.ok()) {
+        return StatusError(StatusCode::NOT_AVAILABLE)
+               << "Failed to get HVAC_TEMPERATURE_SET, error: "
+               << getErrorMsg(hvacTemperatureSetResults);
+    }
+    auto& hvacTemperatureSetValues = hvacTemperatureSetResults.value();
+    std::optional<float> tempCToSynchronize = newTempC;
+    for (size_t i = 0; i < hvacTemperatureSetValues.size(); i++) {
+        int32_t areaId = hvacTemperatureSetValues[i]->areaId;
+        if ((hvacDualOnAreaId & areaId) != areaId) {
+            continue;
+        }
+        if (hvacTemperatureSetValues[i]->status != VehiclePropertyStatus::AVAILABLE) {
+            continue;
+        }
+        // When HVAC_DUAL_ON is initially enabled, synchronize all area IDs
+        // to the temperature of the first area ID, which is the driver's.
+        if (!tempCToSynchronize.has_value()) {
+            tempCToSynchronize = hvacTemperatureSetValues[i]->value.floatValues[0];
+            continue;
+        }
+        auto updatedValue = std::move(hvacTemperatureSetValues[i]);
+        updatedValue->value.floatValues[0] = tempCToSynchronize.value();
+        updatedValue->timestamp = elapsedRealtimeNano();
+        // This will trigger a property change event for the current hvac property value.
+        auto writeResult =
+                mServerSidePropStore->writeValue(std::move(updatedValue), /*updateStatus=*/true,
+                                                 VehiclePropertyStore::EventMode::ALWAYS);
+        if (!writeResult.ok()) {
+            return StatusError(getErrorCode(writeResult))
+                   << "Failed to write value into property store, error: "
+                   << getErrorMsg(writeResult);
+        }
+    }
+    return {};
+}
+
+std::optional<int32_t> FakeVehicleHardware::getSyncedAreaIdIfHvacDualOn(
+        int32_t hvacTemperatureSetAreaId) const {
+    auto hvacDualOnResults =
+            mServerSidePropStore->readValuesForProperty(toInt(VehicleProperty::HVAC_DUAL_ON));
+    if (!hvacDualOnResults.ok()) {
+        return std::nullopt;
+    }
+    auto& hvacDualOnValues = hvacDualOnResults.value();
+    for (size_t i = 0; i < hvacDualOnValues.size(); i++) {
+        if ((hvacDualOnValues[i]->areaId & hvacTemperatureSetAreaId) == hvacTemperatureSetAreaId &&
+            hvacDualOnValues[i]->value.int32Values.size() == 1 &&
+            hvacDualOnValues[i]->value.int32Values[0] == 1) {
+            return hvacDualOnValues[i]->areaId;
+        }
+    }
+    return std::nullopt;
+}
+
 FakeVehicleHardware::ValueResultType FakeVehicleHardware::getUserHalProp(
         const VehiclePropValue& value) const {
     auto propId = value.prop;
@@ -731,9 +796,8 @@
     return std::move(gotValue);
 }
 
-void FakeVehicleHardware::sendHvacPropertiesCurrentValues(int32_t areaId) {
-    for (size_t i = 0; i < sizeof(HVAC_POWER_PROPERTIES) / sizeof(int32_t); i++) {
-        int powerPropId = HVAC_POWER_PROPERTIES[i];
+void FakeVehicleHardware::sendHvacPropertiesCurrentValues(int32_t areaId, int32_t hvacPowerOnVal) {
+    for (auto& powerPropId : hvacPowerDependentProps) {
         auto powerPropResults = mServerSidePropStore->readValuesForProperty(powerPropId);
         if (!powerPropResults.ok()) {
             ALOGW("failed to get power prop 0x%x, error: %s", powerPropId,
@@ -744,7 +808,8 @@
         for (size_t j = 0; j < powerPropValues.size(); j++) {
             auto powerPropValue = std::move(powerPropValues[j]);
             if ((powerPropValue->areaId & areaId) == powerPropValue->areaId) {
-                powerPropValue->status = VehiclePropertyStatus::AVAILABLE;
+                powerPropValue->status = hvacPowerOnVal ? VehiclePropertyStatus::AVAILABLE
+                                                        : VehiclePropertyStatus::UNAVAILABLE;
                 powerPropValue->timestamp = elapsedRealtimeNano();
                 // This will trigger a property change event for the current hvac property value.
                 mServerSidePropStore->writeValue(std::move(powerPropValue), /*updateStatus=*/true,
@@ -796,13 +861,6 @@
         return setUserHalProp(value);
     }
 
-    if (propId == toInt(VehicleProperty::HVAC_POWER_ON) && value.value.int32Values.size() == 1 &&
-        value.value.int32Values[0] == 1) {
-        // If we are turning HVAC power on, send current hvac property values through on change
-        // event.
-        sendHvacPropertiesCurrentValues(value.areaId);
-    }
-
     if (isHvacPropAndHvacNotAvailable(propId, value.areaId)) {
         *isSpecialValue = true;
         return StatusError(StatusCode::NOT_AVAILABLE_DISABLED) << "hvac not available";
@@ -841,9 +899,41 @@
         case toInt(TestVendorProperty::VENDOR_PROPERTY_FOR_ERROR_CODE_TESTING):
             *isSpecialValue = true;
             return StatusError((StatusCode)VENDOR_ERROR_CODE);
+        case toInt(VehicleProperty::HVAC_POWER_ON):
+            if (value.value.int32Values.size() != 1) {
+                *isSpecialValue = true;
+                return StatusError(StatusCode::INVALID_ARG)
+                       << "HVAC_POWER_ON requires only one int32 value";
+            }
+            // When changing HVAC power state, send current hvac property values
+            // through on change event.
+            sendHvacPropertiesCurrentValues(value.areaId, value.value.int32Values[0]);
+            return {};
         case toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION):
             *isSpecialValue = true;
             return setHvacTemperatureValueSuggestion(value);
+        case toInt(VehicleProperty::HVAC_TEMPERATURE_SET):
+            if (value.value.floatValues.size() != 1) {
+                *isSpecialValue = true;
+                return StatusError(StatusCode::INVALID_ARG)
+                       << "HVAC_DUAL_ON requires only one float value";
+            }
+            if (auto hvacDualOnAreaId = getSyncedAreaIdIfHvacDualOn(value.areaId);
+                hvacDualOnAreaId.has_value()) {
+                *isSpecialValue = true;
+                return synchronizeHvacTemp(hvacDualOnAreaId.value(), value.value.floatValues[0]);
+            }
+            return {};
+        case toInt(VehicleProperty::HVAC_DUAL_ON):
+            if (value.value.int32Values.size() != 1) {
+                *isSpecialValue = true;
+                return StatusError(StatusCode::INVALID_ARG)
+                       << "HVAC_DUAL_ON requires only one int32 value";
+            }
+            if (value.value.int32Values[0] == 1) {
+                synchronizeHvacTemp(value.areaId, std::nullopt);
+            }
+            return {};
         case toInt(VehicleProperty::LANE_CENTERING_ASSIST_COMMAND): {
             isAdasPropertyAvailableResult =
                     isAdasPropertyAvailable(toInt(VehicleProperty::LANE_CENTERING_ASSIST_STATE));
@@ -965,10 +1055,11 @@
     }
 
     auto updatedValue = mValuePool->obtain(value);
-    int64_t timestamp = elapsedRealtimeNano();
-    updatedValue->timestamp = timestamp;
 
-    auto writeResult = mServerSidePropStore->writeValue(std::move(updatedValue));
+    auto writeResult = mServerSidePropStore->writeValue(
+            std::move(updatedValue),
+            /*updateStatus=*/false, /*mode=*/VehiclePropertyStore::EventMode::ON_VALUE_CHANGE,
+            /*useCurrentTimestamp=*/true);
     if (!writeResult.ok()) {
         return StatusError(getErrorCode(writeResult))
                << StringPrintf("failed to write value into property store, error: %s",
@@ -1920,43 +2011,109 @@
     mOnPropertySetErrorCallback = std::move(callback);
 }
 
-StatusCode FakeVehicleHardware::updateSampleRate(int32_t propId, int32_t areaId, float sampleRate) {
-    // DefaultVehicleHal makes sure that sampleRate must be within minSampleRate and maxSampleRate.
-    // For fake implementation, we would write the same value with a new timestamp into propStore
-    // at sample rate.
-    std::scoped_lock<std::mutex> lockGuard(mLock);
+StatusCode FakeVehicleHardware::subscribe(SubscribeOptions options) {
+    int32_t propId = options.propId;
 
+    auto configResult = mServerSidePropStore->getConfig(propId);
+    if (!configResult.ok()) {
+        ALOGE("subscribe: property: %" PRId32 " is not supported", propId);
+        return StatusCode::INVALID_ARG;
+    }
+
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    for (int areaId : options.areaIds) {
+        if (StatusCode status = subscribePropIdAreaIdLocked(propId, areaId, options.sampleRate,
+                                                            options.enableVariableUpdateRate,
+                                                            *configResult.value());
+            status != StatusCode::OK) {
+            return status;
+        }
+    }
+    return StatusCode::OK;
+}
+
+bool FakeVehicleHardware::isVariableUpdateRateSupported(const VehiclePropConfig& vehiclePropConfig,
+                                                        int32_t areaId) {
+    for (size_t i = 0; i < vehiclePropConfig.areaConfigs.size(); i++) {
+        const auto& areaConfig = vehiclePropConfig.areaConfigs[i];
+        if (areaConfig.areaId != areaId) {
+            continue;
+        }
+        if (areaConfig.supportVariableUpdateRate) {
+            return true;
+        }
+        break;
+    }
+    return false;
+}
+
+StatusCode FakeVehicleHardware::subscribePropIdAreaIdLocked(
+        int32_t propId, int32_t areaId, float sampleRateHz, bool enableVariableUpdateRate,
+        const VehiclePropConfig& vehiclePropConfig) {
+    PropIdAreaId propIdAreaId{
+            .propId = propId,
+            .areaId = areaId,
+    };
+    switch (vehiclePropConfig.changeMode) {
+        case VehiclePropertyChangeMode::STATIC:
+            ALOGW("subscribe to a static property, do nothing.");
+            return StatusCode::OK;
+        case VehiclePropertyChangeMode::ON_CHANGE:
+            mSubOnChangePropIdAreaIds.insert(std::move(propIdAreaId));
+            return StatusCode::OK;
+        case VehiclePropertyChangeMode::CONTINUOUS:
+            if (sampleRateHz == 0.f) {
+                ALOGE("Must not use sample rate 0 for a continuous property");
+                return StatusCode::INTERNAL_ERROR;
+            }
+            if (mRecurrentActions.find(propIdAreaId) != mRecurrentActions.end()) {
+                mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propIdAreaId]);
+            }
+            int64_t intervalInNanos = static_cast<int64_t>(1'000'000'000. / sampleRateHz);
+
+            // For continuous properties, we must generate a new onPropertyChange event
+            // periodically according to the sample rate.
+            auto eventMode = VehiclePropertyStore::EventMode::ALWAYS;
+            if (isVariableUpdateRateSupported(vehiclePropConfig, areaId) &&
+                enableVariableUpdateRate) {
+                eventMode = VehiclePropertyStore::EventMode::ON_VALUE_CHANGE;
+            }
+            auto action = std::make_shared<RecurrentTimer::Callback>([this, propId, areaId,
+                                                                      eventMode] {
+                // Refresh the property value. In real implementation, this should poll the latest
+                // value from vehicle bus. Here, we are just refreshing the existing value with a
+                // new timestamp.
+                auto result = getValue(VehiclePropValue{
+                        .areaId = areaId,
+                        .prop = propId,
+                        .value = {},
+                });
+                if (!result.ok()) {
+                    // Failed to read current value, skip refreshing.
+                    return;
+                }
+
+                mServerSidePropStore->writeValue(std::move(result.value()),
+                                                 /*updateStatus=*/true, eventMode,
+                                                 /*useCurrentTimestamp=*/true);
+            });
+            mRecurrentTimer->registerTimerCallback(intervalInNanos, action);
+            mRecurrentActions[propIdAreaId] = action;
+            return StatusCode::OK;
+    }
+}
+
+StatusCode FakeVehicleHardware::unsubscribe(int32_t propId, int32_t areaId) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
     PropIdAreaId propIdAreaId{
             .propId = propId,
             .areaId = areaId,
     };
     if (mRecurrentActions.find(propIdAreaId) != mRecurrentActions.end()) {
         mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propIdAreaId]);
+        mRecurrentActions.erase(propIdAreaId);
     }
-    if (sampleRate == 0) {
-        return StatusCode::OK;
-    }
-    int64_t interval = static_cast<int64_t>(1'000'000'000. / sampleRate);
-    auto action = std::make_shared<RecurrentTimer::Callback>([this, propId, areaId] {
-        // Refresh the property value. In real implementation, this should poll the latest value
-        // from vehicle bus. Here, we are just refreshing the existing value with a new timestamp.
-        auto result = getValue(VehiclePropValue{
-                .areaId = areaId,
-                .prop = propId,
-                .value = {},
-        });
-        if (!result.ok()) {
-            // Failed to read current value, skip refreshing.
-            return;
-        }
-        result.value()->timestamp = elapsedRealtimeNano();
-        // For continuous properties, we must generate a new onPropertyChange event periodically
-        // according to the sample rate.
-        mServerSidePropStore->writeValue(std::move(result.value()), /*updateStatus=*/true,
-                                         VehiclePropertyStore::EventMode::ALWAYS);
-    });
-    mRecurrentTimer->registerTimerCallback(interval, action);
-    mRecurrentActions[propIdAreaId] = action;
+    mSubOnChangePropIdAreaIds.erase(propIdAreaId);
     return StatusCode::OK;
 }
 
@@ -1965,6 +2122,23 @@
         return;
     }
 
+    PropIdAreaId propIdAreaId{
+            .propId = value.prop,
+            .areaId = value.areaId,
+    };
+
+    {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
+        if (mRecurrentActions.find(propIdAreaId) == mRecurrentActions.end() &&
+            mSubOnChangePropIdAreaIds.find(propIdAreaId) == mSubOnChangePropIdAreaIds.end()) {
+            if (FAKE_VEHICLEHARDWARE_DEBUG) {
+                ALOGD("The updated property value: %s is not subscribed, ignore",
+                      value.toString().c_str());
+            }
+            return;
+        }
+    }
+
     std::vector<VehiclePropValue> updatedValues;
     updatedValues.push_back(value);
     (*mOnPropertyChangeCallback)(std::move(updatedValues));
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
index 6cc06bc..3b6f717 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
@@ -35,6 +35,7 @@
 #include <inttypes.h>
 #include <chrono>
 #include <condition_variable>
+#include <memory>
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
@@ -71,6 +72,7 @@
 using ::aidl::android::hardware::automotive::vehicle::SetValueRequest;
 using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
 using ::aidl::android::hardware::automotive::vehicle::StatusCode;
+using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
 using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReport;
 using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReq;
 using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateShutdownParam;
@@ -78,7 +80,9 @@
 using ::aidl::android::hardware::automotive::vehicle::VehicleHwKeyInputAction;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
 using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
 using ::aidl::android::hardware::automotive::vehicle::VehicleUnit;
 using ::android::base::expected;
@@ -91,6 +95,7 @@
 using ::testing::Eq;
 using ::testing::HasSubstr;
 using ::testing::IsSubsetOf;
+using ::testing::UnorderedElementsAre;
 using ::testing::WhenSortedBy;
 
 using std::chrono::milliseconds;
@@ -109,6 +114,10 @@
         return mHardware->loadConfigDeclarations();
     }
 
+    std::unordered_set<int32_t> getHvacPowerDependentProps() {
+        return mHardware->hvacPowerDependentProps;
+    }
+
   private:
     FakeVehicleHardware* mHardware;
 };
@@ -142,6 +151,15 @@
         mHardware = std::move(hardware);
     }
 
+    static SubscribeOptions newSubscribeOptions(int32_t propId, int32_t areaId,
+                                                float sampleRateHz) {
+        SubscribeOptions options;
+        options.areaIds = {areaId};
+        options.propId = propId;
+        options.sampleRate = sampleRateHz;
+        return options;
+    }
+
     StatusCode setValues(const std::vector<SetValueRequest>& requests) {
         {
             std::scoped_lock<std::mutex> lockGuard(mLock);
@@ -329,6 +347,13 @@
         return mEventCount[propIdAreaId];
     }
 
+    void subscribe(int32_t propId, int32_t areaId, float sampleRateHz) {
+        ASSERT_EQ(StatusCode::OK,
+                  getHardware()->subscribe(newSubscribeOptions(propId, areaId, sampleRateHz)))
+                << "failed to subscribe to propId: " << propId << "areaId: " << areaId
+                << ", sampleRateHz: " << sampleRateHz;
+    }
+
     static void addSetValueRequest(std::vector<SetValueRequest>& requests,
                                    std::vector<SetValueResult>& expectedResults, int64_t requestId,
                                    const VehiclePropValue& value, StatusCode expectedStatus) {
@@ -363,24 +388,24 @@
     }
 
     std::vector<VehiclePropValue> getTestPropValues() {
-        VehiclePropValue fuelCapacity = {
-                .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY),
-                .value = {.floatValues = {1.0}},
+        VehiclePropValue oilLevel = {
+                .prop = toInt(VehicleProperty::ENGINE_OIL_LEVEL),
+                .value = {.int32Values = {1}},
         };
 
-        VehiclePropValue leftTirePressure = {
-                .prop = toInt(VehicleProperty::TIRE_PRESSURE),
+        VehiclePropValue leftHvacTemp = {
+                .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_CURRENT),
                 .value = {.floatValues = {170.0}},
-                .areaId = WHEEL_FRONT_LEFT,
+                .areaId = SEAT_1_LEFT,
         };
 
-        VehiclePropValue rightTirePressure = {
-                .prop = toInt(VehicleProperty::TIRE_PRESSURE),
+        VehiclePropValue rightHvacTemp = {
+                .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_CURRENT),
                 .value = {.floatValues = {180.0}},
-                .areaId = WHEEL_FRONT_RIGHT,
+                .areaId = SEAT_1_RIGHT,
         };
 
-        return {fuelCapacity, leftTirePressure, rightTirePressure};
+        return {oilLevel, leftHvacTemp, rightHvacTemp};
     }
 
     struct PropValueCmp {
@@ -390,6 +415,25 @@
         }
     } mPropValueCmp;
 
+    std::unique_ptr<VehiclePropConfig> getVehiclePropConfig(int32_t propertyId) {
+        auto configs = mHardware->getAllPropertyConfigs();
+        for (auto& config : configs) {
+            if (config.prop == propertyId) {
+                auto ptr = std::make_unique<VehiclePropConfig>();
+                ptr->prop = config.prop;
+                ptr->access = config.access;
+                ptr->changeMode = config.changeMode;
+                ptr->areaConfigs = config.areaConfigs;
+                ptr->configArray = config.configArray;
+                ptr->configString = config.configString;
+                ptr->minSampleRate = config.minSampleRate;
+                ptr->maxSampleRate = config.maxSampleRate;
+                return ptr;
+            }
+        }
+        return std::unique_ptr<VehiclePropConfig>(nullptr);
+    }
+
   private:
     std::unique_ptr<FakeVehicleHardware> mHardware;
     std::shared_ptr<IVehicleHardware::SetValuesCallback> mSetValuesCallback;
@@ -411,6 +455,29 @@
     ASSERT_EQ(configs.size(), helper.loadConfigDeclarations().size());
 }
 
+TEST_F(FakeVehicleHardwareTest, testGetAllPropertyConfigs_defaultSupportVUR) {
+    std::vector<VehiclePropConfig> configs = getHardware()->getAllPropertyConfigs();
+
+    for (const auto& config : configs) {
+        bool expectedSupportVUR = true;
+        if (config.prop == toInt(VehicleProperty::VHAL_HEARTBEAT) ||
+            config.prop == toInt(VehicleProperty::CLUSTER_HEARTBEAT)) {
+            expectedSupportVUR = false;
+        }
+        EXPECT_GE(config.areaConfigs.size(), 1u)
+                << "expect at least one area config, including global area config, propId: "
+                << config.prop;
+        if (config.areaConfigs.size() == 0) {
+            continue;
+        }
+        for (const auto& areaConfig : config.areaConfigs) {
+            EXPECT_EQ(areaConfig.supportVariableUpdateRate, expectedSupportVUR)
+                    << "unexpected supportVariableUpdateRate for propId: " << config.prop
+                    << ", areaId: " << areaConfig.areaId;
+        }
+    }
+}
+
 TEST_F(FakeVehicleHardwareTest, testGetDefaultValues) {
     std::vector<GetValueRequest> getValueRequests;
     std::vector<GetValueResult> expectedGetValueResults;
@@ -533,17 +600,13 @@
     ASSERT_THAT(getSetValueResults(), ContainerEq(expectedResults));
 }
 
-TEST_F(FakeVehicleHardwareTest, testRegisterOnPropertyChangeEvent) {
-    // We have already registered this callback in Setup, here we are registering again.
-    auto callback = std::make_unique<IVehicleHardware::PropertyChangeCallback>(
-            [this](const std::vector<VehiclePropValue>& values) { onPropertyChangeEvent(values); });
-    getHardware()->registerOnPropertyChangeEvent(std::move(callback));
-
+TEST_F(FakeVehicleHardwareTest, testSetValues_getUpdateEvents) {
     auto testValues = getTestPropValues();
     std::vector<SetValueRequest> requests;
     std::vector<SetValueResult> expectedResults;
     int64_t requestId = 1;
     for (auto& value : testValues) {
+        subscribe(value.prop, value.areaId, /*sampleRateHz=*/0);
         addSetValueRequest(requests, expectedResults, requestId++, value, StatusCode::OK);
     }
     int64_t timestamp = elapsedRealtimeNano();
@@ -1598,27 +1661,30 @@
             return info.param.name;
         });
 
-TEST_F(FakeVehicleHardwareTest, testSetWaitForVhalAfterCarServiceCrash) {
-    int32_t propId = toInt(VehicleProperty::AP_POWER_STATE_REPORT);
+TEST_F(FakeVehicleHardwareTest, testSetWaitForVhal_alwaysTriggerEvents) {
+    int32_t powerReq = toInt(VehicleProperty::AP_POWER_STATE_REQ);
+    subscribe(powerReq, /*areaId*/ 0, /*sampleRateHz*/ 0);
+
+    int32_t powerReport = toInt(VehicleProperty::AP_POWER_STATE_REPORT);
     VehiclePropValue request = VehiclePropValue{
-            .prop = propId,
+            .prop = powerReport,
             .value.int32Values = {toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL)},
     };
-    ASSERT_EQ(setValue(request), StatusCode::OK) << "failed to set property " << propId;
+    ASSERT_EQ(setValue(request), StatusCode::OK) << "failed to set property " << powerReport;
 
     // Clear existing events.
     clearChangedProperties();
 
     // Simulate a Car Service crash, Car Service would restart and send the message again.
-    ASSERT_EQ(setValue(request), StatusCode::OK) << "failed to set property " << propId;
+    ASSERT_EQ(setValue(request), StatusCode::OK) << "failed to set property " << powerReport;
 
     std::vector<VehiclePropValue> events = getChangedProperties();
     // Even though the state is already ON, we should receive another ON event.
-    ASSERT_EQ(events.size(), 1u);
+    ASSERT_EQ(events.size(), 1u) << "failed to receive on-change events AP_POWER_STATE_REQ ON";
     // Erase the timestamp for comparison.
     events[0].timestamp = 0;
     auto expectedValue = VehiclePropValue{
-            .prop = toInt(VehicleProperty::AP_POWER_STATE_REQ),
+            .prop = powerReq,
             .status = VehiclePropertyStatus::AVAILABLE,
             .value.int32Values = {toInt(VehicleApPowerStateReq::ON), 0},
     };
@@ -1687,23 +1753,35 @@
 }
 
 TEST_F(FakeVehicleHardwareTest, testGetHvacPropNotAvailable) {
-    int seatAreaIds[5] = {SEAT_1_LEFT, SEAT_1_RIGHT, SEAT_2_LEFT, SEAT_2_CENTER, SEAT_2_RIGHT};
-    for (int areaId : seatAreaIds) {
+    FakeVehicleHardwareTestHelper helper(getHardware());
+    auto hvacPowerOnConfig = std::move(getVehiclePropConfig(toInt(VehicleProperty::HVAC_POWER_ON)));
+    EXPECT_NE(hvacPowerOnConfig, nullptr);
+    for (auto& hvacPowerOnAreaConfig : hvacPowerOnConfig->areaConfigs) {
+        int hvacPowerAreaId = hvacPowerOnAreaConfig.areaId;
+        // Turn off HVAC_POWER_ON for only 1 area ID
         StatusCode status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_POWER_ON),
-                                                      .areaId = areaId,
+                                                      .areaId = hvacPowerAreaId,
                                                       .value.int32Values = {0}});
+        EXPECT_EQ(status, StatusCode::OK);
 
-        ASSERT_EQ(status, StatusCode::OK);
-
-        for (size_t i = 0; i < sizeof(HVAC_POWER_PROPERTIES) / sizeof(int32_t); i++) {
-            int powerPropId = HVAC_POWER_PROPERTIES[i];
-            for (int powerDependentAreaId : seatAreaIds) {
+        for (auto& powerPropId : helper.getHvacPowerDependentProps()) {
+            auto powerPropConfig = std::move(getVehiclePropConfig(powerPropId));
+            EXPECT_NE(powerPropConfig, nullptr);
+            if (powerPropConfig->access == VehiclePropertyAccess::WRITE) {
+                continue;
+            }
+            // Try getting a value at each area ID supported by the power dependent property
+            for (auto& powerPropAreaConfig : powerPropConfig->areaConfigs) {
+                int powerDependentAreaId = powerPropAreaConfig.areaId;
                 auto getValueResult = getValue(VehiclePropValue{
                         .prop = powerPropId,
                         .areaId = powerDependentAreaId,
                 });
 
-                if (areaId == powerDependentAreaId) {
+                // If the current area ID is contained within the HVAC_POWER_ON area ID
+                // turned off, then getValue should fail and a StatusCode error should be
+                // returned. Otherwise, a value should be returned.
+                if ((hvacPowerAreaId & powerDependentAreaId) == powerDependentAreaId) {
                     EXPECT_FALSE(getValueResult.ok());
                     EXPECT_EQ(getValueResult.error(), StatusCode::NOT_AVAILABLE_DISABLED);
                 } else {
@@ -1716,28 +1794,45 @@
         // on this value from any power dependent property values other than those with the same
         // areaId.
         setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_POWER_ON),
-                                  .areaId = areaId,
+                                  .areaId = hvacPowerAreaId,
                                   .value.int32Values = {1}});
     }
 }
 
 TEST_F(FakeVehicleHardwareTest, testSetHvacPropNotAvailable) {
-    int seatAreaIds[5] = {SEAT_1_LEFT, SEAT_1_RIGHT, SEAT_2_LEFT, SEAT_2_CENTER, SEAT_2_RIGHT};
-    for (int areaId : seatAreaIds) {
+    FakeVehicleHardwareTestHelper helper(getHardware());
+    auto hvacPowerOnConfig = std::move(getVehiclePropConfig(toInt(VehicleProperty::HVAC_POWER_ON)));
+    EXPECT_NE(hvacPowerOnConfig, nullptr);
+    for (auto& hvacPowerOnAreaConfig : hvacPowerOnConfig->areaConfigs) {
+        int hvacPowerAreaId = hvacPowerOnAreaConfig.areaId;
+        // Turn off HVAC_POWER_ON for only 1 area ID
         StatusCode status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_POWER_ON),
-                                                      .areaId = areaId,
+                                                      .areaId = hvacPowerAreaId,
                                                       .value.int32Values = {0}});
+        EXPECT_EQ(status, StatusCode::OK);
 
-        ASSERT_EQ(status, StatusCode::OK);
+        for (auto& powerPropId : helper.getHvacPowerDependentProps()) {
+            auto powerPropConfig = std::move(getVehiclePropConfig(powerPropId));
+            EXPECT_NE(powerPropConfig, nullptr);
+            if (powerPropConfig->access == VehiclePropertyAccess::READ) {
+                continue;
+            }
+            auto propType = getPropType(powerPropId);
+            // Try setting a value at each area ID supported by the power dependent property
+            for (auto& powerPropAreaConfig : powerPropConfig->areaConfigs) {
+                int powerDependentAreaId = powerPropAreaConfig.areaId;
+                auto val = VehiclePropValue{.prop = powerPropId, .areaId = powerDependentAreaId};
+                if (propType == VehiclePropertyType::FLOAT) {
+                    val.value.floatValues.emplace_back(20);
+                } else {
+                    val.value.int32Values.emplace_back(1);
+                }
+                status = setValue(val);
 
-        for (size_t i = 0; i < sizeof(HVAC_POWER_PROPERTIES) / sizeof(int32_t); i++) {
-            int powerPropId = HVAC_POWER_PROPERTIES[i];
-            for (int powerDependentAreaId : seatAreaIds) {
-                StatusCode status = setValue(VehiclePropValue{.prop = powerPropId,
-                                                              .areaId = powerDependentAreaId,
-                                                              .value.int32Values = {1}});
-
-                if (areaId == powerDependentAreaId) {
+                // If the current area ID is contained within the HVAC_POWER_ON area ID
+                // turned off, then setValue should fail and a StatusCode error should be
+                // returned. Otherwise, an ok StatusCode should be returned.
+                if ((hvacPowerAreaId & powerDependentAreaId) == powerDependentAreaId) {
                     EXPECT_EQ(status, StatusCode::NOT_AVAILABLE_DISABLED);
                 } else {
                     EXPECT_EQ(status, StatusCode::OK);
@@ -1749,37 +1844,142 @@
         // on this value from any power dependent property values other than those with the same
         // areaId.
         setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_POWER_ON),
-                                  .areaId = areaId,
+                                  .areaId = hvacPowerAreaId,
                                   .value.int32Values = {1}});
     }
 }
 
 TEST_F(FakeVehicleHardwareTest, testHvacPowerOnSendCurrentHvacPropValues) {
-    int seatAreaIds[5] = {SEAT_1_LEFT, SEAT_1_RIGHT, SEAT_2_LEFT, SEAT_2_CENTER, SEAT_2_RIGHT};
-    for (int areaId : seatAreaIds) {
+    FakeVehicleHardwareTestHelper helper(getHardware());
+    auto hvacPowerOnConfig = std::move(getVehiclePropConfig(toInt(VehicleProperty::HVAC_POWER_ON)));
+    EXPECT_NE(hvacPowerOnConfig, nullptr);
+    for (auto& hvacPowerOnAreaConfig : hvacPowerOnConfig->areaConfigs) {
+        int hvacPowerAreaId = hvacPowerOnAreaConfig.areaId;
         StatusCode status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_POWER_ON),
-                                                      .areaId = areaId,
+                                                      .areaId = hvacPowerAreaId,
                                                       .value.int32Values = {0}});
-
-        ASSERT_EQ(status, StatusCode::OK);
-
-        clearChangedProperties();
-        setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_POWER_ON),
-                                  .areaId = areaId,
-                                  .value.int32Values = {1}});
-
+        EXPECT_EQ(status, StatusCode::OK);
         auto events = getChangedProperties();
-        // If we turn HVAC power on, we expect to receive one property event for every HVAC prop
-        // areas plus one event for HVAC_POWER_ON.
-        std::vector<int32_t> changedPropIds;
-        for (size_t i = 0; i < sizeof(HVAC_POWER_PROPERTIES) / sizeof(int32_t); i++) {
-            changedPropIds.push_back(HVAC_POWER_PROPERTIES[i]);
-        }
-        changedPropIds.push_back(toInt(VehicleProperty::HVAC_POWER_ON));
-
         for (const auto& event : events) {
-            EXPECT_EQ(event.areaId, areaId);
-            EXPECT_THAT(event.prop, AnyOfArray(changedPropIds));
+            // Ignore HVAC_POWER_ON event
+            if (event.prop == toInt(VehicleProperty::HVAC_POWER_ON)) {
+                continue;
+            }
+            EXPECT_THAT(event.prop, AnyOfArray(helper.getHvacPowerDependentProps()));
+            EXPECT_EQ((hvacPowerAreaId & event.areaId), hvacPowerAreaId);
+            EXPECT_EQ(event.status, VehiclePropertyStatus::UNAVAILABLE);
+        }
+        clearChangedProperties();
+
+        status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_POWER_ON),
+                                           .areaId = hvacPowerAreaId,
+                                           .value.int32Values = {1}});
+        EXPECT_EQ(status, StatusCode::OK);
+        events = getChangedProperties();
+        for (const auto& event : events) {
+            // Ignore HVAC_POWER_ON event
+            if (event.prop == toInt(VehicleProperty::HVAC_POWER_ON)) {
+                continue;
+            }
+            EXPECT_THAT(event.prop, AnyOfArray(helper.getHvacPowerDependentProps()));
+            EXPECT_EQ((hvacPowerAreaId & event.areaId), hvacPowerAreaId);
+            EXPECT_EQ(event.status, VehiclePropertyStatus::AVAILABLE);
+        }
+        clearChangedProperties();
+    }
+}
+
+TEST_F(FakeVehicleHardwareTest, testHvacDualOnSynchronizesTemp) {
+    auto hvacDualOnConfig = std::move(getVehiclePropConfig(toInt(VehicleProperty::HVAC_DUAL_ON)));
+    auto hvacTemperatureSetConfig =
+            std::move(getVehiclePropConfig(toInt(VehicleProperty::HVAC_TEMPERATURE_SET)));
+    EXPECT_NE(hvacDualOnConfig, nullptr);
+    EXPECT_NE(hvacTemperatureSetConfig, nullptr);
+    for (auto& hvacTemperatureSetConfig : hvacTemperatureSetConfig->areaConfigs) {
+        int32_t hvacTemperatureSetAreaId = hvacTemperatureSetConfig.areaId;
+        subscribe(toInt(VehicleProperty::HVAC_TEMPERATURE_SET), hvacTemperatureSetAreaId,
+                  /*sampleRateHz*/ 0);
+    }
+    for (auto& hvacDualOnConfig : hvacDualOnConfig->areaConfigs) {
+        int32_t hvacDualOnAreaId = hvacDualOnConfig.areaId;
+        subscribe(toInt(VehicleProperty::HVAC_DUAL_ON), hvacDualOnAreaId, /*sampleRateHz*/ 0);
+        StatusCode status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_DUAL_ON),
+                                                      .areaId = hvacDualOnAreaId,
+                                                      .value.int32Values = {1}});
+        EXPECT_EQ(status, StatusCode::OK);
+
+        // Verify there's an event for all HVAC_TEMPERATURE_SET
+        // area IDs covered by the HVAC_DUAL_ON area ID
+        auto events = getChangedProperties();
+        std::unordered_set<float> temperatureValues;
+        for (const auto& event : events) {
+            // Ignore HVAC_DUAL_ON event
+            if (event.prop == toInt(VehicleProperty::HVAC_DUAL_ON)) {
+                continue;
+            }
+            EXPECT_EQ(event.prop, toInt(VehicleProperty::HVAC_TEMPERATURE_SET));
+            EXPECT_EQ((hvacDualOnAreaId & event.areaId), event.areaId);
+            EXPECT_EQ(1u, event.value.floatValues.size());
+            temperatureValues.insert(event.value.floatValues[0]);
+        }
+        // Verify that the temperature value is the same for all events
+        // Ie the temperature in all area IDs are synchronized
+        EXPECT_EQ(1u, temperatureValues.size());
+        clearChangedProperties();
+
+        // Verify when any HVAC_TEMPERATURE_SET area ID is changed all
+        // area IDs covered by the HVAC_DUAL_ON area ID are also changed
+        for (auto& hvacTemperatureSetConfig : hvacTemperatureSetConfig->areaConfigs) {
+            int32_t hvacTemperatureSetAreaId = hvacTemperatureSetConfig.areaId;
+            if ((hvacDualOnAreaId & hvacTemperatureSetAreaId) != hvacTemperatureSetAreaId) {
+                continue;
+            }
+            float expectedValue = 25;
+            status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_SET),
+                                               .areaId = hvacTemperatureSetAreaId,
+                                               .value.floatValues = {expectedValue}});
+            EXPECT_EQ(status, StatusCode::OK);
+            events = getChangedProperties();
+            for (const auto& event : events) {
+                EXPECT_EQ(event.prop, toInt(VehicleProperty::HVAC_TEMPERATURE_SET));
+                EXPECT_EQ(1u, event.value.floatValues.size());
+                EXPECT_EQ(expectedValue, event.value.floatValues[0]);
+            }
+            clearChangedProperties();
+        }
+
+        status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_DUAL_ON),
+                                           .areaId = hvacDualOnAreaId,
+                                           .value.int32Values = {0}});
+        EXPECT_EQ(status, StatusCode::OK);
+
+        // When HVAC_DUAL_ON is disabled, there should be no events created
+        // for HVAC_TEMPERATURE_SET ie no temperature synchronization.
+        events = getChangedProperties();
+        EXPECT_EQ(1u, events.size());
+        EXPECT_EQ(events[0].prop, toInt(VehicleProperty::HVAC_DUAL_ON));
+        EXPECT_EQ(events[0].areaId, hvacDualOnAreaId);
+        clearChangedProperties();
+
+        // Verify when any HVAC_TEMPERATURE_SET area ID is
+        // changed other area IDs do not change.
+        for (auto& hvacTemperatureSetConfig : hvacTemperatureSetConfig->areaConfigs) {
+            int32_t hvacTemperatureSetAreaId = hvacTemperatureSetConfig.areaId;
+            if ((hvacDualOnAreaId & hvacTemperatureSetAreaId) != hvacTemperatureSetAreaId) {
+                continue;
+            }
+            float expectedValue = 24;
+            status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_SET),
+                                               .areaId = hvacTemperatureSetAreaId,
+                                               .value.floatValues = {expectedValue}});
+            EXPECT_EQ(status, StatusCode::OK);
+            events = getChangedProperties();
+            EXPECT_EQ(1u, events.size());
+            EXPECT_EQ(events[0].prop, toInt(VehicleProperty::HVAC_TEMPERATURE_SET));
+            EXPECT_EQ(events[0].areaId, hvacTemperatureSetAreaId);
+            EXPECT_EQ(1u, events[0].value.floatValues.size());
+            EXPECT_EQ(expectedValue, events[0].value.floatValues[0]);
+            clearChangedProperties();
         }
     }
 }
@@ -1950,6 +2150,22 @@
                     },
             },
     };
+
+    // First subscribe to all the properties that we will change.
+    for (auto& enabledToErrorStateProps : adasEnabledPropToAdasPropWithErrorState) {
+        std::unordered_set<int32_t> expectedChangedPropIds(enabledToErrorStateProps.second.begin(),
+                                                           enabledToErrorStateProps.second.end());
+        expectedChangedPropIds.insert(enabledToErrorStateProps.first);
+
+        for (int32_t propId : expectedChangedPropIds) {
+            int32_t areaId = 0;
+            if (propId == toInt(VehicleProperty::BLIND_SPOT_WARNING_STATE)) {
+                areaId = toInt(VehicleAreaMirror::DRIVER_LEFT);
+            }
+            subscribe(propId, areaId, /*sampleRateHz*/ 0);
+        }
+    }
+
     for (auto& enabledToErrorStateProps : adasEnabledPropToAdasPropWithErrorState) {
         int32_t adasEnabledPropertyId = enabledToErrorStateProps.first;
         StatusCode status =
@@ -2030,9 +2246,16 @@
 }
 
 TEST_F(FakeVehicleHardwareTest, testSwitchUser) {
+    SubscribeOptions options;
+    int32_t propSwitchUser = toInt(VehicleProperty::SWITCH_USER);
+    options.propId = propSwitchUser;
+    options.areaIds = {0, 1};
+    ASSERT_EQ(StatusCode::OK, getHardware()->subscribe(options))
+            << "failed to subscribe to propId: " << propSwitchUser;
+
     // This is the same example as used in User HAL Emulation doc.
     VehiclePropValue valueToSet = {
-            .prop = toInt(VehicleProperty::SWITCH_USER),
+            .prop = propSwitchUser,
             .areaId = 1,
             .value.int32Values = {666, 3, 2},
     };
@@ -2043,7 +2266,7 @@
 
     // Simulate a request from Android side.
     VehiclePropValue switchUserRequest = {
-            .prop = toInt(VehicleProperty::SWITCH_USER),
+            .prop = propSwitchUser,
             .areaId = 0,
             .value.int32Values = {666, 3},
     };
@@ -2073,7 +2296,7 @@
     events[0].timestamp = 0;
     auto expectedValue = VehiclePropValue{
             .areaId = 0,
-            .prop = toInt(VehicleProperty::SWITCH_USER),
+            .prop = propSwitchUser,
             .value.int32Values =
                     {
                             // Request ID
@@ -2088,6 +2311,13 @@
 }
 
 TEST_F(FakeVehicleHardwareTest, testCreateUser) {
+    SubscribeOptions options;
+    int32_t propCreateUser = toInt(VehicleProperty::CREATE_USER);
+    options.propId = propCreateUser;
+    options.areaIds = {0, 1};
+    ASSERT_EQ(StatusCode::OK, getHardware()->subscribe(options))
+            << "failed to subscribe to propId: " << propCreateUser;
+
     // This is the same example as used in User HAL Emulation doc.
     VehiclePropValue valueToSet = {
             .prop = toInt(VehicleProperty::CREATE_USER),
@@ -2101,7 +2331,7 @@
 
     // Simulate a request from Android side.
     VehiclePropValue createUserRequest = {
-            .prop = toInt(VehicleProperty::CREATE_USER),
+            .prop = propCreateUser,
             .areaId = 0,
             .value.int32Values = {666},
     };
@@ -2130,7 +2360,7 @@
     events[0].timestamp = 0;
     auto expectedValue = VehiclePropValue{
             .areaId = 0,
-            .prop = toInt(VehicleProperty::CREATE_USER),
+            .prop = propCreateUser,
             .value.int32Values =
                     {
                             // Request ID
@@ -2143,9 +2373,16 @@
 }
 
 TEST_F(FakeVehicleHardwareTest, testInitialUserInfo) {
+    SubscribeOptions options;
+    int32_t propInitialUserInfo = toInt(VehicleProperty::INITIAL_USER_INFO);
+    options.propId = propInitialUserInfo;
+    options.areaIds = {0, 1};
+    ASSERT_EQ(StatusCode::OK, getHardware()->subscribe(options))
+            << "failed to subscribe to propId: " << propInitialUserInfo;
+
     // This is the same example as used in User HAL Emulation doc.
     VehiclePropValue valueToSet = {
-            .prop = toInt(VehicleProperty::INITIAL_USER_INFO),
+            .prop = propInitialUserInfo,
             .areaId = 1,
             .value.int32Values = {666, 1, 11},
     };
@@ -2156,7 +2393,7 @@
 
     // Simulate a request from Android side.
     VehiclePropValue initialUserInfoRequest = {
-            .prop = toInt(VehicleProperty::INITIAL_USER_INFO),
+            .prop = propInitialUserInfo,
             .areaId = 0,
             .value.int32Values = {3},
     };
@@ -2173,7 +2410,7 @@
     events[0].timestamp = 0;
     auto expectedValue = VehiclePropValue{
             .areaId = 0,
-            .prop = toInt(VehicleProperty::INITIAL_USER_INFO),
+            .prop = propInitialUserInfo,
             .value.int32Values = {3, 1, 11},
     };
     EXPECT_EQ(events[0], expectedValue);
@@ -2188,7 +2425,7 @@
     events[0].timestamp = 0;
     expectedValue = VehiclePropValue{
             .areaId = 0,
-            .prop = toInt(VehicleProperty::INITIAL_USER_INFO),
+            .prop = propInitialUserInfo,
             .value.int32Values =
                     {
                             // Request ID
@@ -2330,13 +2567,14 @@
 }
 
 TEST_F(FakeVehicleHardwareTest, testDumpInjectEvent) {
-    int32_t prop = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
+    int32_t prop = toInt(VehicleProperty::ENGINE_OIL_LEVEL);
     std::string propIdStr = std::to_string(prop);
 
+    subscribe(prop, /*areaId*/ 0, /*sampleRateHz*/ 0);
+
     int64_t timestamp = elapsedRealtimeNano();
-    // Inject an event with float value 123.4 and timestamp.
     DumpResult result = getHardware()->dump(
-            {"--inject-event", propIdStr, "-f", "123.4", "-t", std::to_string(timestamp)});
+            {"--inject-event", propIdStr, "-i", "1234", "-t", std::to_string(timestamp)});
 
     ASSERT_FALSE(result.callerShouldDumpState);
     ASSERT_THAT(result.buffer,
@@ -2347,7 +2585,7 @@
     ASSERT_EQ(events.size(), 1u);
     auto event = events[0];
     ASSERT_EQ(event.timestamp, timestamp);
-    ASSERT_EQ(event.value.floatValues, std::vector<float>({123.4}));
+    ASSERT_EQ(event.value.int32Values, std::vector<int32_t>({1234}));
 }
 
 TEST_F(FakeVehicleHardwareTest, testDumpInvalidOptions) {
@@ -2690,9 +2928,13 @@
         });
 
 TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataLinear) {
-    // Start a fake linear data generator for vehicle speed at 0.1s interval.
+    // Start a fake linear data generator for engine oil level at 0.1s interval.
     // range: 0 - 100, current value: 30, step: 20.
-    std::string propIdString = StringPrintf("%d", toInt(VehicleProperty::PERF_VEHICLE_SPEED));
+    int32_t prop = toInt(VehicleProperty::ENGINE_OIL_LEVEL);
+
+    subscribe(prop, /*areaId*/ 0, /*sampleRateHz*/ 0);
+
+    std::string propIdString = StringPrintf("%d", prop);
     std::vector<std::string> options = {"--genfakedata",         "--startlinear", propIdString,
                                         /*middleValue=*/"50",
                                         /*currentValue=*/"30",
@@ -2705,15 +2947,14 @@
     ASSERT_FALSE(result.callerShouldDumpState);
     ASSERT_THAT(result.buffer, HasSubstr("successfully"));
 
-    ASSERT_TRUE(waitForChangedProperties(toInt(VehicleProperty::PERF_VEHICLE_SPEED), 0, /*count=*/5,
-                                         milliseconds(1000)))
+    ASSERT_TRUE(waitForChangedProperties(prop, 0, /*count=*/5, milliseconds(1000)))
             << "not enough events generated for linear data generator";
 
     int32_t value = 30;
     auto events = getChangedProperties();
     for (size_t i = 0; i < 5; i++) {
-        ASSERT_EQ(1u, events[i].value.floatValues.size());
-        EXPECT_EQ(static_cast<float>(value), events[i].value.floatValues[0]);
+        ASSERT_EQ(1u, events[i].value.int32Values.size());
+        EXPECT_EQ(value, events[i].value.int32Values[0]);
         value = (value + 20) % 100;
     }
 
@@ -2729,7 +2970,7 @@
     std::this_thread::sleep_for(std::chrono::milliseconds(200));
 
     // There should be no new events generated.
-    EXPECT_EQ(0u, getEventCount(toInt(VehicleProperty::PERF_VEHICLE_SPEED), 0));
+    EXPECT_EQ(0u, getEventCount(prop, 0));
 }
 
 std::string getTestFilePath(const char* filename) {
@@ -2738,6 +2979,8 @@
 }
 
 TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJson) {
+    subscribe(toInt(VehicleProperty::GEAR_SELECTION), /*areaId*/ 0, /*sampleRateHz*/ 0);
+
     std::vector<std::string> options = {"--genfakedata", "--startjson", "--path",
                                         getTestFilePath("prop.json"), "2"};
 
@@ -2764,6 +3007,8 @@
 }
 
 TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonByContent) {
+    subscribe(toInt(VehicleProperty::GEAR_SELECTION), /*areaId*/ 0, /*sampleRateHz*/ 0);
+
     std::vector<std::string> options = {
             "--genfakedata", "--startjson", "--content",
             "[{\"timestamp\":1000000,\"areaId\":0,\"value\":8,\"prop\":289408000}]", "1"};
@@ -2838,8 +3083,11 @@
 }
 
 TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataKeyPress) {
+    int32_t propHwKeyInput = toInt(VehicleProperty::HW_KEY_INPUT);
     std::vector<std::string> options = {"--genfakedata", "--keypress", "1", "2"};
 
+    subscribe(propHwKeyInput, /*areaId*/ 0, /*sampleRateHz*/ 0);
+
     DumpResult result = getHardware()->dump(options);
 
     ASSERT_FALSE(result.callerShouldDumpState);
@@ -2847,8 +3095,8 @@
 
     auto events = getChangedProperties();
     ASSERT_EQ(2u, events.size());
-    EXPECT_EQ(toInt(VehicleProperty::HW_KEY_INPUT), events[0].prop);
-    EXPECT_EQ(toInt(VehicleProperty::HW_KEY_INPUT), events[1].prop);
+    EXPECT_EQ(propHwKeyInput, events[0].prop);
+    EXPECT_EQ(propHwKeyInput, events[1].prop);
     ASSERT_EQ(3u, events[0].value.int32Values.size());
     ASSERT_EQ(3u, events[1].value.int32Values.size());
     EXPECT_EQ(toInt(VehicleHwKeyInputAction::ACTION_DOWN), events[0].value.int32Values[0]);
@@ -2860,8 +3108,11 @@
 }
 
 TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataKeyInputV2) {
+    int32_t propHwKeyInputV2 = toInt(VehicleProperty::HW_KEY_INPUT_V2);
     std::vector<std::string> options = {"--genfakedata", "--keyinputv2", "1", "2", "3", "4", "5"};
 
+    subscribe(propHwKeyInputV2, /*areaId*/ 1, /*sampleRateHz*/ 0);
+
     DumpResult result = getHardware()->dump(options);
 
     ASSERT_FALSE(result.callerShouldDumpState);
@@ -2879,6 +3130,7 @@
 }
 
 TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataMotionInput) {
+    int32_t propHwMotionInput = toInt(VehicleProperty::HW_MOTION_INPUT);
     std::vector<std::string> options = {"--genfakedata",
                                         "--motioninput",
                                         "1",
@@ -2901,6 +3153,8 @@
                                         "65.5",
                                         "76.6"};
 
+    subscribe(propHwMotionInput, /*areaId*/ 1, /*sampleRateHz*/ 0);
+
     DumpResult result = getHardware()->dump(options);
 
     ASSERT_FALSE(result.callerShouldDumpState);
@@ -2908,7 +3162,7 @@
 
     auto events = getChangedProperties();
     ASSERT_EQ(1u, events.size());
-    EXPECT_EQ(toInt(VehicleProperty::HW_MOTION_INPUT), events[0].prop);
+    EXPECT_EQ(propHwMotionInput, events[0].prop);
     ASSERT_EQ(9u, events[0].value.int32Values.size());
     EXPECT_EQ(2, events[0].value.int32Values[0]);
     EXPECT_EQ(3, events[0].value.int32Values[1]);
@@ -2949,23 +3203,27 @@
     ASSERT_EQ(result.value().value.byteValues, std::vector<uint8_t>({0x04, 0x03, 0x02, 0x01}));
 }
 
-TEST_F(FakeVehicleHardwareTest, testUpdateSampleRate) {
+TEST_F(FakeVehicleHardwareTest, testSubscribeUnsubscribe_continuous) {
     int32_t propSpeed = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
     int32_t propSteering = toInt(VehicleProperty::PERF_STEERING_ANGLE);
     int32_t areaId = 0;
-    getHardware()->updateSampleRate(propSpeed, areaId, 5);
+
+    auto status = getHardware()->subscribe(newSubscribeOptions(propSpeed, areaId, 5));
+    ASSERT_EQ(status, StatusCode::OK) << "failed to subscribe";
 
     ASSERT_TRUE(waitForChangedProperties(propSpeed, areaId, /*count=*/5, milliseconds(1500)))
             << "not enough events generated for speed";
 
-    getHardware()->updateSampleRate(propSteering, areaId, 10);
+    status = getHardware()->subscribe(newSubscribeOptions(propSteering, areaId, 10));
+    ASSERT_EQ(status, StatusCode::OK) << "failed to subscribe";
 
     ASSERT_TRUE(waitForChangedProperties(propSteering, areaId, /*count=*/10, milliseconds(1500)))
             << "not enough events generated for steering";
 
     int64_t timestamp = elapsedRealtimeNano();
     // Disable refreshing for propSpeed.
-    getHardware()->updateSampleRate(propSpeed, areaId, 0);
+    status = getHardware()->unsubscribe(propSpeed, areaId);
+    ASSERT_EQ(status, StatusCode::OK) << "failed to unsubscribe";
     clearChangedProperties();
 
     ASSERT_TRUE(waitForChangedProperties(propSteering, areaId, /*count=*/5, milliseconds(1500)))
@@ -2978,12 +3236,99 @@
     }
 }
 
+TEST_F(FakeVehicleHardwareTest, testSubscribe_enableVUR) {
+    int32_t propSpeed = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
+    int32_t areaId = 0;
+    SubscribeOptions options;
+    options.propId = propSpeed;
+    options.areaIds = {areaId};
+    options.enableVariableUpdateRate = true;
+    options.sampleRate = 5;
+    int64_t timestamp = elapsedRealtimeNano();
+
+    auto status = getHardware()->subscribe(options);
+    ASSERT_EQ(status, StatusCode::OK) << "failed to subscribe";
+
+    status = setValue({
+            .prop = propSpeed,
+            .areaId = 0,
+            .value.floatValues = {1.1f},
+    });
+    ASSERT_EQ(status, StatusCode::OK) << "failed to set speed";
+
+    status = setValue({
+            .prop = propSpeed,
+            .areaId = 0,
+            .value.floatValues = {1.2f},
+    });
+    ASSERT_EQ(status, StatusCode::OK) << "failed to set speed";
+
+    ASSERT_TRUE(waitForChangedProperties(propSpeed, areaId, /*count=*/2, milliseconds(100)))
+            << "not enough events generated for speed";
+    auto updatedValues = getChangedProperties();
+    std::unordered_set<float> gotValues;
+    for (auto& value : updatedValues) {
+        EXPECT_GE(value.timestamp, timestamp) << "timestamp must be updated";
+        EXPECT_EQ(value.prop, propSpeed) << "propId must be correct";
+        EXPECT_EQ(value.areaId, areaId) << "areaId must be correct";
+        gotValues.insert(value.value.floatValues[0]);
+    }
+    EXPECT_THAT(gotValues, UnorderedElementsAre(1.1f, 1.2f))
+            << "must only receive property event for changed value";
+}
+
+TEST_F(FakeVehicleHardwareTest, testSubscribeUnusubscribe_onChange) {
+    int32_t propHvac = toInt(VehicleProperty::HVAC_TEMPERATURE_SET);
+    int32_t areaId = SEAT_1_LEFT;
+
+    auto status = getHardware()->subscribe(newSubscribeOptions(propHvac, areaId, 0));
+    ASSERT_EQ(status, StatusCode::OK) << "failed to subscribe";
+
+    status = setValue({
+            .prop = propHvac,
+            .areaId = areaId,
+            .value.floatValues = {20.0f},
+    });
+    ASSERT_EQ(status, StatusCode::OK) << "failed to set hvac value";
+
+    ASSERT_TRUE(waitForChangedProperties(propHvac, areaId, /*count=*/1, milliseconds(100)))
+            << "not enough on change events generated for hvac";
+    clearChangedProperties();
+
+    status = setValue({
+            .prop = propHvac,
+            .areaId = areaId,
+            .value.floatValues = {21.0f},
+    });
+    ASSERT_EQ(status, StatusCode::OK) << "failed to set hvac value";
+
+    ASSERT_TRUE(waitForChangedProperties(propHvac, areaId, /*count=*/1, milliseconds(100)))
+            << "not enough on change events generated for hvac";
+    clearChangedProperties();
+
+    status = getHardware()->unsubscribe(propHvac, areaId);
+    ASSERT_EQ(status, StatusCode::OK);
+
+    status = setValue({
+            .prop = propHvac,
+            .areaId = areaId,
+            .value.floatValues = {22.0f},
+    });
+    ASSERT_EQ(status, StatusCode::OK) << "failed to set hvac value";
+
+    ASSERT_FALSE(waitForChangedProperties(propHvac, areaId, /*count=*/1, milliseconds(100)))
+            << "must not receive on change events if the propId, areaId is unsubscribed";
+}
+
 TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) {
     float CELSIUS = static_cast<float>(toInt(VehicleUnit::CELSIUS));
     float FAHRENHEIT = static_cast<float>(toInt(VehicleUnit::FAHRENHEIT));
+    int32_t propHvacTempValueSuggest = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION);
+
+    subscribe(propHvacTempValueSuggest, HVAC_ALL, /*sampleRateHz*/ 0);
 
     VehiclePropValue floatArraySizeFour = {
-            .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
+            .prop = propHvacTempValueSuggest,
             .areaId = HVAC_ALL,
             .value.floatValues = {0, CELSIUS, 0, 0},
     };
@@ -2991,14 +3336,14 @@
     EXPECT_EQ(status, StatusCode::OK);
 
     VehiclePropValue floatArraySizeZero = {
-            .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
+            .prop = propHvacTempValueSuggest,
             .areaId = HVAC_ALL,
     };
     status = setValue(floatArraySizeZero);
     EXPECT_EQ(status, StatusCode::INVALID_ARG);
 
     VehiclePropValue floatArraySizeFive = {
-            .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
+            .prop = propHvacTempValueSuggest,
             .areaId = HVAC_ALL,
             .value.floatValues = {0, CELSIUS, 0, 0, 0},
     };
@@ -3006,7 +3351,7 @@
     EXPECT_EQ(status, StatusCode::INVALID_ARG);
 
     VehiclePropValue invalidUnit = {
-            .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
+            .prop = propHvacTempValueSuggest,
             .areaId = HVAC_ALL,
             .value.floatValues = {0, 0, 0, 0},
     };
@@ -3016,13 +3361,8 @@
 
     // Config array values from HVAC_TEMPERATURE_SET in DefaultProperties.json
     auto configs = getHardware()->getAllPropertyConfigs();
-    VehiclePropConfig* hvacTemperatureSetConfig = nullptr;
-    for (auto& config : configs) {
-        if (config.prop == toInt(VehicleProperty::HVAC_TEMPERATURE_SET)) {
-            hvacTemperatureSetConfig = &config;
-            break;
-        }
-    }
+    auto hvacTemperatureSetConfig =
+            std::move(getVehiclePropConfig(toInt(VehicleProperty::HVAC_TEMPERATURE_SET)));
     EXPECT_NE(hvacTemperatureSetConfig, nullptr);
 
     auto& hvacTemperatureSetConfigArray = hvacTemperatureSetConfig->configArray;
@@ -3042,9 +3382,7 @@
                     .valuesToSet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues = {minTempInCelsius, CELSIUS, 0, 0},
                                     },
@@ -3052,9 +3390,7 @@
                     .expectedValuesToGet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues = {minTempInCelsius, CELSIUS,
                                                                   minTempInCelsius,
@@ -3067,9 +3403,7 @@
                     .valuesToSet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues = {minTempInFahrenheit, FAHRENHEIT,
                                                                   0, 0},
@@ -3078,9 +3412,7 @@
                     .expectedValuesToGet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues = {minTempInFahrenheit, FAHRENHEIT,
                                                                   minTempInCelsius,
@@ -3093,9 +3425,7 @@
                     .valuesToSet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues = {maxTempInCelsius, CELSIUS, 0, 0},
                                     },
@@ -3103,9 +3433,7 @@
                     .expectedValuesToGet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues = {maxTempInCelsius, CELSIUS,
                                                                   maxTempInCelsius,
@@ -3118,9 +3446,7 @@
                     .valuesToSet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues = {maxTempInFahrenheit, FAHRENHEIT,
                                                                   0, 0},
@@ -3129,9 +3455,7 @@
                     .expectedValuesToGet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues = {maxTempInFahrenheit, FAHRENHEIT,
                                                                   maxTempInCelsius,
@@ -3144,9 +3468,7 @@
                     .valuesToSet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues = {minTempInCelsius - 1, CELSIUS, 0,
                                                                   0},
@@ -3155,9 +3477,7 @@
                     .expectedValuesToGet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues = {minTempInCelsius - 1, CELSIUS,
                                                                   minTempInCelsius,
@@ -3170,9 +3490,7 @@
                     .valuesToSet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues = {minTempInFahrenheit - 1,
                                                                   FAHRENHEIT, 0, 0},
@@ -3181,9 +3499,7 @@
                     .expectedValuesToGet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues = {minTempInFahrenheit - 1,
                                                                   FAHRENHEIT, minTempInCelsius,
@@ -3196,9 +3512,7 @@
                     .valuesToSet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues = {maxTempInCelsius + 1, CELSIUS, 0,
                                                                   0},
@@ -3207,9 +3521,7 @@
                     .expectedValuesToGet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues = {maxTempInCelsius + 1, CELSIUS,
                                                                   maxTempInCelsius,
@@ -3222,9 +3534,7 @@
                     .valuesToSet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues = {maxTempInFahrenheit + 1,
                                                                   FAHRENHEIT, 0, 0},
@@ -3233,9 +3543,7 @@
                     .expectedValuesToGet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues = {maxTempInFahrenheit + 1,
                                                                   FAHRENHEIT, maxTempInCelsius,
@@ -3248,9 +3556,7 @@
                     .valuesToSet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues = {minTempInCelsius +
                                                                           incrementInCelsius * 2.5f,
@@ -3260,9 +3566,7 @@
                     .expectedValuesToGet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues =
                                                     {minTempInCelsius + incrementInCelsius * 2.5f,
@@ -3278,9 +3582,7 @@
                     .valuesToSet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues = {minTempInFahrenheit +
                                                                           incrementInFahrenheit *
@@ -3291,9 +3593,7 @@
                     .expectedValuesToGet =
                             {
                                     VehiclePropValue{
-                                            .prop = toInt(
-                                                    VehicleProperty::
-                                                            HVAC_TEMPERATURE_VALUE_SUGGESTION),
+                                            .prop = propHvacTempValueSuggest,
                                             .areaId = HVAC_ALL,
                                             .value.floatValues =
                                                     {minTempInFahrenheit +
diff --git a/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/src/ProtoMessageConverter.cpp b/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/src/ProtoMessageConverter.cpp
index 6b789bb..491aa10 100644
--- a/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/src/ProtoMessageConverter.cpp
+++ b/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/src/ProtoMessageConverter.cpp
@@ -78,6 +78,7 @@
                 protoACfg->add_supported_enum_values(supportedEnumValue);
             }
         }
+        protoACfg->set_support_variable_update_rate(areaConfig.supportVariableUpdateRate);
     }
 }
 
@@ -100,9 +101,14 @@
                 .maxInt64Value = protoAcfg.max_int64_value(),
                 .minFloatValue = protoAcfg.min_float_value(),
                 .maxFloatValue = protoAcfg.max_float_value(),
+                .supportVariableUpdateRate = protoAcfg.support_variable_update_rate(),
         };
-        COPY_PROTOBUF_VEC_TO_VHAL_TYPE(protoAcfg, supported_enum_values, (&vehicleAreaConfig),
-                                       supportedEnumValues.value());
+        if (protoAcfg.supported_enum_values().size() != 0) {
+            vehicleAreaConfig.supportedEnumValues = std::vector<int64_t>();
+            COPY_PROTOBUF_VEC_TO_VHAL_TYPE(protoAcfg, supported_enum_values, (&vehicleAreaConfig),
+                                           supportedEnumValues.value());
+        }
+
         return vehicleAreaConfig;
     };
     CAST_COPY_PROTOBUF_VEC_TO_VHAL_TYPE(in, area_configs, out, areaConfigs, cast_to_acfg);
diff --git a/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h b/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h
index e53947e..f49d91b 100644
--- a/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h
@@ -82,6 +82,117 @@
             const std::vector<aidl::android::hardware::automotive::vehicle::GetValueRequest>&
                     requests) const = 0;
 
+    // Dump debug information in the server.
+    virtual DumpResult dump(const std::vector<std::string>& options) = 0;
+
+    // Check whether the system is healthy, return {@code StatusCode::OK} for healthy.
+    virtual aidl::android::hardware::automotive::vehicle::StatusCode checkHealth() = 0;
+
+    // Register a callback that would be called when there is a property change event from vehicle.
+    // This function must only be called once during initialization.
+    virtual void registerOnPropertyChangeEvent(
+            std::unique_ptr<const PropertyChangeCallback> callback) = 0;
+
+    // Register a callback that would be called when there is a property set error event from
+    // vehicle. Must only be called once during initialization.
+    virtual void registerOnPropertySetErrorEvent(
+            std::unique_ptr<const PropertySetErrorCallback> callback) = 0;
+
+    // Gets the batching window used by DefaultVehicleHal for property change events.
+    //
+    // In DefaultVehicleHal, all the property change events generated within the batching window
+    // will be delivered through one callback to the VHAL client. This affects the maximum supported
+    // subscription rate. For example, if this returns 10ms, then only one callback for property
+    // change events will be called per 10ms, meaining that the max subscription rate for all
+    // continuous properties would be 100hz.
+    //
+    // A higher batching window means less callbacks to the VHAL client, causing a better
+    // performance. However, it also means a longer average latency for every property change
+    // events.
+    //
+    // 0 means no batching should be enabled in DefaultVehicleHal. In this case, batching can
+    // be optionally implemented in IVehicleHardware layer.
+    virtual std::chrono::nanoseconds getPropertyOnChangeEventBatchingWindow() {
+        // By default batching is disabled.
+        return std::chrono::nanoseconds(0);
+    }
+
+    // A [propId, areaId] is newly subscribed or the subscribe options are changed.
+    //
+    // The subscribe options contain sample rate in Hz or enable/disable variable update rate.
+    //
+    // For continuous properties:
+    //
+    // The sample rate is never 0 and indicates the desired polling rate for this property. The
+    // sample rate is guaranteed to be within supported {@code minSampleRate} and
+    // {@code maxSampleRate} as specified in {@code VehiclePropConfig}.
+    //
+    // If the specified sample rate is not supported, e.g. vehicle bus only supports 5hz and 10hz
+    // polling rate but the sample rate is 8hz, impl must choose the higher polling rate (10hz).
+    //
+    // Whether variable update rate is enabled is specified by {@code enableVariableUpdateRate} in
+    // {@code SubscribeOptions}. If variable update rate is not supported for the
+    // [propId, areaId], impl must ignore this option and always treat it as disabled.
+    //
+    // If variable update rate is disabled/not supported, impl must report all the property events
+    // for this [propId, areaId] through {@code propertyChangeCallback} according to the sample
+    // rate. E.g. a sample rate of 10hz must generate at least 10 property change events per second.
+    //
+    // If variable update rate is enabled AND supported, impl must only report property events
+    // when the [propId, areaId]'s value or status changes (a.k.a same as on-change property).
+    // The sample rate still guides the polling rate, but duplicate property events must be dropped
+    // and not reported via {@code propertyChangeCallback}.
+    //
+    // Async property set error events are not affected by variable update rate and must always
+    // be reported.
+    //
+    // If the impl is always polling at {@code maxSampleRate} for all continuous [propId, areaId]s,
+    // and do not support variable update rate for any [propId, areaId], then this function can be a
+    // no-op.
+    //
+    // For on-change properties:
+    //
+    // The sample rate is always 0 and must be ignored. If the impl is always subscribing to all
+    // on-change properties, then this function can be no-op.
+    //
+    // For all properties:
+    //
+    // It is recommended to only deliver the subscribed property events to DefaultVehicleHal to
+    // improve performance. However, even if unsubscribed property events are delivered, they
+    // will be filtered out by DefaultVehicleHal.
+    //
+    // A subscription from VHAL client might not necessarily trigger this function.
+    // DefaultVehicleHal will aggregate all the subscriptions from all the clients and notify
+    // IVehicleHardware if new subscriptions are required or subscribe options are updated.
+    //
+    // For example:
+    // 1. VHAL initially have no subscriber for speed.
+    // 2. A new subscriber is subscribing speed for 10 times/s, 'subscribe' is called
+    //    with sampleRate as 10. The impl is now polling vehicle speed from bus 10 times/s.
+    // 3. A new subscriber is subscribing speed for 5 times/s, because it is less than 10
+    //    times/sec, 'subscribe' is not called.
+    // 4. The initial subscriber is removed, 'subscribe' is called with sampleRate as
+    //    5, because now it only needs to report event 5times/sec. The impl can now poll vehicle
+    //    speed 5 times/s. If the impl is still polling at 10 times/s, that is okay as long as
+    //    the polling rate is larger than 5times/s. DefaultVehicleHal would ignore the additional
+    //    events.
+    // 5. The second subscriber is removed, 'unsubscribe' is called.
+    //    The impl can optionally disable the polling for vehicle speed.
+    //
+    virtual aidl::android::hardware::automotive::vehicle::StatusCode subscribe(
+            [[maybe_unused]] aidl::android::hardware::automotive::vehicle::SubscribeOptions
+                    options) {
+        return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
+    }
+
+    // A [propId, areaId] is unsubscribed. This applies for both continuous or on-change property.
+    virtual aidl::android::hardware::automotive::vehicle::StatusCode unsubscribe(
+            [[maybe_unused]] int32_t propId, [[maybe_unused]] int32_t areaId) {
+        return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
+    }
+
+    // This function is deprecated, subscribe/unsubscribe should be used instead.
+    //
     // Update the sampling rate for the specified property and the specified areaId (0 for global
     // property) if server supports it. The property must be a continuous property.
     // {@code sampleRate} means that for this specific property, the server must generate at least
@@ -91,7 +202,7 @@
     // This would be called if sample rate is updated for a subscriber, a new subscriber is added
     // or an existing subscriber is removed. For example:
     // 1. We have no subscriber for speed.
-    // 2. A new subscriber is subscribing speed for 10 times/s, updsateSampleRate would be called
+    // 2. A new subscriber is subscribing speed for 10 times/s, updateSampleRate would be called
     //    with sampleRate as 10. The impl is now polling vehicle speed from bus 10 times/s.
     // 3. A new subscriber is subscribing speed for 5 times/s, because it is less than 10
     //    times/sec, updateSampleRate would not be called.
@@ -110,22 +221,6 @@
             [[maybe_unused]] float sampleRate) {
         return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
     }
-
-    // Dump debug information in the server.
-    virtual DumpResult dump(const std::vector<std::string>& options) = 0;
-
-    // Check whether the system is healthy, return {@code StatusCode::OK} for healthy.
-    virtual aidl::android::hardware::automotive::vehicle::StatusCode checkHealth() = 0;
-
-    // Register a callback that would be called when there is a property change event from vehicle.
-    // Must only be called once during initialization.
-    virtual void registerOnPropertyChangeEvent(
-            std::unique_ptr<const PropertyChangeCallback> callback) = 0;
-
-    // Register a callback that would be called when there is a property set error event from
-    // vehicle. Must only be called once during initialization.
-    virtual void registerOnPropertySetErrorEvent(
-            std::unique_ptr<const PropertySetErrorCallback> callback) = 0;
 };
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/VehicleAreaConfig.proto b/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/VehicleAreaConfig.proto
index ddc64d9..8093658 100644
--- a/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/VehicleAreaConfig.proto
+++ b/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/VehicleAreaConfig.proto
@@ -43,4 +43,6 @@
      * assumed all @data_enum values are supported unless specified through another mechanism.
      */
     repeated int64 supported_enum_values = 8;
+    int32 access = 9;
+    bool support_variable_update_rate = 10;
 };
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/ConcurrentQueue.h b/automotive/vehicle/aidl/impl/utils/common/include/ConcurrentQueue.h
index 327c0dc..b636aa3 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/ConcurrentQueue.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/ConcurrentQueue.h
@@ -69,6 +69,19 @@
         mCond.notify_one();
     }
 
+    void push(std::vector<T>&& items) {
+        {
+            std::scoped_lock<std::mutex> lockGuard(mLock);
+            if (!mIsActive) {
+                return;
+            }
+            for (T& item : items) {
+                mQueue.push(std::move(item));
+            }
+        }
+        mCond.notify_one();
+    }
+
     // Deactivates the queue, thus no one can push items to it, also notifies all waiting thread.
     // The items already in the queue could still be flushed even after the queue is deactivated.
     void deactivate() {
@@ -92,6 +105,69 @@
     std::queue<T> mQueue GUARDED_BY(mLock);
 };
 
+template <typename T>
+class BatchingConsumer {
+  private:
+    enum class State {
+        INIT = 0,
+        RUNNING = 1,
+        STOP_REQUESTED = 2,
+        STOPPED = 3,
+    };
+
+  public:
+    BatchingConsumer() : mState(State::INIT) {}
+
+    BatchingConsumer(const BatchingConsumer&) = delete;
+    BatchingConsumer& operator=(const BatchingConsumer&) = delete;
+
+    using OnBatchReceivedFunc = std::function<void(std::vector<T> vec)>;
+
+    void run(ConcurrentQueue<T>* queue, std::chrono::nanoseconds batchInterval,
+             const OnBatchReceivedFunc& func) {
+        mQueue = queue;
+        mBatchInterval = batchInterval;
+
+        mWorkerThread = std::thread(&BatchingConsumer<T>::runInternal, this, func);
+    }
+
+    void requestStop() { mState = State::STOP_REQUESTED; }
+
+    void waitStopped() {
+        if (mWorkerThread.joinable()) {
+            mWorkerThread.join();
+        }
+    }
+
+  private:
+    void runInternal(const OnBatchReceivedFunc& onBatchReceived) {
+        if (mState.exchange(State::RUNNING) == State::INIT) {
+            while (State::RUNNING == mState) {
+                mQueue->waitForItems();
+                if (State::STOP_REQUESTED == mState) break;
+
+                std::this_thread::sleep_for(mBatchInterval);
+                if (State::STOP_REQUESTED == mState) break;
+
+                std::vector<T> items = mQueue->flush();
+
+                if (items.size() > 0) {
+                    onBatchReceived(std::move(items));
+                }
+            }
+        }
+
+        mState = State::STOPPED;
+    }
+
+  private:
+    std::thread mWorkerThread;
+
+    std::atomic<State> mState;
+    std::chrono::nanoseconds mBatchInterval;
+    ConcurrentQueue<T>* mQueue;
+};
+
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/PropertyUtils.h b/automotive/vehicle/aidl/impl/utils/common/include/PropertyUtils.h
index e41ec30..78b61f7 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/PropertyUtils.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/PropertyUtils.h
@@ -98,11 +98,6 @@
 constexpr int HVAC_RIGHT = SEAT_1_RIGHT | SEAT_2_RIGHT;
 constexpr int HVAC_ALL = HVAC_LEFT | HVAC_RIGHT;
 
-const int32_t HVAC_POWER_PROPERTIES[] = {
-        toInt(propertyutils_impl::VehicleProperty::HVAC_FAN_SPEED),
-        toInt(propertyutils_impl::VehicleProperty::HVAC_FAN_DIRECTION),
-};
-
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h b/automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h
index 3d25cd3..2812c3b 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h
@@ -92,7 +92,7 @@
     // used as the key.
     void registerProperty(
             const aidl::android::hardware::automotive::vehicle::VehiclePropConfig& config,
-            TokenFunction tokenFunc = nullptr);
+            TokenFunction tokenFunc = nullptr) EXCLUDES(mLock);
 
     // Stores provided value. Returns error if config wasn't registered. If 'updateStatus' is
     // true, the 'status' in 'propValue' would be stored. Otherwise, if this is a new value,
@@ -100,46 +100,51 @@
     // override an existing value, the status for the existing value would be used for the
     // overridden value.
     // 'EventMode' controls whether the 'OnValueChangeCallback' will be called for this operation.
+    // If 'useCurrentTimestamp' is true, the property value will be set to the current timestamp.
     VhalResult<void> writeValue(VehiclePropValuePool::RecyclableType propValue,
                                 bool updateStatus = false,
-                                EventMode mode = EventMode::ON_VALUE_CHANGE);
+                                EventMode mode = EventMode::ON_VALUE_CHANGE,
+                                bool useCurrentTimestamp = false) EXCLUDES(mLock);
 
     // Remove a given property value from the property store. The 'propValue' would be used to
     // generate the key for the value to remove.
     void removeValue(
-            const aidl::android::hardware::automotive::vehicle::VehiclePropValue& propValue);
+            const aidl::android::hardware::automotive::vehicle::VehiclePropValue& propValue)
+            EXCLUDES(mLock);
 
     // Remove all the values for the property.
-    void removeValuesForProperty(int32_t propId);
+    void removeValuesForProperty(int32_t propId) EXCLUDES(mLock);
 
     // Read all the stored values.
-    std::vector<VehiclePropValuePool::RecyclableType> readAllValues() const;
+    std::vector<VehiclePropValuePool::RecyclableType> readAllValues() const EXCLUDES(mLock);
 
     // Read all the values for the property.
-    ValuesResultType readValuesForProperty(int32_t propId) const;
+    ValuesResultType readValuesForProperty(int32_t propId) const EXCLUDES(mLock);
 
     // Read the value for the requested property. Returns {@code StatusCode::NOT_AVAILABLE} if the
     // value has not been set yet. Returns {@code StatusCode::INVALID_ARG} if the property is
     // not configured.
     ValueResultType readValue(
-            const aidl::android::hardware::automotive::vehicle::VehiclePropValue& request) const;
+            const aidl::android::hardware::automotive::vehicle::VehiclePropValue& request) const
+            EXCLUDES(mLock);
 
     // Read the value for the requested property. Returns {@code StatusCode::NOT_AVAILABLE} if the
     // value has not been set yet. Returns {@code StatusCode::INVALID_ARG} if the property is
     // not configured.
-    ValueResultType readValue(int32_t prop, int32_t area = 0, int64_t token = 0) const;
+    ValueResultType readValue(int32_t prop, int32_t area = 0, int64_t token = 0) const
+            EXCLUDES(mLock);
 
     // Get all property configs.
     std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropConfig> getAllConfigs()
-            const;
+            const EXCLUDES(mLock);
 
     // Get the property config for the requested property.
     android::base::Result<const aidl::android::hardware::automotive::vehicle::VehiclePropConfig*,
                           VhalError>
-    getConfig(int32_t propId) const;
+    getConfig(int32_t propId) const EXCLUDES(mLock);
 
     // Set a callback that would be called when a property value has been updated.
-    void setOnValueChangeCallback(const OnValueChangeCallback& callback);
+    void setOnValueChangeCallback(const OnValueChangeCallback& callback) EXCLUDES(mLock);
 
     inline std::shared_ptr<VehiclePropValuePool> getValuePool() { return mValuePool; }
 
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h b/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
index c94bad6..546421e 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
@@ -329,6 +329,11 @@
     }
 };
 
+inline std::string propIdToString(int32_t propId) {
+    return toString(
+            static_cast<aidl::android::hardware::automotive::vehicle::VehicleProperty>(propId));
+}
+
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp b/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp
index 646dc0e..b879850 100644
--- a/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp
+++ b/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "VehiclePropertyStore"
 #include <utils/Log.h>
+#include <utils/SystemClock.h>
 
 #include "VehiclePropertyStore.h"
 
@@ -107,52 +108,70 @@
 
 VhalResult<void> VehiclePropertyStore::writeValue(VehiclePropValuePool::RecyclableType propValue,
                                                   bool updateStatus,
-                                                  VehiclePropertyStore::EventMode eventMode) {
-    std::scoped_lock<std::mutex> g(mLock);
-
-    int32_t propId = propValue->prop;
-
-    VehiclePropertyStore::Record* record = getRecordLocked(propId);
-    if (record == nullptr) {
-        return StatusError(StatusCode::INVALID_ARG) << "property: " << propId << " not registered";
-    }
-
-    if (!isGlobalProp(propId) && getAreaConfig(*propValue, record->propConfig) == nullptr) {
-        return StatusError(StatusCode::INVALID_ARG)
-               << "no config for property: " << propId << " area: " << propValue->areaId;
-    }
-
-    VehiclePropertyStore::RecordId recId = getRecordIdLocked(*propValue, *record);
+                                                  VehiclePropertyStore::EventMode eventMode,
+                                                  bool useCurrentTimestamp) {
     bool valueUpdated = true;
-    if (auto it = record->values.find(recId); it != record->values.end()) {
-        const VehiclePropValue* valueToUpdate = it->second.get();
-        int64_t oldTimestamp = valueToUpdate->timestamp;
-        VehiclePropertyStatus oldStatus = valueToUpdate->status;
-        // propValue is outdated and drops it.
-        if (oldTimestamp > propValue->timestamp) {
+    VehiclePropValue updatedValue;
+    OnValueChangeCallback onValueChangeCallback = nullptr;
+    {
+        std::scoped_lock<std::mutex> g(mLock);
+
+        // Must set timestamp inside the lock to make sure no other writeValue will update the
+        // the timestamp to a newer one while we are writing this value.
+        if (useCurrentTimestamp) {
+            propValue->timestamp = elapsedRealtimeNano();
+        }
+
+        int32_t propId = propValue->prop;
+
+        VehiclePropertyStore::Record* record = getRecordLocked(propId);
+        if (record == nullptr) {
             return StatusError(StatusCode::INVALID_ARG)
-                   << "outdated timestamp: " << propValue->timestamp;
-        }
-        if (!updateStatus) {
-            propValue->status = oldStatus;
+                   << "property: " << propId << " not registered";
         }
 
-        valueUpdated = (valueToUpdate->value != propValue->value ||
-                        valueToUpdate->status != propValue->status ||
-                        valueToUpdate->prop != propValue->prop ||
-                        valueToUpdate->areaId != propValue->areaId);
-    } else if (!updateStatus) {
-        propValue->status = VehiclePropertyStatus::AVAILABLE;
+        if (!isGlobalProp(propId) && getAreaConfig(*propValue, record->propConfig) == nullptr) {
+            return StatusError(StatusCode::INVALID_ARG)
+                   << "no config for property: " << propId << " area ID: " << propValue->areaId;
+        }
+
+        VehiclePropertyStore::RecordId recId = getRecordIdLocked(*propValue, *record);
+        if (auto it = record->values.find(recId); it != record->values.end()) {
+            const VehiclePropValue* valueToUpdate = it->second.get();
+            int64_t oldTimestampNanos = valueToUpdate->timestamp;
+            VehiclePropertyStatus oldStatus = valueToUpdate->status;
+            // propValue is outdated and drops it.
+            if (oldTimestampNanos > propValue->timestamp) {
+                return StatusError(StatusCode::INVALID_ARG)
+                       << "outdated timestampNanos: " << propValue->timestamp;
+            }
+            if (!updateStatus) {
+                propValue->status = oldStatus;
+            }
+
+            valueUpdated = (valueToUpdate->value != propValue->value ||
+                            valueToUpdate->status != propValue->status ||
+                            valueToUpdate->prop != propValue->prop ||
+                            valueToUpdate->areaId != propValue->areaId);
+        } else if (!updateStatus) {
+            propValue->status = VehiclePropertyStatus::AVAILABLE;
+        }
+
+        record->values[recId] = std::move(propValue);
+
+        if (eventMode == EventMode::NEVER) {
+            return {};
+        }
+        updatedValue = *(record->values[recId]);
+        if (mOnValueChangeCallback == nullptr) {
+            return {};
+        }
+        onValueChangeCallback = mOnValueChangeCallback;
     }
 
-    record->values[recId] = std::move(propValue);
-
-    if (eventMode == EventMode::NEVER) {
-        return {};
-    }
-
-    if ((eventMode == EventMode::ALWAYS || valueUpdated) && mOnValueChangeCallback != nullptr) {
-        mOnValueChangeCallback(*(record->values[recId]));
+    // Invoke the callback outside the lock to prevent dead-lock.
+    if (eventMode == EventMode::ALWAYS || valueUpdated) {
+        onValueChangeCallback(updatedValue);
     }
     return {};
 }
diff --git a/automotive/vehicle/aidl/impl/utils/common/test/VehiclePropertyStoreTest.cpp b/automotive/vehicle/aidl/impl/utils/common/test/VehiclePropertyStoreTest.cpp
index fea5034..625652e 100644
--- a/automotive/vehicle/aidl/impl/utils/common/test/VehiclePropertyStoreTest.cpp
+++ b/automotive/vehicle/aidl/impl/utils/common/test/VehiclePropertyStoreTest.cpp
@@ -509,6 +509,24 @@
     ASSERT_EQ(updatedValue.prop, INVALID_PROP_ID);
 }
 
+TEST_F(VehiclePropertyStoreTest, testPropertyChangeCallbackUseVehiclePropertyStore_noDeadLock) {
+    VehiclePropValue fuelCapacity = {
+            .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY),
+            .value = {.floatValues = {1.0}},
+    };
+
+    std::vector<VehiclePropConfig> configs;
+
+    mStore->setOnValueChangeCallback(
+            [this, &configs]([[maybe_unused]] const VehiclePropValue& value) {
+                configs = mStore->getAllConfigs();
+            });
+
+    ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity), /*updateStatus=*/true,
+                                        VehiclePropertyStore::EventMode::ALWAYS));
+    ASSERT_EQ(configs.size(), static_cast<size_t>(2));
+}
+
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
index 419e16b..f7a71b4 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
@@ -144,6 +144,15 @@
     std::shared_ptr<PendingRequestPool> mPendingRequestPool;
     // SubscriptionManager is thread-safe.
     std::shared_ptr<SubscriptionManager> mSubscriptionManager;
+    // ConcurrentQueue is thread-safe.
+    std::shared_ptr<ConcurrentQueue<aidl::android::hardware::automotive::vehicle::VehiclePropValue>>
+            mBatchedEventQueue;
+    // BatchingConsumer is thread-safe.
+    std::shared_ptr<
+            BatchingConsumer<aidl::android::hardware::automotive::vehicle::VehiclePropValue>>
+            mPropertyChangeEventsBatchingConsumer;
+    // Only set once during initialization.
+    std::chrono::nanoseconds mEventBatchingWindow;
 
     std::mutex mLock;
     std::unordered_map<const AIBinder*, std::unique_ptr<OnBinderDiedContext>> mOnBinderDiedContexts
@@ -182,6 +191,10 @@
             const std::vector<aidl::android::hardware::automotive::vehicle::SubscribeOptions>&
                     options);
 
+    VhalResult<void> checkPermissionHelper(
+            const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value,
+            aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess accessToTest) const;
+
     VhalResult<void> checkReadPermission(
             const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const;
 
@@ -209,6 +222,19 @@
 
     size_t countSubscribeClients();
 
+    // Handles the property change events in batch.
+    void handleBatchedPropertyEvents(
+            std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue>&&
+                    batchedEvents);
+
+    // Puts the property change events into a queue so that they can handled in batch.
+    static void batchPropertyChangeEvent(
+            const std::weak_ptr<ConcurrentQueue<
+                    aidl::android::hardware::automotive::vehicle::VehiclePropValue>>&
+                    batchedEventQueue,
+            std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue>&&
+                    updatedValues);
+
     // Gets or creates a {@code T} object for the client to or from {@code clients}.
     template <class T>
     static std::shared_ptr<T> getOrCreateClient(
@@ -217,7 +243,7 @@
 
     static void onPropertyChangeEvent(
             const std::weak_ptr<SubscriptionManager>& subscriptionManager,
-            const std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue>&
+            std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue>&&
                     updatedValues);
 
     static void onPropertySetErrorEvent(
diff --git a/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h b/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
index 512d906..5053c96 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
@@ -36,20 +36,29 @@
 namespace automotive {
 namespace vehicle {
 
+// A structure to represent subscription config for one subscription client.
+struct SubConfig {
+    float sampleRateHz;
+    bool enableVur;
+};
+
 // A class to represent all the subscription configs for a continuous [propId, areaId].
 class ContSubConfigs final {
   public:
     using ClientIdType = const AIBinder*;
 
-    void addClient(const ClientIdType& clientId, float sampleRateHz);
+    void addClient(const ClientIdType& clientId, float sampleRateHz, bool enableVur);
     void removeClient(const ClientIdType& clientId);
     float getMaxSampleRateHz() const;
+    bool isVurEnabled() const;
+    bool isVurEnabledForClient(const ClientIdType& clientId);
 
   private:
     float mMaxSampleRateHz = 0.;
-    std::unordered_map<ClientIdType, float> mSampleRateHzByClient;
+    bool mEnableVur;
+    std::unordered_map<ClientIdType, SubConfig> mConfigByClient;
 
-    void refreshMaxSampleRateHz();
+    void refreshCombinedConfig();
 };
 
 // A thread-safe subscription manager that manages all VHAL subscriptions.
@@ -58,6 +67,7 @@
     using ClientIdType = const AIBinder*;
     using CallbackType =
             std::shared_ptr<aidl::android::hardware::automotive::vehicle::IVehicleCallback>;
+    using VehiclePropValue = aidl::android::hardware::automotive::vehicle::VehiclePropValue;
 
     explicit SubscriptionManager(IVehicleHardware* vehicleHardware);
     ~SubscriptionManager();
@@ -92,12 +102,8 @@
     // For a list of updated properties, returns a map that maps clients subscribing to
     // the updated properties to a list of updated values. This would only return on-change property
     // clients that should be informed for the given updated values.
-    std::unordered_map<
-            CallbackType,
-            std::vector<const aidl::android::hardware::automotive::vehicle::VehiclePropValue*>>
-    getSubscribedClients(
-            const std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue>&
-                    updatedValues);
+    std::unordered_map<CallbackType, std::vector<VehiclePropValue>> getSubscribedClients(
+            std::vector<VehiclePropValue>&& updatedValues);
 
     // For a list of set property error events, returns a map that maps clients subscribing to the
     // properties to a list of errors for each client.
@@ -117,28 +123,60 @@
 
     IVehicleHardware* mVehicleHardware;
 
+    struct VehiclePropValueHashPropIdAreaId {
+        inline size_t operator()(const VehiclePropValue& vehiclePropValue) const {
+            size_t res = 0;
+            hashCombine(res, vehiclePropValue.prop);
+            hashCombine(res, vehiclePropValue.areaId);
+            return res;
+        }
+    };
+
+    struct VehiclePropValueEqualPropIdAreaId {
+        inline bool operator()(const VehiclePropValue& left, const VehiclePropValue& right) const {
+            return left.prop == right.prop && left.areaId == right.areaId;
+        }
+    };
+
     mutable std::mutex mLock;
     std::unordered_map<PropIdAreaId, std::unordered_map<ClientIdType, CallbackType>,
                        PropIdAreaIdHash>
-            mClientsByPropIdArea GUARDED_BY(mLock);
+            mClientsByPropIdAreaId GUARDED_BY(mLock);
     std::unordered_map<ClientIdType, std::unordered_set<PropIdAreaId, PropIdAreaIdHash>>
             mSubscribedPropsByClient GUARDED_BY(mLock);
     std::unordered_map<PropIdAreaId, ContSubConfigs, PropIdAreaIdHash> mContSubConfigsByPropIdArea
             GUARDED_BY(mLock);
+    std::unordered_map<CallbackType,
+                       std::unordered_set<VehiclePropValue, VehiclePropValueHashPropIdAreaId,
+                                          VehiclePropValueEqualPropIdAreaId>>
+            mContSubValuesByCallback GUARDED_BY(mLock);
 
     VhalResult<void> addContinuousSubscriberLocked(const ClientIdType& clientId,
                                                    const PropIdAreaId& propIdAreaId,
-                                                   float sampleRateHz) REQUIRES(mLock);
+                                                   float sampleRateHz, bool enableVur)
+            REQUIRES(mLock);
+    VhalResult<void> addOnChangeSubscriberLocked(const PropIdAreaId& propIdAreaId) REQUIRES(mLock);
+    // Removes the subscription client for the continuous [propId, areaId].
     VhalResult<void> removeContinuousSubscriberLocked(const ClientIdType& clientId,
                                                       const PropIdAreaId& propIdAreaId)
             REQUIRES(mLock);
+    // Removes one subscription client for the on-change [propId, areaId].
+    VhalResult<void> removeOnChangeSubscriberLocked(const PropIdAreaId& propIdAreaId)
+            REQUIRES(mLock);
 
-    VhalResult<void> updateContSubConfigs(const PropIdAreaId& PropIdAreaId,
-                                          const ContSubConfigs& newConfig) REQUIRES(mLock);
+    VhalResult<void> updateContSubConfigsLocked(const PropIdAreaId& PropIdAreaId,
+                                                const ContSubConfigs& newConfig) REQUIRES(mLock);
+
+    VhalResult<void> unsubscribePropIdAreaIdLocked(SubscriptionManager::ClientIdType clientId,
+                                                   const PropIdAreaId& propIdAreaId)
+            REQUIRES(mLock);
 
     // Checks whether the manager is empty. For testing purpose.
     bool isEmpty();
 
+    bool isValueUpdatedLocked(const CallbackType& callback, const VehiclePropValue& value)
+            REQUIRES(mLock);
+
     // Get the interval in nanoseconds accroding to sample rate.
     static android::base::Result<int64_t> getIntervalNanos(float sampleRateHz);
 };
diff --git a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
index f3eba2e..847f3b8 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
@@ -32,6 +32,7 @@
 #include <utils/Trace.h>
 
 #include <inttypes.h>
+#include <chrono>
 #include <set>
 #include <unordered_set>
 
@@ -101,12 +102,32 @@
 
     IVehicleHardware* vehicleHardwarePtr = mVehicleHardware.get();
     mSubscriptionManager = std::make_shared<SubscriptionManager>(vehicleHardwarePtr);
+    mEventBatchingWindow = mVehicleHardware->getPropertyOnChangeEventBatchingWindow();
+    if (mEventBatchingWindow != std::chrono::nanoseconds(0)) {
+        mBatchedEventQueue = std::make_shared<ConcurrentQueue<VehiclePropValue>>();
+        mPropertyChangeEventsBatchingConsumer =
+                std::make_shared<BatchingConsumer<VehiclePropValue>>();
+        mPropertyChangeEventsBatchingConsumer->run(
+                mBatchedEventQueue.get(), mEventBatchingWindow,
+                [this](std::vector<VehiclePropValue> batchedEvents) {
+                    handleBatchedPropertyEvents(std::move(batchedEvents));
+                });
+    }
 
+    std::weak_ptr<ConcurrentQueue<VehiclePropValue>> batchedEventQueueCopy = mBatchedEventQueue;
+    std::chrono::nanoseconds eventBatchingWindow = mEventBatchingWindow;
     std::weak_ptr<SubscriptionManager> subscriptionManagerCopy = mSubscriptionManager;
     mVehicleHardware->registerOnPropertyChangeEvent(
             std::make_unique<IVehicleHardware::PropertyChangeCallback>(
-                    [subscriptionManagerCopy](std::vector<VehiclePropValue> updatedValues) {
-                        onPropertyChangeEvent(subscriptionManagerCopy, updatedValues);
+                    [subscriptionManagerCopy, batchedEventQueueCopy,
+                     eventBatchingWindow](std::vector<VehiclePropValue> updatedValues) {
+                        if (eventBatchingWindow != std::chrono::nanoseconds(0)) {
+                            batchPropertyChangeEvent(batchedEventQueueCopy,
+                                                     std::move(updatedValues));
+                        } else {
+                            onPropertyChangeEvent(subscriptionManagerCopy,
+                                                  std::move(updatedValues));
+                        }
                     }));
     mVehicleHardware->registerOnPropertySetErrorEvent(
             std::make_unique<IVehicleHardware::PropertySetErrorCallback>(
@@ -139,26 +160,47 @@
     // mRecurrentAction uses pointer to mVehicleHardware, so it has to be unregistered before
     // mVehicleHardware.
     mRecurrentTimer.unregisterTimerCallback(mRecurrentAction);
+
+    if (mBatchedEventQueue) {
+        // mPropertyChangeEventsBatchingConsumer uses mSubscriptionManager and mBatchedEventQueue.
+        mBatchedEventQueue->deactivate();
+        mPropertyChangeEventsBatchingConsumer->requestStop();
+        mPropertyChangeEventsBatchingConsumer->waitStopped();
+        mPropertyChangeEventsBatchingConsumer.reset();
+        mBatchedEventQueue.reset();
+    }
+
     // mSubscriptionManager uses pointer to mVehicleHardware, so it has to be destroyed before
     // mVehicleHardware.
     mSubscriptionManager.reset();
     mVehicleHardware.reset();
 }
 
+void DefaultVehicleHal::batchPropertyChangeEvent(
+        const std::weak_ptr<ConcurrentQueue<VehiclePropValue>>& batchedEventQueue,
+        std::vector<VehiclePropValue>&& updatedValues) {
+    auto batchedEventQueueStrong = batchedEventQueue.lock();
+    if (batchedEventQueueStrong == nullptr) {
+        ALOGW("the batched property events queue is destroyed, DefaultVehicleHal is ending");
+        return;
+    }
+    batchedEventQueueStrong->push(std::move(updatedValues));
+}
+
+void DefaultVehicleHal::handleBatchedPropertyEvents(std::vector<VehiclePropValue>&& batchedEvents) {
+    onPropertyChangeEvent(mSubscriptionManager, std::move(batchedEvents));
+}
+
 void DefaultVehicleHal::onPropertyChangeEvent(
         const std::weak_ptr<SubscriptionManager>& subscriptionManager,
-        const std::vector<VehiclePropValue>& updatedValues) {
+        std::vector<VehiclePropValue>&& updatedValues) {
     auto manager = subscriptionManager.lock();
     if (manager == nullptr) {
         ALOGW("the SubscriptionManager is destroyed, DefaultVehicleHal is ending");
         return;
     }
-    auto updatedValuesByClients = manager->getSubscribedClients(updatedValues);
-    for (const auto& [callback, valuePtrs] : updatedValuesByClients) {
-        std::vector<VehiclePropValue> values;
-        for (const VehiclePropValue* valuePtr : valuePtrs) {
-            values.push_back(*valuePtr);
-        }
+    auto updatedValuesByClients = manager->getSubscribedClients(std::move(updatedValues));
+    for (auto& [callback, values] : updatedValuesByClients) {
         SubscriptionClient::sendUpdatedValues(callback, std::move(values));
     }
 }
@@ -563,6 +605,23 @@
     return vectorToStableLargeParcelable(std::move(configs), output);
 }
 
+bool hasRequiredAccess(VehiclePropertyAccess access, VehiclePropertyAccess requiredAccess) {
+    return access == requiredAccess || access == VehiclePropertyAccess::READ_WRITE;
+}
+
+bool areaConfigsHaveRequiredAccess(const std::vector<VehicleAreaConfig>& areaConfigs,
+                                   VehiclePropertyAccess requiredAccess) {
+    if (areaConfigs.empty()) {
+        return false;
+    }
+    for (VehicleAreaConfig areaConfig : areaConfigs) {
+        if (!hasRequiredAccess(areaConfig.access, requiredAccess)) {
+            return false;
+        }
+    }
+    return true;
+}
+
 VhalResult<void> DefaultVehicleHal::checkSubscribeOptions(
         const std::vector<SubscribeOptions>& options) {
     for (const auto& option : options) {
@@ -572,6 +631,26 @@
                    << StringPrintf("no config for property, ID: %" PRId32, propId);
         }
         const VehiclePropConfig& config = mConfigsByPropId[propId];
+        std::vector<VehicleAreaConfig> areaConfigs;
+        if (option.areaIds.empty()) {
+            areaConfigs = config.areaConfigs;
+        } else {
+            std::unordered_map<int, VehicleAreaConfig> areaConfigByAreaId;
+            for (const VehicleAreaConfig& areaConfig : config.areaConfigs) {
+                areaConfigByAreaId.emplace(areaConfig.areaId, areaConfig);
+            }
+            for (int areaId : option.areaIds) {
+                auto it = areaConfigByAreaId.find(areaId);
+                if (it != areaConfigByAreaId.end()) {
+                    areaConfigs.push_back(it->second);
+                } else if (areaId != 0 || !areaConfigByAreaId.empty()) {
+                    return StatusError(StatusCode::INVALID_ARG)
+                           << StringPrintf("invalid area ID: %" PRId32 " for prop ID: %" PRId32
+                                           ", not listed in config",
+                                           areaId, propId);
+                }
+            }
+        }
 
         if (config.changeMode != VehiclePropertyChangeMode::ON_CHANGE &&
             config.changeMode != VehiclePropertyChangeMode::CONTINUOUS) {
@@ -579,8 +658,9 @@
                    << "only support subscribing to ON_CHANGE or CONTINUOUS property";
         }
 
-        if (config.access != VehiclePropertyAccess::READ &&
-            config.access != VehiclePropertyAccess::READ_WRITE) {
+        // Either VehiclePropConfig.access or VehicleAreaConfig.access will be specified
+        if (!hasRequiredAccess(config.access, VehiclePropertyAccess::READ) &&
+            !areaConfigsHaveRequiredAccess(areaConfigs, VehiclePropertyAccess::READ)) {
             return StatusError(StatusCode::ACCESS_DENIED)
                    << StringPrintf("Property %" PRId32 " has no read access", propId);
         }
@@ -602,20 +682,6 @@
                        << "invalid sample rate: " << sampleRateHz << " HZ";
             }
         }
-
-        if (isGlobalProp(propId)) {
-            continue;
-        }
-
-        // Non-global property.
-        for (int32_t areaId : option.areaIds) {
-            if (auto areaConfig = getAreaConfig(propId, areaId, config); areaConfig == nullptr) {
-                return StatusError(StatusCode::INVALID_ARG)
-                       << StringPrintf("invalid area ID: %" PRId32 " for prop ID: %" PRId32
-                                       ", not listed in config",
-                                       areaId, propId);
-            }
-        }
     }
     return {};
 }
@@ -653,7 +719,39 @@
         if (config.changeMode == VehiclePropertyChangeMode::CONTINUOUS) {
             optionCopy.sampleRate = getDefaultSampleRateHz(
                     optionCopy.sampleRate, config.minSampleRate, config.maxSampleRate);
-            continuousSubscriptions.push_back(std::move(optionCopy));
+            if (!optionCopy.enableVariableUpdateRate) {
+                continuousSubscriptions.push_back(std::move(optionCopy));
+            } else {
+                // If clients enables to VUR, we need to check whether VUR is supported for the
+                // specific [propId, areaId] and overwrite the option to disable if not supported.
+                std::vector<int32_t> areasVurEnabled;
+                std::vector<int32_t> areasVurDisabled;
+                for (int32_t areaId : optionCopy.areaIds) {
+                    const VehicleAreaConfig* areaConfig = getAreaConfig(propId, areaId, config);
+                    if (areaConfig == nullptr) {
+                        areasVurDisabled.push_back(areaId);
+                        continue;
+                    }
+                    if (!areaConfig->supportVariableUpdateRate) {
+                        areasVurDisabled.push_back(areaId);
+                        continue;
+                    }
+                    areasVurEnabled.push_back(areaId);
+                }
+                if (!areasVurEnabled.empty()) {
+                    SubscribeOptions optionVurEnabled = optionCopy;
+                    optionVurEnabled.areaIds = areasVurEnabled;
+                    optionVurEnabled.enableVariableUpdateRate = true;
+                    continuousSubscriptions.push_back(std::move(optionVurEnabled));
+                }
+
+                if (!areasVurDisabled.empty()) {
+                    // We use optionCopy for areas with VUR disabled.
+                    optionCopy.areaIds = areasVurDisabled;
+                    optionCopy.enableVariableUpdateRate = false;
+                    continuousSubscriptions.push_back(std::move(optionCopy));
+                }
+            }
         } else {
             onChangeSubscriptions.push_back(std::move(optionCopy));
         }
@@ -702,36 +800,42 @@
     return mVehicleHardware.get();
 }
 
-VhalResult<void> DefaultVehicleHal::checkWritePermission(const VehiclePropValue& value) const {
+VhalResult<void> DefaultVehicleHal::checkPermissionHelper(
+        const VehiclePropValue& value, VehiclePropertyAccess accessToTest) const {
+    static const std::unordered_set<VehiclePropertyAccess> validAccesses = {
+            VehiclePropertyAccess::WRITE, VehiclePropertyAccess::READ,
+            VehiclePropertyAccess::READ_WRITE};
+    if (validAccesses.find(accessToTest) == validAccesses.end()) {
+        return StatusError(StatusCode::INVALID_ARG)
+               << "checkPermissionHelper parameter is an invalid access type";
+    }
+
     int32_t propId = value.prop;
     auto result = getConfig(propId);
     if (!result.ok()) {
         return StatusError(StatusCode::INVALID_ARG) << getErrorMsg(result);
     }
     const VehiclePropConfig* config = result.value();
+    const VehicleAreaConfig* areaConfig = getAreaConfig(value, *config);
 
-    if (config->access != VehiclePropertyAccess::WRITE &&
-        config->access != VehiclePropertyAccess::READ_WRITE) {
+    if (areaConfig == nullptr && !isGlobalProp(propId)) {
+        return StatusError(StatusCode::INVALID_ARG) << "no config for area ID: " << value.areaId;
+    }
+    if (!hasRequiredAccess(config->access, accessToTest) &&
+        (areaConfig == nullptr || !hasRequiredAccess(areaConfig->access, accessToTest))) {
         return StatusError(StatusCode::ACCESS_DENIED)
-               << StringPrintf("Property %" PRId32 " has no write access", propId);
+               << StringPrintf("Property %" PRId32 " does not have the following access: %" PRId32,
+                               propId, accessToTest);
     }
     return {};
 }
 
-VhalResult<void> DefaultVehicleHal::checkReadPermission(const VehiclePropValue& value) const {
-    int32_t propId = value.prop;
-    auto result = getConfig(propId);
-    if (!result.ok()) {
-        return StatusError(StatusCode::INVALID_ARG) << getErrorMsg(result);
-    }
-    const VehiclePropConfig* config = result.value();
+VhalResult<void> DefaultVehicleHal::checkWritePermission(const VehiclePropValue& value) const {
+    return checkPermissionHelper(value, VehiclePropertyAccess::WRITE);
+}
 
-    if (config->access != VehiclePropertyAccess::READ &&
-        config->access != VehiclePropertyAccess::READ_WRITE) {
-        return StatusError(StatusCode::ACCESS_DENIED)
-               << StringPrintf("Property %" PRId32 " has no read access", propId);
-    }
-    return {};
+VhalResult<void> DefaultVehicleHal::checkReadPermission(const VehiclePropValue& value) const {
+    return checkPermissionHelper(value, VehiclePropertyAccess::READ);
 }
 
 void DefaultVehicleHal::checkHealth(IVehicleHardware* vehicleHardware,
@@ -742,12 +846,12 @@
         return;
     }
     std::vector<VehiclePropValue> values = {{
-            .prop = toInt(VehicleProperty::VHAL_HEARTBEAT),
             .areaId = 0,
+            .prop = toInt(VehicleProperty::VHAL_HEARTBEAT),
             .status = VehiclePropertyStatus::AVAILABLE,
             .value.int64Values = {uptimeMillis()},
     }};
-    onPropertyChangeEvent(subscriptionManager, values);
+    onPropertyChangeEvent(subscriptionManager, std::move(values));
     return;
 }
 
diff --git a/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp b/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
index 17683ae..29d81a7 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
@@ -16,6 +16,7 @@
 
 #include "SubscriptionManager.h"
 
+#include <VehicleUtils.h>
 #include <android-base/stringprintf.h>
 #include <utils/Log.h>
 #include <utils/SystemClock.h>
@@ -29,10 +30,6 @@
 
 namespace {
 
-constexpr float ONE_SECOND_IN_NANO = 1'000'000'000.;
-
-}  // namespace
-
 using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback;
 using ::aidl::android::hardware::automotive::vehicle::StatusCode;
 using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
@@ -43,13 +40,28 @@
 using ::android::base::StringPrintf;
 using ::ndk::ScopedAStatus;
 
+constexpr float ONE_SECOND_IN_NANOS = 1'000'000'000.;
+
+SubscribeOptions newSubscribeOptions(int32_t propId, int32_t areaId, float sampleRateHz,
+                                     bool enableVur) {
+    SubscribeOptions subscribedOptions;
+    subscribedOptions.propId = propId;
+    subscribedOptions.areaIds = {areaId};
+    subscribedOptions.sampleRate = sampleRateHz;
+    subscribedOptions.enableVariableUpdateRate = enableVur;
+
+    return subscribedOptions;
+}
+
+}  // namespace
+
 SubscriptionManager::SubscriptionManager(IVehicleHardware* vehicleHardware)
     : mVehicleHardware(vehicleHardware) {}
 
 SubscriptionManager::~SubscriptionManager() {
     std::scoped_lock<std::mutex> lockGuard(mLock);
 
-    mClientsByPropIdArea.clear();
+    mClientsByPropIdAreaId.clear();
     mSubscribedPropsByClient.clear();
 }
 
@@ -62,45 +74,83 @@
     if (sampleRateHz <= 0) {
         return Error() << "invalid sample rate, must be a positive number";
     }
-    if (sampleRateHz <= (ONE_SECOND_IN_NANO / static_cast<float>(INT64_MAX))) {
+    if (sampleRateHz <= (ONE_SECOND_IN_NANOS / static_cast<float>(INT64_MAX))) {
         return Error() << "invalid sample rate: " << sampleRateHz << ", too small";
     }
-    intervalNanos = static_cast<int64_t>(ONE_SECOND_IN_NANO / sampleRateHz);
+    intervalNanos = static_cast<int64_t>(ONE_SECOND_IN_NANOS / sampleRateHz);
     return intervalNanos;
 }
 
-void ContSubConfigs::refreshMaxSampleRateHz() {
+void ContSubConfigs::refreshCombinedConfig() {
     float maxSampleRateHz = 0.;
+    bool enableVur = true;
     // This is not called frequently so a brute-focre is okay. More efficient way exists but this
     // is simpler.
-    for (const auto& [_, sampleRateHz] : mSampleRateHzByClient) {
-        if (sampleRateHz > maxSampleRateHz) {
-            maxSampleRateHz = sampleRateHz;
+    for (const auto& [_, subConfig] : mConfigByClient) {
+        if (subConfig.sampleRateHz > maxSampleRateHz) {
+            maxSampleRateHz = subConfig.sampleRateHz;
+        }
+        if (!subConfig.enableVur) {
+            // If one client does not enable variable update rate, we cannot enable variable update
+            // rate in IVehicleHardware.
+            enableVur = false;
         }
     }
     mMaxSampleRateHz = maxSampleRateHz;
+    mEnableVur = enableVur;
 }
 
-void ContSubConfigs::addClient(const ClientIdType& clientId, float sampleRateHz) {
-    mSampleRateHzByClient[clientId] = sampleRateHz;
-    refreshMaxSampleRateHz();
+void ContSubConfigs::addClient(const ClientIdType& clientId, float sampleRateHz, bool enableVur) {
+    mConfigByClient[clientId] = {
+            .sampleRateHz = sampleRateHz,
+            .enableVur = enableVur,
+    };
+    refreshCombinedConfig();
 }
 
 void ContSubConfigs::removeClient(const ClientIdType& clientId) {
-    mSampleRateHzByClient.erase(clientId);
-    refreshMaxSampleRateHz();
+    mConfigByClient.erase(clientId);
+    refreshCombinedConfig();
 }
 
 float ContSubConfigs::getMaxSampleRateHz() const {
     return mMaxSampleRateHz;
 }
 
+bool ContSubConfigs::isVurEnabled() const {
+    return mEnableVur;
+}
+
+bool ContSubConfigs::isVurEnabledForClient(const ClientIdType& clientId) {
+    return mConfigByClient[clientId].enableVur;
+}
+
+VhalResult<void> SubscriptionManager::addOnChangeSubscriberLocked(
+        const PropIdAreaId& propIdAreaId) {
+    if (mClientsByPropIdAreaId.find(propIdAreaId) != mClientsByPropIdAreaId.end()) {
+        // This propId, areaId is already subscribed, ignore the request.
+        return {};
+    }
+
+    int32_t propId = propIdAreaId.propId;
+    int32_t areaId = propIdAreaId.areaId;
+    if (auto status = mVehicleHardware->subscribe(
+                newSubscribeOptions(propId, areaId, /*updateRateHz=*/0, /*enableVur*/ false));
+        status != StatusCode::OK) {
+        return StatusError(status)
+               << StringPrintf("failed subscribe for prop: %s, areaId: %" PRId32,
+                               propIdToString(propId).c_str(), areaId);
+    }
+    return {};
+}
+
 VhalResult<void> SubscriptionManager::addContinuousSubscriberLocked(
-        const ClientIdType& clientId, const PropIdAreaId& propIdAreaId, float sampleRateHz) {
+        const ClientIdType& clientId, const PropIdAreaId& propIdAreaId, float sampleRateHz,
+        bool enableVur) {
     // Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases.
     ContSubConfigs newConfig = mContSubConfigsByPropIdArea[propIdAreaId];
-    newConfig.addClient(clientId, sampleRateHz);
-    return updateContSubConfigs(propIdAreaId, newConfig);
+    newConfig.addClient(clientId, sampleRateHz, enableVur);
+    return updateContSubConfigsLocked(propIdAreaId, newConfig);
 }
 
 VhalResult<void> SubscriptionManager::removeContinuousSubscriberLocked(
@@ -108,25 +158,62 @@
     // Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases.
     ContSubConfigs newConfig = mContSubConfigsByPropIdArea[propIdAreaId];
     newConfig.removeClient(clientId);
-    return updateContSubConfigs(propIdAreaId, newConfig);
+    return updateContSubConfigsLocked(propIdAreaId, newConfig);
 }
 
-VhalResult<void> SubscriptionManager::updateContSubConfigs(const PropIdAreaId& propIdAreaId,
-                                                           const ContSubConfigs& newConfig) {
-    if (newConfig.getMaxSampleRateHz() ==
-        mContSubConfigsByPropIdArea[propIdAreaId].getMaxSampleRateHz()) {
+VhalResult<void> SubscriptionManager::removeOnChangeSubscriberLocked(
+        const PropIdAreaId& propIdAreaId) {
+    if (mClientsByPropIdAreaId[propIdAreaId].size() > 1) {
+        // After unsubscribing this client, there is still client subscribed, so do nothing.
+        return {};
+    }
+
+    int32_t propId = propIdAreaId.propId;
+    int32_t areaId = propIdAreaId.areaId;
+    if (auto status = mVehicleHardware->unsubscribe(propId, areaId); status != StatusCode::OK) {
+        return StatusError(status)
+               << StringPrintf("failed unsubscribe for prop: %s, areaId: %" PRId32,
+                               propIdToString(propId).c_str(), areaId);
+    }
+    return {};
+}
+
+VhalResult<void> SubscriptionManager::updateContSubConfigsLocked(const PropIdAreaId& propIdAreaId,
+                                                                 const ContSubConfigs& newConfig) {
+    const auto& oldConfig = mContSubConfigsByPropIdArea[propIdAreaId];
+    float newRateHz = newConfig.getMaxSampleRateHz();
+    float oldRateHz = oldConfig.getMaxSampleRateHz();
+    if (newRateHz == oldRateHz && newConfig.isVurEnabled() == oldConfig.isVurEnabled()) {
         mContSubConfigsByPropIdArea[propIdAreaId] = newConfig;
         return {};
     }
-    float newRateHz = newConfig.getMaxSampleRateHz();
     int32_t propId = propIdAreaId.propId;
     int32_t areaId = propIdAreaId.areaId;
-    if (auto status = mVehicleHardware->updateSampleRate(propId, areaId, newRateHz);
-        status != StatusCode::OK) {
-        return StatusError(status) << StringPrintf("failed to update sample rate for prop: %" PRId32
-                                                   ", area"
-                                                   ": %" PRId32 ", sample rate: %f HZ",
-                                                   propId, areaId, newRateHz);
+    if (newRateHz != oldRateHz) {
+        if (auto status = mVehicleHardware->updateSampleRate(propId, areaId, newRateHz);
+            status != StatusCode::OK) {
+            return StatusError(status)
+                   << StringPrintf("failed to update sample rate for prop: %s, areaId: %" PRId32
+                                   ", sample rate: %f HZ",
+                                   propIdToString(propId).c_str(), areaId, newRateHz);
+        }
+    }
+    if (newRateHz != 0) {
+        if (auto status = mVehicleHardware->subscribe(
+                    newSubscribeOptions(propId, areaId, newRateHz, newConfig.isVurEnabled()));
+            status != StatusCode::OK) {
+            return StatusError(status) << StringPrintf(
+                           "failed subscribe for prop: %s, areaId"
+                           ": %" PRId32 ", sample rate: %f HZ",
+                           propIdToString(propId).c_str(), areaId, newRateHz);
+        }
+    } else {
+        if (auto status = mVehicleHardware->unsubscribe(propId, areaId); status != StatusCode::OK) {
+            return StatusError(status) << StringPrintf(
+                           "failed unsubscribe for prop: %s, areaId"
+                           ": %" PRId32,
+                           propIdToString(propId).c_str(), areaId);
+        }
     }
     mContSubConfigsByPropIdArea[propIdAreaId] = newConfig;
     return {};
@@ -163,21 +250,54 @@
                     .propId = propId,
                     .areaId = areaId,
             };
+            VhalResult<void> result;
             if (isContinuousProperty) {
-                if (auto result = addContinuousSubscriberLocked(clientId, propIdAreaId,
-                                                                option.sampleRate);
-                    !result.ok()) {
-                    return result;
-                }
+                result = addContinuousSubscriberLocked(clientId, propIdAreaId, option.sampleRate,
+                                                       option.enableVariableUpdateRate);
+            } else {
+                result = addOnChangeSubscriberLocked(propIdAreaId);
+            }
+
+            if (!result.ok()) {
+                return result;
             }
 
             mSubscribedPropsByClient[clientId].insert(propIdAreaId);
-            mClientsByPropIdArea[propIdAreaId][clientId] = callback;
+            mClientsByPropIdAreaId[propIdAreaId][clientId] = callback;
         }
     }
     return {};
 }
 
+VhalResult<void> SubscriptionManager::unsubscribePropIdAreaIdLocked(
+        SubscriptionManager::ClientIdType clientId, const PropIdAreaId& propIdAreaId) {
+    if (mContSubConfigsByPropIdArea.find(propIdAreaId) != mContSubConfigsByPropIdArea.end()) {
+        // This is a subscribed continuous property.
+        if (auto result = removeContinuousSubscriberLocked(clientId, propIdAreaId); !result.ok()) {
+            return result;
+        }
+    } else {
+        if (mClientsByPropIdAreaId.find(propIdAreaId) == mClientsByPropIdAreaId.end()) {
+            ALOGW("Unsubscribe: The property: %s, areaId: %" PRId32
+                  " was not previously subscribed, do nothing",
+                  propIdToString(propIdAreaId.propId).c_str(), propIdAreaId.areaId);
+            return {};
+        }
+        // This is an on-change property.
+        if (auto result = removeOnChangeSubscriberLocked(propIdAreaId); !result.ok()) {
+            return result;
+        }
+    }
+
+    auto& clients = mClientsByPropIdAreaId[propIdAreaId];
+    clients.erase(clientId);
+    if (clients.empty()) {
+        mClientsByPropIdAreaId.erase(propIdAreaId);
+        mContSubConfigsByPropIdArea.erase(propIdAreaId);
+    }
+    return {};
+}
+
 VhalResult<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId,
                                                   const std::vector<int32_t>& propIds) {
     std::scoped_lock<std::mutex> lockGuard(mLock);
@@ -186,39 +306,27 @@
         return StatusError(StatusCode::INVALID_ARG)
                << "No property was subscribed for the callback";
     }
-    std::unordered_set<int32_t> subscribedPropIds;
-    for (auto const& propIdAreaId : mSubscribedPropsByClient[clientId]) {
-        subscribedPropIds.insert(propIdAreaId.propId);
-    }
 
+    std::vector<PropIdAreaId> propIdAreaIdsToUnsubscribe;
+    std::unordered_set<int32_t> propIdSet;
     for (int32_t propId : propIds) {
-        if (subscribedPropIds.find(propId) == subscribedPropIds.end()) {
-            return StatusError(StatusCode::INVALID_ARG)
-                   << "property ID: " << propId << " is not subscribed";
+        propIdSet.insert(propId);
+    }
+    auto& subscribedPropIdsAreaIds = mSubscribedPropsByClient[clientId];
+    for (const auto& propIdAreaId : subscribedPropIdsAreaIds) {
+        if (propIdSet.find(propIdAreaId.propId) != propIdSet.end()) {
+            propIdAreaIdsToUnsubscribe.push_back(propIdAreaId);
         }
     }
 
-    auto& propIdAreaIds = mSubscribedPropsByClient[clientId];
-    auto it = propIdAreaIds.begin();
-    while (it != propIdAreaIds.end()) {
-        int32_t propId = it->propId;
-        if (std::find(propIds.begin(), propIds.end(), propId) != propIds.end()) {
-            if (auto result = removeContinuousSubscriberLocked(clientId, *it); !result.ok()) {
-                return result;
-            }
-
-            auto& clients = mClientsByPropIdArea[*it];
-            clients.erase(clientId);
-            if (clients.empty()) {
-                mClientsByPropIdArea.erase(*it);
-                mContSubConfigsByPropIdArea.erase(*it);
-            }
-            it = propIdAreaIds.erase(it);
-        } else {
-            it++;
+    for (const auto& propIdAreaId : propIdAreaIdsToUnsubscribe) {
+        if (auto result = unsubscribePropIdAreaIdLocked(clientId, propIdAreaId); !result.ok()) {
+            return result;
         }
+        subscribedPropIdsAreaIds.erase(propIdAreaId);
     }
-    if (propIdAreaIds.empty()) {
+
+    if (subscribedPropIdsAreaIds.empty()) {
         mSubscribedPropsByClient.erase(clientId);
     }
     return {};
@@ -233,38 +341,68 @@
 
     auto& subscriptions = mSubscribedPropsByClient[clientId];
     for (auto const& propIdAreaId : subscriptions) {
-        if (auto result = removeContinuousSubscriberLocked(clientId, propIdAreaId); !result.ok()) {
+        if (auto result = unsubscribePropIdAreaIdLocked(clientId, propIdAreaId); !result.ok()) {
             return result;
         }
-
-        auto& clients = mClientsByPropIdArea[propIdAreaId];
-        clients.erase(clientId);
-        if (clients.empty()) {
-            mClientsByPropIdArea.erase(propIdAreaId);
-            mContSubConfigsByPropIdArea.erase(propIdAreaId);
-        }
     }
     mSubscribedPropsByClient.erase(clientId);
     return {};
 }
 
-std::unordered_map<std::shared_ptr<IVehicleCallback>, std::vector<const VehiclePropValue*>>
-SubscriptionManager::getSubscribedClients(const std::vector<VehiclePropValue>& updatedValues) {
-    std::scoped_lock<std::mutex> lockGuard(mLock);
-    std::unordered_map<std::shared_ptr<IVehicleCallback>, std::vector<const VehiclePropValue*>>
-            clients;
+bool SubscriptionManager::isValueUpdatedLocked(const std::shared_ptr<IVehicleCallback>& callback,
+                                               const VehiclePropValue& value) {
+    const auto& it = mContSubValuesByCallback[callback].find(value);
+    if (it == mContSubValuesByCallback[callback].end()) {
+        mContSubValuesByCallback[callback].insert(value);
+        return true;
+    }
 
-    for (const auto& value : updatedValues) {
+    if (it->timestamp > value.timestamp) {
+        ALOGE("The updated property value: %s is outdated, ignored", value.toString().c_str());
+        return false;
+    }
+
+    if (it->value == value.value && it->status == value.status) {
+        // Even though the property value is the same, we need to store the new property event to
+        // update the timestamp.
+        mContSubValuesByCallback[callback].insert(value);
+        ALOGD("The updated property value for propId: %" PRId32 ", areaId: %" PRId32
+              " has the "
+              "same value and status, ignored if VUR is enabled",
+              it->prop, it->areaId);
+        return false;
+    }
+
+    mContSubValuesByCallback[callback].insert(value);
+    return true;
+}
+
+std::unordered_map<std::shared_ptr<IVehicleCallback>, std::vector<VehiclePropValue>>
+SubscriptionManager::getSubscribedClients(std::vector<VehiclePropValue>&& updatedValues) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    std::unordered_map<std::shared_ptr<IVehicleCallback>, std::vector<VehiclePropValue>> clients;
+
+    for (auto& value : updatedValues) {
         PropIdAreaId propIdAreaId{
                 .propId = value.prop,
                 .areaId = value.areaId,
         };
-        if (mClientsByPropIdArea.find(propIdAreaId) == mClientsByPropIdArea.end()) {
+        if (mClientsByPropIdAreaId.find(propIdAreaId) == mClientsByPropIdAreaId.end()) {
             continue;
         }
 
-        for (const auto& [_, client] : mClientsByPropIdArea[propIdAreaId]) {
-            clients[client].push_back(&value);
+        for (const auto& [client, callback] : mClientsByPropIdAreaId[propIdAreaId]) {
+            auto& subConfigs = mContSubConfigsByPropIdArea[propIdAreaId];
+            // If client wants VUR (and VUR is supported as checked in DefaultVehicleHal), it is
+            // possible that VUR is not enabled in IVehicleHardware because another client does not
+            // enable VUR. We will implement VUR filtering here for the client that enables it.
+            if (subConfigs.isVurEnabledForClient(client) && !subConfigs.isVurEnabled()) {
+                if (isValueUpdatedLocked(callback, value)) {
+                    clients[callback].push_back(value);
+                }
+            } else {
+                clients[callback].push_back(value);
+            }
         }
     }
     return clients;
@@ -281,11 +419,11 @@
                 .propId = errorEvent.propId,
                 .areaId = errorEvent.areaId,
         };
-        if (mClientsByPropIdArea.find(propIdAreaId) == mClientsByPropIdArea.end()) {
+        if (mClientsByPropIdAreaId.find(propIdAreaId) == mClientsByPropIdAreaId.end()) {
             continue;
         }
 
-        for (const auto& [_, client] : mClientsByPropIdArea[propIdAreaId]) {
+        for (const auto& [_, client] : mClientsByPropIdAreaId[propIdAreaId]) {
             clients[client].push_back({
                     .propId = errorEvent.propId,
                     .areaId = errorEvent.areaId,
@@ -298,7 +436,7 @@
 
 bool SubscriptionManager::isEmpty() {
     std::scoped_lock<std::mutex> lockGuard(mLock);
-    return mSubscribedPropsByClient.empty() && mClientsByPropIdArea.empty();
+    return mSubscribedPropsByClient.empty() && mClientsByPropIdAreaId.empty();
 }
 
 size_t SubscriptionManager::countClients() {
diff --git a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
index fb14373..63964ef 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
@@ -98,12 +98,26 @@
 constexpr int32_t READ_ONLY_PROP = 10006 + 0x10000000 + 0x01000000 + 0x00400000;
 // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
 constexpr int32_t WRITE_ONLY_PROP = 10007 + 0x10000000 + 0x01000000 + 0x00400000;
+// VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
+constexpr int32_t GLOBAL_CONTINUOUS_PROP_NO_VUR = 10008 + 0x10000000 + 0x01000000 + 0x00400000;
+// VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
+constexpr int32_t GLOBAL_NONE_ACCESS_PROP = 10009 + 0x10000000 + 0x01000000 + 0x00400000;
+// VehiclePropertyGroup:SYSTEM,VehicleArea:WINDOW,VehiclePropertyType:INT32
+constexpr int32_t AREA_NONE_ACCESS_PROP = 10010 + 0x10000000 + 0x03000000 + 0x00400000;
 
 int32_t testInt32VecProp(size_t i) {
     // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32_VEC
     return static_cast<int32_t>(i) + 0x10000000 + 0x01000000 + 0x00410000;
 }
 
+std::string toString(const std::vector<SubscribeOptions>& options) {
+    std::string optionsStr;
+    for (const auto& option : options) {
+        optionsStr += option.toString() + "\n";
+    }
+    return optionsStr;
+}
+
 struct PropConfigCmp {
     bool operator()(const VehiclePropConfig& a, const VehiclePropConfig& b) const {
         return (a.prop < b.prop);
@@ -165,6 +179,26 @@
                                     .value.int32Values = {0},
                             },
                     .expectedStatus = StatusCode::ACCESS_DENIED,
+            },
+            {
+                    .name = "none_access",
+                    .request =
+                            {
+                                    .prop = GLOBAL_NONE_ACCESS_PROP,
+                                    .value.int32Values = {0},
+                            },
+                    .expectedStatus = StatusCode::ACCESS_DENIED,
+            },
+            {
+                    .name = "none_area_access",
+                    .request =
+                            {
+                                    .prop = AREA_NONE_ACCESS_PROP,
+                                    .value.int32Values = {0},
+                                    // Only ROW_1_LEFT is allowed.
+                                    .areaId = toInt(VehicleAreaWindow::ROW_1_RIGHT),
+                            },
+                    .expectedStatus = StatusCode::ACCESS_DENIED,
             }};
 }
 
@@ -211,17 +245,18 @@
 
 class DefaultVehicleHalTest : public testing::Test {
   public:
-    void SetUp() override {
-        auto hardware = std::make_unique<MockVehicleHardware>();
+    void SetUp() override { init(std::make_unique<MockVehicleHardware>()); }
+
+    void init(std::unique_ptr<MockVehicleHardware> hardware) {
         std::vector<VehiclePropConfig> testConfigs;
         for (size_t i = 0; i < 10000; i++) {
             testConfigs.push_back(VehiclePropConfig{
                     .prop = testInt32VecProp(i),
-                    .access = VehiclePropertyAccess::READ_WRITE,
                     .areaConfigs =
                             {
                                     {
                                             .areaId = 0,
+                                            .access = VehiclePropertyAccess::READ_WRITE,
                                             .minInt32Value = 0,
                                             .maxInt32Value = 100,
                                     },
@@ -231,9 +266,9 @@
         // A property with area config.
         testConfigs.push_back(
                 VehiclePropConfig{.prop = INT32_WINDOW_PROP,
-                                  .access = VehiclePropertyAccess::READ_WRITE,
                                   .areaConfigs = {{
                                           .areaId = toInt(VehicleAreaWindow::ROW_1_LEFT),
+                                          .access = VehiclePropertyAccess::READ_WRITE,
                                           .minInt32Value = 0,
                                           .maxInt32Value = 100,
                                   }}});
@@ -244,8 +279,18 @@
                 .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
         });
         // A global continuous property.
+        testConfigs.push_back(VehiclePropConfig{.prop = GLOBAL_CONTINUOUS_PROP,
+                                                .access = VehiclePropertyAccess::READ_WRITE,
+                                                .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
+                                                .minSampleRate = 0.0,
+                                                .maxSampleRate = 100.0,
+                                                .areaConfigs = {{
+                                                        .areaId = 0,
+                                                        .supportVariableUpdateRate = true,
+                                                }}});
+        // A global continuous property that does not support VUR.
         testConfigs.push_back(VehiclePropConfig{
-                .prop = GLOBAL_CONTINUOUS_PROP,
+                .prop = GLOBAL_CONTINUOUS_PROP_NO_VUR,
                 .access = VehiclePropertyAccess::READ_WRITE,
                 .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
                 .minSampleRate = 0.0,
@@ -254,18 +299,19 @@
         // A per-area on-change property.
         testConfigs.push_back(VehiclePropConfig{
                 .prop = AREA_ON_CHANGE_PROP,
-                .access = VehiclePropertyAccess::READ_WRITE,
                 .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
                 .areaConfigs =
                         {
                                 {
 
                                         .areaId = toInt(VehicleAreaWindow::ROW_1_LEFT),
+                                        .access = VehiclePropertyAccess::READ_WRITE,
                                         .minInt32Value = 0,
                                         .maxInt32Value = 100,
                                 },
                                 {
                                         .areaId = toInt(VehicleAreaWindow::ROW_1_RIGHT),
+                                        .access = VehiclePropertyAccess::READ,
                                         .minInt32Value = 0,
                                         .maxInt32Value = 100,
                                 },
@@ -274,7 +320,6 @@
         // A per-area continuous property.
         testConfigs.push_back(VehiclePropConfig{
                 .prop = AREA_CONTINUOUS_PROP,
-                .access = VehiclePropertyAccess::READ_WRITE,
                 .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
                 .minSampleRate = 0.0,
                 .maxSampleRate = 1000.0,
@@ -283,13 +328,17 @@
                                 {
 
                                         .areaId = toInt(VehicleAreaWindow::ROW_1_LEFT),
+                                        .access = VehiclePropertyAccess::READ_WRITE,
                                         .minInt32Value = 0,
                                         .maxInt32Value = 100,
+                                        .supportVariableUpdateRate = true,
                                 },
                                 {
                                         .areaId = toInt(VehicleAreaWindow::ROW_1_RIGHT),
+                                        .access = VehiclePropertyAccess::READ_WRITE,
                                         .minInt32Value = 0,
                                         .maxInt32Value = 100,
+                                        .supportVariableUpdateRate = false,
                                 },
                         },
         });
@@ -309,6 +358,37 @@
                 .minSampleRate = 0.0,
                 .maxSampleRate = 1000.0,
         });
+        // Global access set to NONE
+        testConfigs.push_back(VehiclePropConfig{
+                .prop = GLOBAL_NONE_ACCESS_PROP,
+                .access = VehiclePropertyAccess::NONE,
+                .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
+                .minSampleRate = 0.0,
+                .maxSampleRate = 100.0,
+        });
+        // Area access set to NONE
+        testConfigs.push_back(VehiclePropConfig{
+                .prop = AREA_NONE_ACCESS_PROP,
+                .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
+                .minSampleRate = 0.0,
+                .maxSampleRate = 1000.0,
+                .areaConfigs =
+                        {
+                                {
+
+                                        .areaId = toInt(VehicleAreaWindow::ROW_1_LEFT),
+                                        .access = VehiclePropertyAccess::NONE,
+                                        .minInt32Value = 0,
+                                        .maxInt32Value = 100,
+                                },
+                                {
+                                        .areaId = toInt(VehicleAreaWindow::ROW_1_RIGHT),
+                                        .access = VehiclePropertyAccess::NONE,
+                                        .minInt32Value = 0,
+                                        .maxInt32Value = 100,
+                                },
+                        },
+        });
         // Register the heartbeat event property.
         testConfigs.push_back(VehiclePropConfig{
                 .prop = toInt(VehicleProperty::VHAL_HEARTBEAT),
@@ -650,6 +730,21 @@
                                                     .prop = WRITE_ONLY_PROP,
                                             },
                             },
+                            {
+                                    .requestId = 1,
+                                    .prop =
+                                            {
+                                                    .prop = GLOBAL_NONE_ACCESS_PROP,
+                                            },
+                            },
+                            {
+                                    .requestId = 2,
+                                    .prop =
+                                            {
+                                                    .prop = AREA_NONE_ACCESS_PROP,
+                                                    .areaId = toInt(VehicleAreaWindow::ROW_1_LEFT),
+                                            },
+                            },
                     },
     };
 
@@ -667,6 +762,14 @@
                                                             .requestId = 0,
                                                             .status = StatusCode::ACCESS_DENIED,
                                                     },
+                                                    {
+                                                            .requestId = 1,
+                                                            .status = StatusCode::ACCESS_DENIED,
+                                                    },
+                                                    {
+                                                            .requestId = 2,
+                                                            .status = StatusCode::ACCESS_DENIED,
+                                                    },
                                             }))
             << "expect to get ACCESS_DENIED status if no read permission";
 }
@@ -1293,8 +1396,8 @@
 
     auto maybeResults = getCallback()->nextOnPropertyEventResults();
     ASSERT_TRUE(maybeResults.has_value()) << "no results in callback";
-    ASSERT_THAT(maybeResults.value().payloads, UnorderedElementsAre(testValue1, testValue2))
-            << "results mismatch, expect two on-change events for all updated areas";
+    ASSERT_THAT(maybeResults.value().payloads, UnorderedElementsAre(testValue1))
+            << "results mismatch, expect one on-change events for all updated areas";
     ASSERT_FALSE(getCallback()->nextOnPropertyEventResults().has_value())
             << "more results than expected";
 }
@@ -1351,6 +1454,62 @@
     EXPECT_EQ(countClients(), static_cast<size_t>(1));
 }
 
+TEST_F(DefaultVehicleHalTest, testSubscribeContinuous_propNotSupportVur) {
+    std::vector<SubscribeOptions> options = {
+            {
+                    .propId = GLOBAL_CONTINUOUS_PROP,
+                    .sampleRate = 20.0,
+                    .enableVariableUpdateRate = true,
+            },
+            {
+                    .propId = GLOBAL_CONTINUOUS_PROP_NO_VUR,
+                    .sampleRate = 30.0,
+                    .enableVariableUpdateRate = true,
+            },
+    };
+
+    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+    ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage();
+    auto receivedSubscribeOptions = getHardware()->getSubscribeOptions();
+    ASSERT_THAT(receivedSubscribeOptions, UnorderedElementsAre(
+                                                  SubscribeOptions{
+                                                          .propId = GLOBAL_CONTINUOUS_PROP,
+                                                          .areaIds = {0},
+                                                          .enableVariableUpdateRate = true,
+                                                          .sampleRate = 20.0,
+                                                  },
+                                                  SubscribeOptions{
+                                                          .propId = GLOBAL_CONTINUOUS_PROP_NO_VUR,
+                                                          .areaIds = {0},
+                                                          .enableVariableUpdateRate = false,
+                                                          .sampleRate = 30.0,
+                                                  }))
+            << "received unexpected subscribe options: " << toString(receivedSubscribeOptions);
+}
+
+TEST_F(DefaultVehicleHalTest, testSubscribeContinuous_propSupportVurNotEnabled) {
+    std::vector<SubscribeOptions> options = {
+            {
+                    .propId = GLOBAL_CONTINUOUS_PROP,
+                    .sampleRate = 20.0,
+                    .enableVariableUpdateRate = false,
+            },
+    };
+
+    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+    ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage();
+    auto receivedSubscribeOptions = getHardware()->getSubscribeOptions();
+    ASSERT_THAT(receivedSubscribeOptions, UnorderedElementsAre(SubscribeOptions{
+                                                  .propId = GLOBAL_CONTINUOUS_PROP,
+                                                  .areaIds = {0},
+                                                  .enableVariableUpdateRate = false,
+                                                  .sampleRate = 20.0,
+                                          }))
+            << "received unexpected subscribe options: " << toString(receivedSubscribeOptions);
+}
+
 TEST_F(DefaultVehicleHalTest, testSubscribeAreaContinuous) {
     std::vector<SubscribeOptions> options = {
             {
@@ -1403,6 +1562,44 @@
     ASSERT_GE(rightCount, static_cast<size_t>(5));
 }
 
+TEST_F(DefaultVehicleHalTest, testAreaContinuous_areaNotSupportVur) {
+    std::vector<SubscribeOptions> options = {
+            {
+                    .propId = AREA_CONTINUOUS_PROP,
+                    .sampleRate = 20.0,
+                    .areaIds = {toInt(VehicleAreaWindow::ROW_1_LEFT)},
+                    .enableVariableUpdateRate = true,
+            },
+            {
+                    .propId = AREA_CONTINUOUS_PROP,
+                    .sampleRate = 10.0,
+                    .areaIds = {toInt(VehicleAreaWindow::ROW_1_RIGHT)},
+                    .enableVariableUpdateRate = true,
+            },
+    };
+
+    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+    ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage();
+    auto receivedSubscribeOptions = getHardware()->getSubscribeOptions();
+    ASSERT_THAT(receivedSubscribeOptions,
+                UnorderedElementsAre(
+                        SubscribeOptions{
+                                .propId = AREA_CONTINUOUS_PROP,
+                                .sampleRate = 20.0,
+                                .areaIds = {toInt(VehicleAreaWindow::ROW_1_LEFT)},
+                                .enableVariableUpdateRate = true,
+                        },
+                        SubscribeOptions{
+                                .propId = AREA_CONTINUOUS_PROP,
+                                .sampleRate = 10.0,
+                                .areaIds = {toInt(VehicleAreaWindow::ROW_1_RIGHT)},
+                                // Area2 actually does not support VUR.
+                                .enableVariableUpdateRate = false,
+                        }))
+            << "received unexpected subscribe options: " << toString(receivedSubscribeOptions);
+}
+
 TEST_F(DefaultVehicleHalTest, testUnsubscribeOnChange) {
     std::vector<SubscribeOptions> options = {
             {
@@ -1510,6 +1707,27 @@
     ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::ACCESS_DENIED));
 }
 
+TEST_F(DefaultVehicleHalTest, testSubscribeGlobalNoneAccess) {
+    std::vector<SubscribeOptions> options = {{
+            .propId = GLOBAL_NONE_ACCESS_PROP,
+    }};
+
+    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+    ASSERT_FALSE(status.isOk()) << "subscribe to a property with NONE global access must fail";
+    ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::ACCESS_DENIED));
+}
+
+TEST_F(DefaultVehicleHalTest, testSubscribeAreaNoneAccess) {
+    std::vector<SubscribeOptions> options = {
+            {.propId = AREA_NONE_ACCESS_PROP, .areaIds = {toInt(VehicleAreaWindow::ROW_1_LEFT)}}};
+
+    auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+
+    ASSERT_FALSE(status.isOk()) << "subscribe to a property with NONE area access must fail";
+    ASSERT_EQ(status.getServiceSpecificError(), toInt(StatusCode::ACCESS_DENIED));
+}
+
 TEST_F(DefaultVehicleHalTest, testUnsubscribeFailure) {
     auto status = getClient()->unsubscribe(getCallbackClient(),
                                            std::vector<int32_t>({GLOBAL_ON_CHANGE_PROP}));
@@ -1711,6 +1929,92 @@
     ASSERT_THAT(vehiclePropErrors.payloads, UnorderedElementsAreArray(expectedResults));
 }
 
+TEST_F(DefaultVehicleHalTest, testBatchOnPropertyChangeEvents) {
+    auto hardware = std::make_unique<MockVehicleHardware>();
+    hardware->setPropertyOnChangeEventBatchingWindow(std::chrono::milliseconds(10));
+    init(std::move(hardware));
+
+    std::vector<SubscribeOptions> options = {
+            {
+                    .propId = GLOBAL_ON_CHANGE_PROP,
+            },
+            {
+                    .propId = AREA_ON_CHANGE_PROP,
+                    // No areaIds means subscribing to all area IDs.
+                    .areaIds = {},
+            },
+    };
+
+    getClient()->subscribe(getCallbackClient(), options, 0);
+    VehiclePropValue testValue1 = {
+            .prop = GLOBAL_ON_CHANGE_PROP,
+            .value.int32Values = {0},
+    };
+    SetValueRequest request1 = {
+            .requestId = 1,
+            .value = testValue1,
+    };
+    SetValueResult result1 = {
+            .requestId = 1,
+            .status = StatusCode::OK,
+    };
+    VehiclePropValue testValue2 = {
+            .prop = AREA_ON_CHANGE_PROP,
+            .areaId = toInt(VehicleAreaWindow::ROW_1_LEFT),
+            .value.int32Values = {1},
+    };
+    SetValueRequest request2 = {
+            .requestId = 2,
+            .value = testValue2,
+    };
+    SetValueResult result2 = {
+            .requestId = 2,
+            .status = StatusCode::OK,
+    };
+    VehiclePropValue testValue3 = {
+            .prop = AREA_ON_CHANGE_PROP,
+            .areaId = toInt(VehicleAreaWindow::ROW_1_RIGHT),
+            .value.int32Values = {1},
+    };
+    SetValueRequest request3 = {
+            .requestId = 3,
+            .value = testValue3,
+    };
+    SetValueResult result3 = {
+            .requestId = 3,
+            .status = StatusCode::ACCESS_DENIED,
+    };
+    // Prepare the responses
+    for (int i = 0; i < 2; i++) {
+        getHardware()->addSetValueResponses({result1});
+        getHardware()->addSetValueResponses({result2, result3});
+    }
+
+    // Try to cause two batches, each with three on property change events.
+    // Set GLOBAL_ON_CHANGE_PROP causing one event.
+    // Set AREA_ON_CHANGE_PROP with two areas causing two events.
+    for (int i = 0; i < 2; i++) {
+        auto status = getClient()->setValues(getCallbackClient(),
+                                             SetValueRequests{.payloads = {request1}});
+        ASSERT_TRUE(status.isOk()) << "setValues failed: " << status.getMessage();
+
+        status = getClient()->setValues(getCallbackClient(),
+                                        SetValueRequests{.payloads = {request2, request3}});
+        ASSERT_TRUE(status.isOk()) << "setValues failed: " << status.getMessage();
+
+        ASSERT_TRUE(getCallback()->waitForOnPropertyEventResults(/*size=*/1,
+                                                                 /*timeoutInNano=*/1'000'000'000))
+                << "not received enough property change events before timeout";
+
+        auto maybeResults = getCallback()->nextOnPropertyEventResults();
+        ASSERT_TRUE(maybeResults.has_value()) << "no results in callback";
+        ASSERT_THAT(maybeResults.value().payloads, UnorderedElementsAre(testValue1, testValue2))
+                << "results mismatch, expect 2 batched on change events";
+        ASSERT_FALSE(getCallback()->nextOnPropertyEventResults().has_value())
+                << "more results than expected";
+    }
+}
+
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.cpp b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.cpp
index 54fede1..c272123 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.cpp
@@ -137,6 +137,14 @@
     });
 }
 
+bool MockVehicleCallback::waitForOnPropertyEventResults(size_t size, size_t timeoutInNano) {
+    std::unique_lock lk(mLock);
+    return mCond.wait_for(lk, std::chrono::nanoseconds(timeoutInNano), [this, size] {
+        ScopedLockAssertion lockAssertion(mLock);
+        return mOnPropertyEventResults.size() >= size;
+    });
+}
+
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.h b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.h
index 1545eae..672ff4f 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.h
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleCallback.h
@@ -69,6 +69,7 @@
     size_t countOnPropertyEventResults();
     bool waitForSetValueResults(size_t size, size_t timeoutInNano);
     bool waitForGetValueResults(size_t size, size_t timeoutInNano);
+    bool waitForOnPropertyEventResults(size_t size, size_t timeoutInNano);
 
   private:
     std::mutex mLock;
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
index ba0d33d..db15c89 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
@@ -29,6 +29,7 @@
 using ::aidl::android::hardware::automotive::vehicle::SetValueRequest;
 using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
 using ::aidl::android::hardware::automotive::vehicle::StatusCode;
+using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
 
@@ -88,7 +89,40 @@
     return StatusCode::OK;
 }
 
-StatusCode MockVehicleHardware::updateSampleRate(int32_t propId, int32_t areaId, float sampleRate) {
+StatusCode MockVehicleHardware::subscribe(SubscribeOptions options) {
+    {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
+        mSubscribeOptions.push_back(options);
+    }
+    for (int32_t areaId : options.areaIds) {
+        if (auto status = subscribePropIdAreaId(options.propId, areaId, options.sampleRate);
+            status != StatusCode::OK) {
+            return status;
+        }
+    }
+    return StatusCode::OK;
+}
+
+std::vector<SubscribeOptions> MockVehicleHardware::getSubscribeOptions() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return mSubscribeOptions;
+}
+
+void MockVehicleHardware::clearSubscribeOptions() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    mSubscribeOptions.clear();
+}
+
+StatusCode MockVehicleHardware::subscribePropIdAreaId(int32_t propId, int32_t areaId,
+                                                      float sampleRateHz) {
+    if (sampleRateHz == 0) {
+        // on-change property.
+        std::scoped_lock<std::mutex> lockGuard(mLock);
+        mSubOnChangePropIdAreaIds.insert(std::pair<int32_t, int32_t>(propId, areaId));
+        return StatusCode::OK;
+    }
+
+    // continuous property.
     std::shared_ptr<std::function<void()>> action;
 
     {
@@ -97,9 +131,6 @@
             // Remove the previous action register for this [propId, areaId].
             mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propId][areaId]);
         }
-        if (sampleRate == 0) {
-            return StatusCode::OK;
-        }
 
         // We are sure 'propertyChangeCallback' would be alive because we would unregister timer
         // before destroying 'this' which owns mPropertyChangeCallback.
@@ -107,8 +138,8 @@
         action = std::make_shared<std::function<void()>>([propertyChangeCallback, propId, areaId] {
             std::vector<VehiclePropValue> values = {
                     {
-                            .prop = propId,
                             .areaId = areaId,
+                            .prop = propId,
                     },
             };
             (*propertyChangeCallback)(values);
@@ -119,11 +150,45 @@
 
     // In mock implementation, we generate a new property change event for this property at sample
     // rate.
-    int64_t interval = static_cast<int64_t>(1'000'000'000. / sampleRate);
+    int64_t interval = static_cast<int64_t>(1'000'000'000. / sampleRateHz);
     mRecurrentTimer->registerTimerCallback(interval, action);
     return StatusCode::OK;
 }
 
+StatusCode MockVehicleHardware::unsubscribe(int32_t propId, int32_t areaId) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    // For on-change property.
+    mSubOnChangePropIdAreaIds.erase(std::make_pair(propId, areaId));
+    // for continuous property.
+    if (mRecurrentActions[propId][areaId] != nullptr) {
+        // Remove the previous action register for this [propId, areaId].
+        mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propId][areaId]);
+        mRecurrentActions[propId].erase(areaId);
+        if (mRecurrentActions[propId].empty()) {
+            mRecurrentActions.erase(propId);
+        }
+    }
+    return StatusCode::OK;
+}
+
+std::set<std::pair<int32_t, int32_t>> MockVehicleHardware::getSubscribedOnChangePropIdAreaIds() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    std::set<std::pair<int32_t, int32_t>> propIdAreaIds;
+    propIdAreaIds = mSubOnChangePropIdAreaIds;
+    return propIdAreaIds;
+}
+
+std::set<std::pair<int32_t, int32_t>> MockVehicleHardware::getSubscribedContinuousPropIdAreaIds() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    std::set<std::pair<int32_t, int32_t>> propIdAreaIds;
+    for (const auto& [propId, actionByAreaId] : mRecurrentActions) {
+        for (const auto& [areaId, _] : actionByAreaId) {
+            propIdAreaIds.insert(std::make_pair(propId, areaId));
+        }
+    }
+    return propIdAreaIds;
+}
+
 void MockVehicleHardware::registerOnPropertyChangeEvent(
         std::unique_ptr<const PropertyChangeCallback> callback) {
     std::scoped_lock<std::mutex> lockGuard(mLock);
@@ -186,6 +251,16 @@
     mSleepTime = timeInNano;
 }
 
+void MockVehicleHardware::setPropertyOnChangeEventBatchingWindow(std::chrono::nanoseconds window) {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    mEventBatchingWindow = window;
+}
+
+std::chrono::nanoseconds MockVehicleHardware::getPropertyOnChangeEventBatchingWindow() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return mEventBatchingWindow;
+}
+
 template <class ResultType>
 StatusCode MockVehicleHardware::returnResponse(
         std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback,
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
index 46b30b9..eeca582 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
@@ -24,10 +24,12 @@
 #include <android-base/thread_annotations.h>
 
 #include <atomic>
+#include <chrono>
 #include <condition_variable>
 #include <list>
 #include <memory>
 #include <mutex>
+#include <set>
 #include <thread>
 #include <unordered_map>
 #include <vector>
@@ -58,8 +60,11 @@
     void registerOnPropertyChangeEvent(
             std::unique_ptr<const PropertyChangeCallback> callback) override;
     void registerOnPropertySetErrorEvent(std::unique_ptr<const PropertySetErrorCallback>) override;
-    aidl::android::hardware::automotive::vehicle::StatusCode updateSampleRate(
-            int32_t propId, int32_t areaId, float sampleRate) override;
+    aidl::android::hardware::automotive::vehicle::StatusCode subscribe(
+            aidl::android::hardware::automotive::vehicle::SubscribeOptions options) override;
+    aidl::android::hardware::automotive::vehicle::StatusCode unsubscribe(int32_t propId,
+                                                                         int32_t areaId) override;
+    std::chrono::nanoseconds getPropertyOnChangeEventBatchingWindow() override;
 
     // Test functions.
     void setPropertyConfigs(
@@ -86,6 +91,13 @@
     void setSleepTime(int64_t timeInNano);
     void setDumpResult(DumpResult result);
     void sendOnPropertySetErrorEvent(const std::vector<SetValueErrorEvent>& errorEvents);
+    void setPropertyOnChangeEventBatchingWindow(std::chrono::nanoseconds window);
+
+    std::set<std::pair<int32_t, int32_t>> getSubscribedOnChangePropIdAreaIds();
+    std::set<std::pair<int32_t, int32_t>> getSubscribedContinuousPropIdAreaIds();
+    std::vector<aidl::android::hardware::automotive::vehicle::SubscribeOptions>
+    getSubscribeOptions();
+    void clearSubscribeOptions();
 
   private:
     mutable std::mutex mLock;
@@ -110,6 +122,10 @@
             std::shared_ptr<const GetValuesCallback>,
             const std::vector<aidl::android::hardware::automotive::vehicle::GetValueRequest>&)>
             mGetValueResponder GUARDED_BY(mLock);
+    std::chrono::nanoseconds mEventBatchingWindow GUARDED_BY(mLock) = std::chrono::nanoseconds(0);
+    std::set<std::pair<int32_t, int32_t>> mSubOnChangePropIdAreaIds GUARDED_BY(mLock);
+    std::vector<aidl::android::hardware::automotive::vehicle::SubscribeOptions> mSubscribeOptions
+            GUARDED_BY(mLock);
 
     template <class ResultType>
     aidl::android::hardware::automotive::vehicle::StatusCode returnResponse(
@@ -122,6 +138,8 @@
             const std::vector<RequestType>& requests,
             std::list<std::vector<RequestType>>* storedRequests,
             std::list<std::vector<ResultType>>* storedResponses) const REQUIRES(mLock);
+    aidl::android::hardware::automotive::vehicle::StatusCode subscribePropIdAreaId(
+            int32_t propId, int32_t areaId, float sampleRateHz);
 
     DumpResult mDumpResult;
 
diff --git a/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
index cb8c8d1..aa5f003 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
@@ -43,12 +43,14 @@
 using ::aidl::android::hardware::automotive::vehicle::SetValueResults;
 using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropErrors;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValues;
 using ::ndk::ScopedAStatus;
 using ::ndk::SpAIBinder;
+using ::testing::Contains;
 using ::testing::ElementsAre;
-using ::testing::WhenSorted;
+using ::testing::UnorderedElementsAre;
 
 class PropertyCallback final : public BnVehicleCallback {
   public:
@@ -114,6 +116,8 @@
 
     void clearEvents() { return getCallback()->clearEvents(); }
 
+    std::shared_ptr<MockVehicleHardware> getHardware() { return mHardware; }
+
   private:
     std::unique_ptr<SubscriptionManager> mManager;
     std::shared_ptr<PropertyCallback> mCallback;
@@ -132,6 +136,9 @@
     auto result = getManager()->subscribe(getCallbackClient(), options, true);
     ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
 
+    ASSERT_THAT(getHardware()->getSubscribedContinuousPropIdAreaIds(),
+                UnorderedElementsAre(std::pair<int32_t, int32_t>(0, 0)));
+
     std::this_thread::sleep_for(std::chrono::seconds(1));
 
     // Theoretically trigger 10 times, but check for at least 9 times to be stable.
@@ -240,6 +247,8 @@
     result = getManager()->unsubscribe(getCallbackClient()->asBinder().get());
     ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message();
 
+    ASSERT_EQ(getHardware()->getSubscribedContinuousPropIdAreaIds().size(), 0u);
+
     // Wait for the last events to come.
     std::this_thread::sleep_for(std::chrono::milliseconds(100));
 
@@ -316,7 +325,7 @@
     EXPECT_TRUE(getEvents().empty());
 }
 
-TEST_F(SubscriptionManagerTest, testUnsubscribeFailure) {
+TEST_F(SubscriptionManagerTest, testUnsubscribeUnsubscribedPropId) {
     std::vector<SubscribeOptions> options = {
             {
                     .propId = 0,
@@ -334,14 +343,21 @@
     // Property ID: 2 was not subscribed.
     result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(),
                                        std::vector<int32_t>({0, 1, 2}));
-    ASSERT_FALSE(result.ok()) << "unsubscribe an unsubscribed property must fail";
+    ASSERT_TRUE(result.ok()) << "unsubscribe an unsubscribed property must do nothing";
 
-    // Since property 0 and property 1 was not unsubscribed successfully, we should be able to
-    // unsubscribe them again.
-    result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(),
-                                       std::vector<int32_t>({0, 1}));
-    ASSERT_TRUE(result.ok()) << "a failed unsubscription must not unsubscribe any properties"
-                             << result.error().message();
+    std::vector<VehiclePropValue> updatedValues = {
+            {
+                    .prop = 0,
+                    .areaId = 0,
+            },
+            {
+                    .prop = 1,
+                    .areaId = 0,
+            },
+    };
+    auto clients = getManager()->getSubscribedClients(std::vector<VehiclePropValue>(updatedValues));
+
+    ASSERT_EQ(clients.size(), 0u) << "all subscribed properties must be unsubscribed";
 }
 
 TEST_F(SubscriptionManagerTest, testSubscribeOnchange) {
@@ -370,6 +386,11 @@
     ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
     result = getManager()->subscribe(client2, options2, false);
     ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+    ASSERT_THAT(getHardware()->getSubscribedOnChangePropIdAreaIds(),
+                UnorderedElementsAre(std::pair<int32_t, int32_t>(0, 0),
+                                     std::pair<int32_t, int32_t>(0, 1),
+                                     std::pair<int32_t, int32_t>(1, 0)));
+    ASSERT_EQ(getHardware()->getSubscribedContinuousPropIdAreaIds().size(), 0u);
 
     std::vector<VehiclePropValue> updatedValues = {
             {
@@ -389,11 +410,11 @@
                     .areaId = 1,
             },
     };
-    auto clients = getManager()->getSubscribedClients(updatedValues);
+    auto clients = getManager()->getSubscribedClients(std::vector<VehiclePropValue>(updatedValues));
 
     ASSERT_THAT(clients[client1],
-                WhenSorted(ElementsAre(&updatedValues[0], &updatedValues[1], &updatedValues[2])));
-    ASSERT_THAT(clients[client2], ElementsAre(&updatedValues[0]));
+                UnorderedElementsAre(updatedValues[0], updatedValues[1], updatedValues[2]));
+    ASSERT_THAT(clients[client2], ElementsAre(updatedValues[0]));
 }
 
 TEST_F(SubscriptionManagerTest, testSubscribeInvalidOption) {
@@ -480,9 +501,11 @@
                     .areaId = 0,
             },
     };
-    auto clients = getManager()->getSubscribedClients(updatedValues);
+    auto clients = getManager()->getSubscribedClients(std::vector<VehiclePropValue>(updatedValues));
 
-    ASSERT_THAT(clients[getCallbackClient()], ElementsAre(&updatedValues[1]));
+    ASSERT_THAT(clients[getCallbackClient()], ElementsAre(updatedValues[1]));
+    ASSERT_THAT(getHardware()->getSubscribedOnChangePropIdAreaIds(),
+                UnorderedElementsAre(std::pair<int32_t, int32_t>(1, 0)));
 }
 
 TEST_F(SubscriptionManagerTest, testCheckSampleRateHzValid) {
@@ -497,6 +520,257 @@
     ASSERT_FALSE(SubscriptionManager::checkSampleRateHz(0));
 }
 
+TEST_F(SubscriptionManagerTest, testSubscribe_enableVur) {
+    std::vector<SubscribeOptions> options = {{
+            .propId = 0,
+            .areaIds = {0},
+            .sampleRate = 10.0,
+            .enableVariableUpdateRate = true,
+    }};
+
+    auto result = getManager()->subscribe(getCallbackClient(), options, true);
+    ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+    ASSERT_THAT(getHardware()->getSubscribeOptions(), ElementsAre(options[0]));
+}
+
+TEST_F(SubscriptionManagerTest, testSubscribe_VurStateChange) {
+    std::vector<SubscribeOptions> options = {{
+            .propId = 0,
+            .areaIds = {0},
+            .sampleRate = 10.0,
+            .enableVariableUpdateRate = true,
+    }};
+
+    auto result = getManager()->subscribe(getCallbackClient(), options, true);
+    ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+    ASSERT_THAT(getHardware()->getSubscribeOptions(), ElementsAre(options[0]));
+
+    getHardware()->clearSubscribeOptions();
+    result = getManager()->subscribe(getCallbackClient(), options, true);
+    ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+    ASSERT_TRUE(getHardware()->getSubscribeOptions().empty());
+
+    std::vector<SubscribeOptions> newOptions = {{
+            .propId = 0,
+            .areaIds = {0},
+            .sampleRate = 10.0,
+            .enableVariableUpdateRate = false,
+    }};
+    result = getManager()->subscribe(getCallbackClient(), newOptions, true);
+    ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+    ASSERT_THAT(getHardware()->getSubscribeOptions(), ElementsAre(newOptions[0]));
+}
+
+TEST_F(SubscriptionManagerTest, testSubscribe_enableVur_filterUnchangedEvents) {
+    SpAIBinder binder1 = ndk::SharedRefBase::make<PropertyCallback>()->asBinder();
+    std::shared_ptr<IVehicleCallback> client1 = IVehicleCallback::fromBinder(binder1);
+    SpAIBinder binder2 = ndk::SharedRefBase::make<PropertyCallback>()->asBinder();
+    std::shared_ptr<IVehicleCallback> client2 = IVehicleCallback::fromBinder(binder2);
+    SubscribeOptions client1Option = {
+            .propId = 0,
+            .areaIds = {0},
+            .sampleRate = 10.0,
+            .enableVariableUpdateRate = false,
+    };
+    auto result = getManager()->subscribe(client1, {client1Option}, true);
+    ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+    ASSERT_THAT(getHardware()->getSubscribeOptions(), UnorderedElementsAre(client1Option));
+
+    getHardware()->clearSubscribeOptions();
+    SubscribeOptions client2Option = {
+            .propId = 0,
+            .areaIds = {0, 1},
+            .sampleRate = 20.0,
+            .enableVariableUpdateRate = true,
+    };
+
+    result = getManager()->subscribe(client2, {client2Option}, true);
+    ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+    ASSERT_THAT(getHardware()->getSubscribeOptions(),
+                UnorderedElementsAre(
+                        SubscribeOptions{
+                                .propId = 0,
+                                .areaIds = {0},
+                                .sampleRate = 20.0,
+                                // This is enabled for client2, but disabled for client1.
+                                .enableVariableUpdateRate = false,
+                        },
+                        SubscribeOptions{
+                                .propId = 0,
+                                .areaIds = {1},
+                                .sampleRate = 20.0,
+                                .enableVariableUpdateRate = true,
+                        }));
+
+    std::vector<VehiclePropValue> propertyEvents = {{
+                                                            .prop = 0,
+                                                            .areaId = 0,
+                                                            .value = {.int32Values = {0}},
+                                                            .timestamp = 1,
+                                                    },
+                                                    {
+                                                            .prop = 0,
+                                                            .areaId = 1,
+                                                            .value = {.int32Values = {1}},
+                                                            .timestamp = 1,
+                                                    }};
+    auto clients =
+            getManager()->getSubscribedClients(std::vector<VehiclePropValue>(propertyEvents));
+
+    ASSERT_THAT(clients[client1], UnorderedElementsAre(propertyEvents[0]));
+    ASSERT_THAT(clients[client2], UnorderedElementsAre(propertyEvents[0], propertyEvents[1]));
+
+    // If the same property events happen again with a new timestamp.
+    // VUR is disabled for client1, enabled for client2.
+    clients = getManager()->getSubscribedClients({{
+            .prop = 0,
+            .areaId = 0,
+            .value = {.int32Values = {0}},
+            .timestamp = 2,
+    }});
+
+    ASSERT_FALSE(clients.find(client1) == clients.end())
+            << "Must not filter out property events if VUR is not enabled";
+    ASSERT_TRUE(clients.find(client2) == clients.end())
+            << "Must filter out property events if VUR is enabled";
+}
+
+TEST_F(SubscriptionManagerTest, testSubscribe_enableVur_mustNotFilterStatusChange) {
+    SpAIBinder binder1 = ndk::SharedRefBase::make<PropertyCallback>()->asBinder();
+    std::shared_ptr<IVehicleCallback> client1 = IVehicleCallback::fromBinder(binder1);
+    SpAIBinder binder2 = ndk::SharedRefBase::make<PropertyCallback>()->asBinder();
+    std::shared_ptr<IVehicleCallback> client2 = IVehicleCallback::fromBinder(binder2);
+    SubscribeOptions client1Option = {
+            .propId = 0,
+            .areaIds = {0},
+            .sampleRate = 10.0,
+            .enableVariableUpdateRate = false,
+    };
+    auto result = getManager()->subscribe(client1, {client1Option}, true);
+    ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+    ASSERT_THAT(getHardware()->getSubscribeOptions(), UnorderedElementsAre(client1Option));
+
+    getHardware()->clearSubscribeOptions();
+    SubscribeOptions client2Option = {
+            .propId = 0,
+            .areaIds = {0, 1},
+            .sampleRate = 20.0,
+            .enableVariableUpdateRate = true,
+    };
+
+    result = getManager()->subscribe(client2, {client2Option}, true);
+    ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+    ASSERT_THAT(getHardware()->getSubscribeOptions(),
+                UnorderedElementsAre(
+                        SubscribeOptions{
+                                .propId = 0,
+                                .areaIds = {0},
+                                .sampleRate = 20.0,
+                                // This is enabled for client2, but disabled for client1.
+                                .enableVariableUpdateRate = false,
+                        },
+                        SubscribeOptions{
+                                .propId = 0,
+                                .areaIds = {1},
+                                .sampleRate = 20.0,
+                                .enableVariableUpdateRate = true,
+                        }));
+
+    VehiclePropValue propValue1 = {
+            .prop = 0,
+            .areaId = 0,
+            .value = {.int32Values = {0}},
+            .timestamp = 1,
+    };
+    auto clients = getManager()->getSubscribedClients(std::vector<VehiclePropValue>({propValue1}));
+
+    ASSERT_THAT(clients[client1], UnorderedElementsAre(propValue1));
+
+    // A new event with the same value, but different status must not be filtered out.
+    VehiclePropValue propValue2 = {
+            .prop = 0,
+            .areaId = 0,
+            .value = {.int32Values = {0}},
+            .status = VehiclePropertyStatus::UNAVAILABLE,
+            .timestamp = 2,
+    };
+    clients = getManager()->getSubscribedClients({propValue2});
+
+    ASSERT_THAT(clients[client1], UnorderedElementsAre(propValue2))
+            << "Must not filter out property events that has status change";
+}
+
+TEST_F(SubscriptionManagerTest, testSubscribe_enableVur_timestampUpdated_filterOutdatedEvent) {
+    SpAIBinder binder1 = ndk::SharedRefBase::make<PropertyCallback>()->asBinder();
+    std::shared_ptr<IVehicleCallback> client1 = IVehicleCallback::fromBinder(binder1);
+    SpAIBinder binder2 = ndk::SharedRefBase::make<PropertyCallback>()->asBinder();
+    std::shared_ptr<IVehicleCallback> client2 = IVehicleCallback::fromBinder(binder2);
+    std::vector<SubscribeOptions> options = {{
+            .propId = 0,
+            .areaIds = {0},
+            .sampleRate = 10.0,
+            .enableVariableUpdateRate = true,
+    }};
+
+    // client1 subscribe with VUR enabled.
+    auto result = getManager()->subscribe(client1, options, true);
+    ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+    // Let client2 subscribe with VUR disabled so that we enabled VUR in DefaultVehicleHal layer.
+    result = getManager()->subscribe(client2,
+                                     {{
+                                             .propId = 0,
+                                             .areaIds = {0},
+                                             .sampleRate = 10.0,
+                                             .enableVariableUpdateRate = false,
+                                     }},
+                                     true);
+    ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
+
+    VehiclePropValue value0 = {
+            .prop = 0,
+            .areaId = 0,
+            .value = {.int32Values = {0}},
+            .timestamp = 1,
+    };
+    auto clients = getManager()->getSubscribedClients({value0});
+
+    ASSERT_THAT(clients[client1], UnorderedElementsAre(value0));
+
+    // A new event with the same value arrived. This must update timestamp to 3.
+    VehiclePropValue value1 = {
+            .prop = 0,
+            .areaId = 0,
+            .value = {.int32Values = {0}},
+            .timestamp = 3,
+    };
+    clients = getManager()->getSubscribedClients({value1});
+
+    ASSERT_TRUE(clients.find(client1) == clients.end())
+            << "Must filter out duplicate property events if VUR is enabled";
+
+    // The latest timestamp is 3, so even though the value is not the same, this is outdated and
+    // must be ignored.
+    VehiclePropValue value2 = {
+            .prop = 0,
+            .areaId = 0,
+            .value = {.int32Values = {1}},
+            .timestamp = 2,
+    };
+    clients = getManager()->getSubscribedClients({value1});
+
+    ASSERT_TRUE(clients.find(client1) == clients.end())
+            << "Must filter out outdated property events if VUR is enabled";
+}
+
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/vehicle/aidl_property/aidl_api/android.hardware.automotive.vehicle.property/current/android/hardware/automotive/vehicle/VehicleProperty.aidl b/automotive/vehicle/aidl_property/aidl_api/android.hardware.automotive.vehicle.property/current/android/hardware/automotive/vehicle/VehicleProperty.aidl
index ba75e7b..231186f 100644
--- a/automotive/vehicle/aidl_property/aidl_api/android.hardware.automotive.vehicle.property/current/android/hardware/automotive/vehicle/VehicleProperty.aidl
+++ b/automotive/vehicle/aidl_property/aidl_api/android.hardware.automotive.vehicle.property/current/android/hardware/automotive/vehicle/VehicleProperty.aidl
@@ -245,6 +245,7 @@
   SUPPORTED_PROPERTY_IDS = (((0x0F48 + 0x10000000) + 0x01000000) + 0x00410000) /* 289476424 */,
   SHUTDOWN_REQUEST = (((0x0F49 + android.hardware.automotive.vehicle.VehiclePropertyGroup.SYSTEM) + android.hardware.automotive.vehicle.VehicleArea.GLOBAL) + android.hardware.automotive.vehicle.VehiclePropertyType.INT32) /* 289410889 */,
   VEHICLE_IN_USE = (((0x0F4A + android.hardware.automotive.vehicle.VehiclePropertyGroup.SYSTEM) + android.hardware.automotive.vehicle.VehicleArea.GLOBAL) + android.hardware.automotive.vehicle.VehiclePropertyType.BOOLEAN) /* 287313738 */,
+  CLUSTER_HEARTBEAT = (((0x0F4B + android.hardware.automotive.vehicle.VehiclePropertyGroup.SYSTEM) + android.hardware.automotive.vehicle.VehicleArea.GLOBAL) + android.hardware.automotive.vehicle.VehiclePropertyType.MIXED) /* 299896651 */,
   AUTOMATIC_EMERGENCY_BRAKING_ENABLED = (((0x1000 + android.hardware.automotive.vehicle.VehiclePropertyGroup.SYSTEM) + android.hardware.automotive.vehicle.VehicleArea.GLOBAL) + android.hardware.automotive.vehicle.VehiclePropertyType.BOOLEAN) /* 287313920 */,
   AUTOMATIC_EMERGENCY_BRAKING_STATE = (((0x1001 + android.hardware.automotive.vehicle.VehiclePropertyGroup.SYSTEM) + android.hardware.automotive.vehicle.VehicleArea.GLOBAL) + android.hardware.automotive.vehicle.VehiclePropertyType.INT32) /* 289411073 */,
   FORWARD_COLLISION_WARNING_ENABLED = (((0x1002 + android.hardware.automotive.vehicle.VehiclePropertyGroup.SYSTEM) + android.hardware.automotive.vehicle.VehicleArea.GLOBAL) + android.hardware.automotive.vehicle.VehiclePropertyType.BOOLEAN) /* 287313922 */,
diff --git a/automotive/vehicle/aidl_property/aidl_api/android.hardware.automotive.vehicle.property/current/android/hardware/automotive/vehicle/VehiclePropertyGroup.aidl b/automotive/vehicle/aidl_property/aidl_api/android.hardware.automotive.vehicle.property/current/android/hardware/automotive/vehicle/VehiclePropertyGroup.aidl
index 714d514..b4f6850 100644
--- a/automotive/vehicle/aidl_property/aidl_api/android.hardware.automotive.vehicle.property/current/android/hardware/automotive/vehicle/VehiclePropertyGroup.aidl
+++ b/automotive/vehicle/aidl_property/aidl_api/android.hardware.automotive.vehicle.property/current/android/hardware/automotive/vehicle/VehiclePropertyGroup.aidl
@@ -36,5 +36,6 @@
 enum VehiclePropertyGroup {
   SYSTEM = 0x10000000,
   VENDOR = 0x20000000,
+  BACKPORTED = 0x30000000,
   MASK = 0xf0000000,
 }
diff --git a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl
index 7d88810..af2e903 100644
--- a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl
+++ b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl
@@ -4487,6 +4487,20 @@
     VEHICLE_IN_USE =
             0x0F4A + VehiclePropertyGroup.SYSTEM + VehicleArea.GLOBAL + VehiclePropertyType.BOOLEAN,
 
+    /**
+     * Sends the heartbeat signal to ClusterOS.
+     *
+     * int64[0]: epochTimeNs
+     * int64[1]: the visibility of ClusterUI, 0 - invisible, 1 - visible
+     * bytes: the app specific metadata, this can be empty when ClusterHomeService use the heartbeat
+     *     to deliver the change of the visibility.
+     *
+     * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+     * @access VehiclePropertyAccess.WRITE
+     */
+    CLUSTER_HEARTBEAT =
+            0x0F4B + VehiclePropertyGroup.SYSTEM + VehicleArea.GLOBAL + VehiclePropertyType.MIXED,
+
     /***********************************************************************************************
      * Start of ADAS Properties
      *
@@ -4737,11 +4751,11 @@
      *
      * Commands to activate and suspend LCA.
      *
-     * When the command ACTIVATE from LaneCenteringAssistCommmand is sent,
+     * When the command ACTIVATE from LaneCenteringAssistCommand is sent,
      * LANE_CENTERING_ASSIST_STATE must be set to LaneCenteringAssistState#ACTIVATION_REQUESTED.
      * When the ACTIVATE command succeeds, LANE_CENTERING_ASSIST_STATE must be set to
      * LaneCenteringAssistState#ACTIVATED. When the command DEACTIVATE from
-     * LaneCenteringAssistCommmand succeeds, LANE_CENTERING_ASSIST_STATE must be set to
+     * LaneCenteringAssistCommand succeeds, LANE_CENTERING_ASSIST_STATE must be set to
      * LaneCenteringAssistState#ENABLED.
      *
      * For the global area ID (0), the VehicleAreaConfig#supportedEnumValues must be defined unless
@@ -4757,7 +4771,7 @@
      *
      * @change_mode VehiclePropertyChangeMode.ON_CHANGE
      * @access VehiclePropertyAccess.WRITE
-     * @data_enum LaneCenteringAssistCommmand
+     * @data_enum LaneCenteringAssistCommand
      */
     LANE_CENTERING_ASSIST_COMMAND =
             0x100B + VehiclePropertyGroup.SYSTEM + VehicleArea.GLOBAL + VehiclePropertyType.INT32,
diff --git a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehiclePropertyGroup.aidl b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehiclePropertyGroup.aidl
index a2cbdec..a417388 100644
--- a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehiclePropertyGroup.aidl
+++ b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehiclePropertyGroup.aidl
@@ -29,5 +29,34 @@
      */
     VENDOR = 0x20000000,
 
+    /**
+     * Group reserved for backporting system properties introduced in a newer Android
+     * release to an older Android release.
+     *
+     * It is recommended to map the system property ID to a backported property ID by replacing the
+     * VehiclePropertyGroup, e.g. backported PERF_VEHICLE_SPEED(0x11600207) would be 0x31600207.
+     *
+     * When updated to a newer Android release where the property is defined as system properties,
+     * the backported properties must be migrated to system properties.
+     *
+     * In Android system, the backported property is treated the same as a vendor defined property
+     * with the same vendor permission model, a.k.a. Default required permission is
+     * `android.car.Car.PERMISSION_VENDOR_EXTENSION`, or customized by
+     * `SUPPORT_CUSTOMIZE_VENDOR_PERMISSION` VHAL property.
+     *
+     * Only applications with vendor permissions may access these backported properties.
+     *
+     * Vendors must also make sure this property's behavior is consistent with what is expected for
+     * the backported system property, e.g. the access mode, the change mode and the config array
+     * must be correct.
+     *
+     * When vendors define custom properties, they must use {@code VENDOR} flag, instead of
+     * {@code BACKPORTED}
+     */
+    BACKPORTED = 0x30000000,
+
+    /**
+     * The bit mask for {@code VehiclePropertyGroup}. This is not a group by itself.
+     */
     MASK = 0xf0000000,
 }
diff --git a/automotive/vehicle/proto/Android.bp b/automotive/vehicle/proto/Android.bp
index 7b98540..e7dabcf 100644
--- a/automotive/vehicle/proto/Android.bp
+++ b/automotive/vehicle/proto/Android.bp
@@ -27,7 +27,7 @@
     visibility: [
         "//hardware/interfaces/automotive/vehicle:__subpackages__",
         "//device/generic/car/emulator:__subpackages__",
-        "//device/google/sdv/sdv_ivi_cf:__subpackages__",
+        "//system/software_defined_vehicle/core_services:__subpackages__",
     ],
     vendor: true,
     host_supported: true,
diff --git a/automotive/vehicle/tools/generate_annotation_enums.py b/automotive/vehicle/tools/generate_annotation_enums.py
index fddc55b..05fc99a 100755
--- a/automotive/vehicle/tools/generate_annotation_enums.py
+++ b/automotive/vehicle/tools/generate_annotation_enums.py
@@ -18,7 +18,7 @@
 
    Need ANDROID_BUILD_TOP environmental variable to be set. This script will update
    ChangeModeForVehicleProperty.h and AccessForVehicleProperty.h under generated_lib/cpp and
-   ChangeModeForVehicleProperty.java and AccessForVehicleProperty.java under generated_lib/java.
+   ChangeModeForVehicleProperty.java, AccessForVehicleProperty.java, EnumForVehicleProperty.java under generated_lib/java.
 
    Usage:
    $ python generate_annotation_enums.py
@@ -40,6 +40,8 @@
     'ChangeModeForVehicleProperty.java')
 ACCESS_JAVA_FILE_PATH = ('hardware/interfaces/automotive/vehicle/aidl/generated_lib/java/' +
     'AccessForVehicleProperty.java')
+ENUM_JAVA_FILE_PATH = ('hardware/interfaces/automotive/vehicle/aidl/generated_lib/java/' +
+                         'EnumForVehicleProperty.java')
 SCRIPT_PATH = 'hardware/interfaces/automotive/vehicle/tools/generate_annotation_enums.py'
 
 TAB = '    '
@@ -54,7 +56,7 @@
 RE_VALUE = re.compile('\s*(\w+)\s*=(.*)')
 
 LICENSE = """/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -167,6 +169,22 @@
 }
 """
 
+ENUM_JAVA_HEADER = """package android.hardware.automotive.vehicle;
+
+import java.util.List;
+import java.util.Map;
+
+public final class EnumForVehicleProperty {
+
+    public static final Map<Integer, List<Class<?>>> values = Map.ofEntries(
+"""
+
+ENUM_JAVA_FOOTER = """
+    );
+
+}
+"""
+
 
 class PropertyConfig:
     """Represents one VHAL property definition in VehicleProperty.aidl."""
@@ -272,6 +290,11 @@
                     annotation = "VehiclePropertyAccess::" + config.access_modes[0]
                 else:
                     annotation = "VehiclePropertyAccess." + config.access_modes[0]
+            elif field == 'enum_types':
+                if len(config.enum_types) < 1:
+                    continue;
+                if not cpp:
+                    annotation = "List.of(" + ', '.join([class_name + ".class" for class_name in config.enum_types]) + ")"
             else:
                 raise Exception('Unknown field: ' + field)
             if counter != 0:
@@ -348,7 +371,7 @@
     else:
         android_top = os.environ['ANDROID_BUILD_TOP']
     if not android_top:
-        print('ANDROID_BUILD_TOP is not in envorinmental variable, please run source and lunch ' +
+        print('ANDROID_BUILD_TOP is not in environmental variable, please run source and lunch ' +
             'at the android root')
 
     aidl_file = os.path.join(android_top, PROP_AIDL_FILE_PATH)
@@ -363,6 +386,7 @@
     access_cpp_file = os.path.join(android_top, ACCESS_CPP_FILE_PATH);
     change_mode_java_file = os.path.join(android_top, CHANGE_MODE_JAVA_FILE_PATH);
     access_java_file = os.path.join(android_top, ACCESS_JAVA_FILE_PATH);
+    enum_java_file = os.path.join(android_top, ENUM_JAVA_FILE_PATH);
     temp_files = []
 
     if not args.check_only:
@@ -370,6 +394,7 @@
         access_cpp_output = access_cpp_file
         change_mode_java_output = change_mode_java_file
         access_java_output = access_java_file
+        enum_java_output = enum_java_file
     else:
         change_mode_cpp_output = createTempFile()
         temp_files.append(change_mode_cpp_output)
@@ -379,6 +404,8 @@
         temp_files.append(change_mode_java_output)
         access_java_output = createTempFile()
         temp_files.append(access_java_output)
+        enum_java_output = createTempFile()
+        temp_files.append(enum_java_output)
 
     try:
         f.convert(change_mode_cpp_output, CHANGE_MODE_CPP_HEADER, CHANGE_MODE_CPP_FOOTER,
@@ -387,6 +414,7 @@
                 CHANGE_MODE_JAVA_FOOTER, False, 'change_mode')
         f.convert(access_cpp_output, ACCESS_CPP_HEADER, ACCESS_CPP_FOOTER, True, 'access_mode')
         f.convert(access_java_output, ACCESS_JAVA_HEADER, ACCESS_JAVA_FOOTER, False, 'access_mode')
+        f.convert(enum_java_output, ENUM_JAVA_HEADER, ENUM_JAVA_FOOTER, False, 'enum_types')
 
         if not args.check_only:
             return
@@ -394,7 +422,8 @@
         if ((not filecmp.cmp(change_mode_cpp_output, change_mode_cpp_file)) or
                 (not filecmp.cmp(change_mode_java_output, change_mode_java_file)) or
                 (not filecmp.cmp(access_cpp_output, access_cpp_file)) or
-                (not filecmp.cmp(access_java_output, access_java_file))):
+                (not filecmp.cmp(access_java_output, access_java_file)) or
+                (not filecmp.cmp(enum_java_output, enum_java_file))):
             print('The generated enum files for VehicleProperty.aidl requires update, ')
             print('Run \npython ' + android_top + '/' + SCRIPT_PATH)
             sys.exit(1)
diff --git a/automotive/vehicle/vhal_static_cpp_lib.mk b/automotive/vehicle/vhal_static_cpp_lib.mk
index bffa076..6b3d486 100644
--- a/automotive/vehicle/vhal_static_cpp_lib.mk
+++ b/automotive/vehicle/vhal_static_cpp_lib.mk
@@ -16,5 +16,5 @@
 # interface and VHAL properties.
 
 LOCAL_STATIC_LIBRARIES += \
-    android.hardware.automotive.vehicle-V2-ndk \
+    android.hardware.automotive.vehicle-V3-ndk \
     android.hardware.automotive.vehicle.property-V3-ndk
diff --git a/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp b/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
index 8bcad1e..3dca0ae 100644
--- a/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
+++ b/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
@@ -115,6 +115,9 @@
 };
 
 class VtsHalAutomotiveVehicleTargetTest : public testing::TestWithParam<ServiceDescriptor> {
+  protected:
+    bool checkIsSupported(int32_t propertyId);
+
   public:
     void verifyProperty(VehicleProperty propId, VehiclePropertyAccess access,
                         VehiclePropertyChangeMode changeMode, VehiclePropertyGroup group,
@@ -155,7 +158,7 @@
     }
 }
 
-// Test getAllPropConfig() returns at least 4 property configs.
+// Test getAllPropConfigs() returns at least 1 property configs.
 TEST_P(VtsHalAutomotiveVehicleTargetTest, getAllPropConfigs) {
     ALOGD("VtsHalAutomotiveVehicleTargetTest::getAllPropConfigs");
 
@@ -163,25 +166,29 @@
 
     ASSERT_TRUE(result.ok()) << "Failed to get all property configs, error: "
                              << result.error().message();
-    ASSERT_GE(result.value().size(), 4u) << StringPrintf(
-            "Expect to get at least 4 property configs, got %zu", result.value().size());
+    ASSERT_GE(result.value().size(), 1u) << StringPrintf(
+            "Expect to get at least 1 property config, got %zu", result.value().size());
 }
 
-// Test getPropConfigs() can query all properties listed in CDD.
-TEST_P(VtsHalAutomotiveVehicleTargetTest, getRequiredPropConfigs) {
+// Test getPropConfigs() can query properties returned by getAllPropConfigs.
+TEST_P(VtsHalAutomotiveVehicleTargetTest, getPropConfigsWithValidProps) {
     ALOGD("VtsHalAutomotiveVehicleTargetTest::getRequiredPropConfigs");
 
-    // Check the properties listed in CDD
-    std::vector<int32_t> properties = {
-            toInt(VehicleProperty::GEAR_SELECTION), toInt(VehicleProperty::NIGHT_MODE),
-            toInt(VehicleProperty::PARKING_BRAKE_ON), toInt(VehicleProperty::PERF_VEHICLE_SPEED)};
+    std::vector<int32_t> properties;
+    auto result = mVhalClient->getAllPropConfigs();
 
-    auto result = mVhalClient->getPropConfigs(properties);
+    ASSERT_TRUE(result.ok()) << "Failed to get all property configs, error: "
+                             << result.error().message();
+    for (const auto& cfgPtr : result.value()) {
+        properties.push_back(cfgPtr->getPropId());
+    }
+
+    result = mVhalClient->getPropConfigs(properties);
 
     ASSERT_TRUE(result.ok()) << "Failed to get required property config, error: "
                              << result.error().message();
-    ASSERT_EQ(result.value().size(), 4u)
-            << StringPrintf("Expect to get exactly 4 configs, got %zu", result.value().size());
+    ASSERT_EQ(result.value().size(), properties.size()) << StringPrintf(
+            "Expect to get exactly %zu configs, got %zu", properties.size(), result.value().size());
 }
 
 // Test getPropConfig() with an invalid propertyId returns an error code.
@@ -200,6 +207,9 @@
     ALOGD("VtsHalAutomotiveVehicleTargetTest::get");
 
     int32_t propId = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
+    if (!checkIsSupported(propId)) {
+        GTEST_SKIP() << "Property: " << propId << " is not supported, skip the test";
+    }
     auto result = mVhalClient->getValueSync(*mVhalClient->createHalPropValue(propId));
 
     ASSERT_TRUE(result.ok()) << StringPrintf("Failed to get value for property: %" PRId32
@@ -285,6 +295,10 @@
     ALOGD("VtsHalAutomotiveVehicleTargetTest::setNotWritableProp");
 
     int32_t propId = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
+    if (!checkIsSupported(propId)) {
+        GTEST_SKIP() << "Property: " << propId << " is not supported, skip the test";
+    }
+
     auto getValueResult = mVhalClient->getValueSync(*mVhalClient->createHalPropValue(propId));
     ASSERT_TRUE(getValueResult.ok())
             << StringPrintf("Failed to get value for property: %" PRId32 ", error: %s", propId,
@@ -325,6 +339,9 @@
     ALOGD("VtsHalAutomotiveVehicleTargetTest::subscribeAndUnsubscribe");
 
     int32_t propId = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
+    if (!checkIsSupported(propId)) {
+        GTEST_SKIP() << "Property: " << propId << " is not supported, skip the test";
+    }
 
     auto propConfigsResult = mVhalClient->getPropConfigs({propId});
 
@@ -414,6 +431,9 @@
     }
 
     int32_t propId = toInt(VehicleProperty::PARKING_BRAKE_ON);
+    if (!checkIsSupported(propId)) {
+        GTEST_SKIP() << "Property: " << propId << " is not supported, skip the test";
+    }
     auto prop = mVhalClient->createHalPropValue(propId);
 
     auto result = mVhalClient->getValueSync(*prop);
@@ -865,6 +885,17 @@
                    VehicleArea::GLOBAL, VehiclePropertyType::INT32);
 }
 
+TEST_P(VtsHalAutomotiveVehicleTargetTest, verifyClusterHeartbeatConfig) {
+    verifyProperty(VehicleProperty::CLUSTER_HEARTBEAT, VehiclePropertyAccess::WRITE,
+                   VehiclePropertyChangeMode::ON_CHANGE, VehiclePropertyGroup::SYSTEM,
+                   VehicleArea::GLOBAL, VehiclePropertyType::MIXED);
+}
+
+bool VtsHalAutomotiveVehicleTargetTest::checkIsSupported(int32_t propertyId) {
+    auto result = mVhalClient->getPropConfigs({propertyId});
+    return result.ok();
+}
+
 std::vector<ServiceDescriptor> getDescriptors() {
     std::vector<ServiceDescriptor> descriptors;
     for (std::string name : getAidlHalInstanceNames(IVehicle::descriptor)) {
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/FoldState.aidl
similarity index 87%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
copy to biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/FoldState.aidl
index b9df1f5..06baf00 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/FoldState.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,12 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
+package android.hardware.biometrics.common;
 /* @hide */
-@Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+@Backing(type="int") @VintfStability
+enum FoldState {
+  UNKNOWN,
+  HALF_OPENED,
+  FULLY_OPENED,
+  FULLY_CLOSED,
 }
diff --git a/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/OperationContext.aidl b/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/OperationContext.aidl
index 378017e..42c305a 100644
--- a/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/OperationContext.aidl
+++ b/biometrics/common/aidl/aidl_api/android.hardware.biometrics.common/current/android/hardware/biometrics/common/OperationContext.aidl
@@ -45,4 +45,5 @@
   android.hardware.biometrics.common.WakeReason wakeReason = android.hardware.biometrics.common.WakeReason.UNKNOWN;
   android.hardware.biometrics.common.DisplayState displayState = android.hardware.biometrics.common.DisplayState.UNKNOWN;
   @nullable android.hardware.biometrics.common.AuthenticateReason authenticateReason;
+  android.hardware.biometrics.common.FoldState foldState = android.hardware.biometrics.common.FoldState.UNKNOWN;
 }
diff --git a/biometrics/common/aidl/android/hardware/biometrics/common/FoldState.aidl b/biometrics/common/aidl/android/hardware/biometrics/common/FoldState.aidl
new file mode 100644
index 0000000..03e606a
--- /dev/null
+++ b/biometrics/common/aidl/android/hardware/biometrics/common/FoldState.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics.common;
+
+/**
+ * Fold/Unfold state during an operation.
+ *
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+enum FoldState {
+    /** The fold state is unknown. */
+    UNKNOWN,
+
+    /** The fold state is half opened. */
+    HALF_OPENED,
+
+    /** The fold state is fully opened. */
+    FULLY_OPENED,
+
+    /** The fold state is fully closed. */
+    FULLY_CLOSED,
+}
diff --git a/biometrics/common/aidl/android/hardware/biometrics/common/OperationContext.aidl b/biometrics/common/aidl/android/hardware/biometrics/common/OperationContext.aidl
index f4191d7..584057d 100644
--- a/biometrics/common/aidl/android/hardware/biometrics/common/OperationContext.aidl
+++ b/biometrics/common/aidl/android/hardware/biometrics/common/OperationContext.aidl
@@ -18,6 +18,7 @@
 
 import android.hardware.biometrics.common.AuthenticateReason;
 import android.hardware.biometrics.common.DisplayState;
+import android.hardware.biometrics.common.FoldState;
 import android.hardware.biometrics.common.OperationReason;
 import android.hardware.biometrics.common.WakeReason;
 
@@ -71,4 +72,7 @@
      * framework may choose to omit the reason at any time based on the device's policy.
      */
     @nullable AuthenticateReason authenticateReason;
+
+    /** The current fold/unfold state. */
+    FoldState foldState = FoldState.UNKNOWN;
 }
diff --git a/biometrics/common/util/include/util/Util.h b/biometrics/common/util/include/util/Util.h
index da19dc6..efd66bc 100644
--- a/biometrics/common/util/include/util/Util.h
+++ b/biometrics/common/util/include/util/Util.h
@@ -23,6 +23,9 @@
 #include <thread>
 #include <vector>
 
+#include <android-base/parseint.h>
+using ::android::base::ParseInt;
+
 namespace aidl::android::hardware::biometrics {
 
 #define SLEEP_MS(x) \
@@ -64,6 +67,87 @@
                 std::sregex_token_iterator());
         return parts;
     }
+
+    // Returns a vector of integers for the string separated by comma,
+    // Empty vector is returned if there is any parsing error
+    static std::vector<int32_t> parseIntSequence(const std::string& str,
+                                                 const std::string& sep = ",") {
+        std::vector<std::string> seqs = Util::split(str, sep);
+        std::vector<int32_t> res;
+
+        for (const auto& seq : seqs) {
+            int32_t val;
+            if (ParseInt(seq, &val)) {
+                res.push_back(val);
+            } else {
+                LOG(WARNING) << "Invalid int sequence:" + str + " seq:" + seq;
+                res.clear();
+                break;
+            }
+        }
+
+        return res;
+    }
+
+    // Parses a single enrollment stage string in the format of
+    //     enroll_stage_spec: <duration>[-acquiredInfos]
+    //                                      duration: integerInMs
+    //                                      acquiredInfos: [info1,info2,...]
+    //
+    // Returns false if there is parsing error
+    //
+    static bool parseEnrollmentCaptureSingle(const std::string& str,
+                                             std::vector<std::vector<int32_t>>& res) {
+        std::vector<int32_t> defaultAcquiredInfo = {1};
+        bool aborted = true;
+
+        do {
+            std::smatch sms;
+            // Parses strings like "1000-[5,1]" or "500"
+            std::regex ex("((\\d+)(-\\[([\\d|,]+)\\])?)");
+            if (!regex_match(str.cbegin(), str.cend(), sms, ex)) break;
+            int32_t duration;
+            if (!ParseInt(sms.str(2), &duration)) break;
+            res.push_back({duration});
+            if (!sms.str(4).empty()) {
+                auto acqv = parseIntSequence(sms.str(4));
+                if (acqv.empty()) break;
+                res.push_back(acqv);
+            } else
+                res.push_back(defaultAcquiredInfo);
+            aborted = false;
+        } while (0);
+
+        return !aborted;
+    }
+
+    // Parses enrollment string consisting of one or more stages in the formst of
+    //  <enroll_stage_spec>[,enroll_stage_spec,...]
+    // Empty vector is returned in case of parsing error
+    static std::vector<std::vector<int32_t>> parseEnrollmentCapture(const std::string& str) {
+        std::vector<std::vector<int32_t>> res;
+
+        std::string s(str);
+        s.erase(std::remove_if(s.begin(), s.end(), ::isspace), s.end());
+        bool aborted = false;
+        std::smatch sms;
+        // Parses strings like "1000-[5,1],500,800-[6,5,1]"
+        //                               -------------- ----- ---------------
+        //  into parts:                       A       B       C
+        while (regex_search(s, sms, std::regex("^(,)?(\\d+(-\\[[\\d|,]+\\])?)"))) {
+            if (!parseEnrollmentCaptureSingle(sms.str(2), res)) {
+                aborted = true;
+                break;
+            }
+            s = sms.suffix();
+        }
+        if (aborted || s.length() != 0) {
+            res.clear();
+            LOG(ERROR) << "Failed to parse enrollment captures:" + str;
+        }
+
+        return res;
+    }
 };
 
 }  // namespace aidl::android::hardware::biometrics
diff --git a/biometrics/face/aidl/Android.bp b/biometrics/face/aidl/Android.bp
index 79df9c6..6c8cd78 100644
--- a/biometrics/face/aidl/Android.bp
+++ b/biometrics/face/aidl/Android.bp
@@ -18,6 +18,9 @@
         "android.hardware.common-V2",
         "android.hardware.keymaster-V4",
     ],
+    include_dirs: [
+        "frameworks/native/aidl/gui",
+    ],
     stability: "vintf",
     backend: {
         java: {
@@ -26,6 +29,11 @@
         cpp: {
             enabled: false,
         },
+        ndk: {
+            additional_shared_libraries: [
+                "libnativewindow",
+            ],
+        },
     },
     versions_with_info: [
         {
@@ -54,6 +62,6 @@
         },
 
     ],
-    frozen: true,
+    frozen: false,
 
 }
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/AcquiredInfo.aidl b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/AcquiredInfo.aidl
index eaa43f3..5312ca1 100644
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/AcquiredInfo.aidl
+++ b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/AcquiredInfo.aidl
@@ -34,31 +34,31 @@
 package android.hardware.biometrics.face;
 @Backing(type="byte") @VintfStability
 enum AcquiredInfo {
-  UNKNOWN = 0,
-  GOOD = 1,
-  INSUFFICIENT = 2,
-  TOO_BRIGHT = 3,
-  TOO_DARK = 4,
-  TOO_CLOSE = 5,
-  TOO_FAR = 6,
-  FACE_TOO_HIGH = 7,
-  FACE_TOO_LOW = 8,
-  FACE_TOO_RIGHT = 9,
-  FACE_TOO_LEFT = 10,
-  POOR_GAZE = 11,
-  NOT_DETECTED = 12,
-  TOO_MUCH_MOTION = 13,
-  RECALIBRATE = 14,
-  TOO_DIFFERENT = 15,
-  TOO_SIMILAR = 16,
-  PAN_TOO_EXTREME = 17,
-  TILT_TOO_EXTREME = 18,
-  ROLL_TOO_EXTREME = 19,
-  FACE_OBSCURED = 20,
-  START = 21,
-  SENSOR_DIRTY = 22,
-  VENDOR = 23,
-  FIRST_FRAME_RECEIVED = 24,
-  DARK_GLASSES_DETECTED = 25,
-  MOUTH_COVERING_DETECTED = 26,
+  UNKNOWN,
+  GOOD,
+  INSUFFICIENT,
+  TOO_BRIGHT,
+  TOO_DARK,
+  TOO_CLOSE,
+  TOO_FAR,
+  FACE_TOO_HIGH,
+  FACE_TOO_LOW,
+  FACE_TOO_RIGHT,
+  FACE_TOO_LEFT,
+  POOR_GAZE,
+  NOT_DETECTED,
+  TOO_MUCH_MOTION,
+  RECALIBRATE,
+  TOO_DIFFERENT,
+  TOO_SIMILAR,
+  PAN_TOO_EXTREME,
+  TILT_TOO_EXTREME,
+  ROLL_TOO_EXTREME,
+  FACE_OBSCURED,
+  START,
+  SENSOR_DIRTY,
+  VENDOR,
+  FIRST_FRAME_RECEIVED,
+  DARK_GLASSES_DETECTED,
+  MOUTH_COVERING_DETECTED,
 }
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/EnrollmentStage.aidl b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/EnrollmentStage.aidl
index ce5679a..a203dbe 100644
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/EnrollmentStage.aidl
+++ b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/EnrollmentStage.aidl
@@ -34,11 +34,11 @@
 package android.hardware.biometrics.face;
 @Backing(type="byte") @VintfStability
 enum EnrollmentStage {
-  UNKNOWN = 0,
-  FIRST_FRAME_RECEIVED = 1,
-  WAITING_FOR_CENTERING = 2,
-  HOLD_STILL_IN_CENTER = 3,
-  ENROLLING_MOVEMENT_1 = 4,
-  ENROLLING_MOVEMENT_2 = 5,
-  ENROLLMENT_FINISHED = 6,
+  UNKNOWN,
+  FIRST_FRAME_RECEIVED,
+  WAITING_FOR_CENTERING,
+  HOLD_STILL_IN_CENTER,
+  ENROLLING_MOVEMENT_1,
+  ENROLLING_MOVEMENT_2,
+  ENROLLMENT_FINISHED,
 }
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/EnrollmentType.aidl b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/EnrollmentType.aidl
index 8e99ad6..da1e8a3 100644
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/EnrollmentType.aidl
+++ b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/EnrollmentType.aidl
@@ -34,6 +34,6 @@
 package android.hardware.biometrics.face;
 @Backing(type="byte") @VintfStability
 enum EnrollmentType {
-  DEFAULT = 0,
-  ACCESSIBILITY = 1,
+  DEFAULT,
+  ACCESSIBILITY,
 }
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/Error.aidl b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/Error.aidl
index 1a21661..28eb420 100644
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/Error.aidl
+++ b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/Error.aidl
@@ -34,13 +34,13 @@
 package android.hardware.biometrics.face;
 @Backing(type="byte") @VintfStability
 enum Error {
-  UNKNOWN = 0,
-  HW_UNAVAILABLE = 1,
-  UNABLE_TO_PROCESS = 2,
-  TIMEOUT = 3,
-  NO_SPACE = 4,
-  CANCELED = 5,
-  UNABLE_TO_REMOVE = 6,
-  VENDOR = 7,
-  REENROLL_REQUIRED = 8,
+  UNKNOWN,
+  HW_UNAVAILABLE,
+  UNABLE_TO_PROCESS,
+  TIMEOUT,
+  NO_SPACE,
+  CANCELED,
+  UNABLE_TO_REMOVE,
+  VENDOR,
+  REENROLL_REQUIRED,
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/SatelliteCapabilities.aidl b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/FaceEnrollOptions.aidl
similarity index 67%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/SatelliteCapabilities.aidl
copy to biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/FaceEnrollOptions.aidl
index e0af71a..23fa147 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/SatelliteCapabilities.aidl
+++ b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/FaceEnrollOptions.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,13 +31,16 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
-/* @hide */
-@JavaDerive(toString=true) @VintfStability
-parcelable SatelliteCapabilities {
-  android.hardware.radio.satellite.NTRadioTechnology[] supportedRadioTechnologies;
-  boolean isAlwaysOn;
-  boolean needsPointingToSatellite;
-  android.hardware.radio.satellite.SatelliteFeature[] supportedFeatures;
-  boolean needsSeparateSimProfile;
+package android.hardware.biometrics.face;
+@VintfStability
+parcelable FaceEnrollOptions {
+  android.hardware.keymaster.HardwareAuthToken hardwareAuthToken;
+  android.hardware.biometrics.face.EnrollmentType enrollmentType;
+  android.hardware.biometrics.face.Feature[] features;
+  /**
+   * @deprecated use {@link surfacePreview} instead {@link NativeHandle} a handle used to render content from the face HAL. Note that only one of [{@link surfacePreview}, {@link nativeHandlePreview}] should be set at one time.
+   */
+  @nullable android.hardware.common.NativeHandle nativeHandlePreview;
+  @nullable android.view.Surface surfacePreview;
+  @nullable android.hardware.biometrics.common.OperationContext context;
 }
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/FaceSensorType.aidl b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/FaceSensorType.aidl
index a215b99..bf1677c 100644
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/FaceSensorType.aidl
+++ b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/FaceSensorType.aidl
@@ -34,7 +34,7 @@
 package android.hardware.biometrics.face;
 @Backing(type="byte") @VintfStability
 enum FaceSensorType {
-  UNKNOWN = 0,
-  RGB = 1,
-  IR = 2,
+  UNKNOWN,
+  RGB,
+  IR,
 }
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/Feature.aidl b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/Feature.aidl
index 1875b97..924e6af 100644
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/Feature.aidl
+++ b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/Feature.aidl
@@ -34,7 +34,7 @@
 package android.hardware.biometrics.face;
 @Backing(type="byte") @VintfStability
 enum Feature {
-  REQUIRE_ATTENTION = 0,
-  REQUIRE_DIVERSE_POSES = 1,
-  DEBUG = 2,
+  REQUIRE_ATTENTION,
+  REQUIRE_DIVERSE_POSES,
+  DEBUG,
 }
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISession.aidl b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISession.aidl
index 3665534..4d99f5a 100644
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISession.aidl
+++ b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISession.aidl
@@ -37,6 +37,9 @@
   void generateChallenge();
   void revokeChallenge(in long challenge);
   android.hardware.biometrics.face.EnrollmentStageConfig[] getEnrollmentConfig(in android.hardware.biometrics.face.EnrollmentType enrollmentType);
+  /**
+   * @deprecated use {@link enrollWithOptions} instead.
+   */
   android.hardware.biometrics.common.ICancellationSignal enroll(in android.hardware.keymaster.HardwareAuthToken hat, in android.hardware.biometrics.face.EnrollmentType type, in android.hardware.biometrics.face.Feature[] features, in @nullable android.hardware.common.NativeHandle previewSurface);
   android.hardware.biometrics.common.ICancellationSignal authenticate(in long operationId);
   android.hardware.biometrics.common.ICancellationSignal detectInteraction();
@@ -49,7 +52,11 @@
   void resetLockout(in android.hardware.keymaster.HardwareAuthToken hat);
   void close();
   android.hardware.biometrics.common.ICancellationSignal authenticateWithContext(in long operationId, in android.hardware.biometrics.common.OperationContext context);
+  /**
+   * @deprecated use {@link enrollWithOptions} instead.
+   */
   android.hardware.biometrics.common.ICancellationSignal enrollWithContext(in android.hardware.keymaster.HardwareAuthToken hat, in android.hardware.biometrics.face.EnrollmentType type, in android.hardware.biometrics.face.Feature[] features, in @nullable android.hardware.common.NativeHandle previewSurface, in android.hardware.biometrics.common.OperationContext context);
   android.hardware.biometrics.common.ICancellationSignal detectInteractionWithContext(in android.hardware.biometrics.common.OperationContext context);
   void onContextChanged(in android.hardware.biometrics.common.OperationContext context);
+  android.hardware.biometrics.common.ICancellationSignal enrollWithOptions(in android.hardware.biometrics.face.FaceEnrollOptions options);
 }
diff --git a/biometrics/face/aidl/android/hardware/biometrics/face/FaceEnrollOptions.aidl b/biometrics/face/aidl/android/hardware/biometrics/face/FaceEnrollOptions.aidl
new file mode 100644
index 0000000..75e3978
--- /dev/null
+++ b/biometrics/face/aidl/android/hardware/biometrics/face/FaceEnrollOptions.aidl
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics.face;
+
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.face.EnrollmentStageConfig;
+import android.hardware.biometrics.face.EnrollmentType;
+import android.hardware.biometrics.face.Feature;
+import android.hardware.common.NativeHandle;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.view.Surface;
+
+/**
+ * Enroll options used to pass information to the HAL when requesting an enroll operation.
+ */
+@VintfStability
+parcelable FaceEnrollOptions {
+    /**
+     * See {@link HardwareAuthToken}.
+     */
+    HardwareAuthToken hardwareAuthToken;
+
+    /**
+     * See {@link EnrollmentType}
+     */
+    EnrollmentType enrollmentType;
+
+    /**
+     * See {@link Feature}
+     */
+    Feature[] features;
+
+    /**
+     * @deprecated use {@link surfacePreview} instead
+     *
+     * {@link NativeHandle} a handle used to render content from the face HAL.
+     *
+     * Note that only one of [{@link surfacePreview}, {@link nativeHandlePreview}]
+     * should be set at one time.
+     */
+    @nullable NativeHandle nativeHandlePreview;
+
+    /**
+     * {@link Surface} a surface used to render content from the face HAL.
+     *
+     * Note that only one of [{@link surfacePreview}, {@link nativeHandlePreview}]
+     * should be set at one time.
+     */
+    @nullable Surface surfacePreview;
+
+    /**
+     * See {@link OperationContext}
+     */
+    @nullable OperationContext context;
+}
diff --git a/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl b/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl
index 2be76cb..825af0c 100644
--- a/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl
+++ b/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl
@@ -20,6 +20,7 @@
 import android.hardware.biometrics.common.OperationContext;
 import android.hardware.biometrics.face.EnrollmentStageConfig;
 import android.hardware.biometrics.face.EnrollmentType;
+import android.hardware.biometrics.face.FaceEnrollOptions;
 import android.hardware.biometrics.face.Feature;
 import android.hardware.common.NativeHandle;
 import android.hardware.keymaster.HardwareAuthToken;
@@ -115,44 +116,7 @@
     EnrollmentStageConfig[] getEnrollmentConfig(in EnrollmentType enrollmentType);
 
     /**
-     * enroll:
-     *
-     * A request to add a face enrollment.
-     *
-     * At any point during enrollment, if a non-recoverable error occurs, the HAL must notify the
-     * framework via ISessionCallback#onError with the applicable enrollment-specific error.
-     *
-     * Before capturing face data, the HAL must first verify the authenticity and integrity of the
-     * provided HardwareAuthToken. In addition, it must check that the challenge within the provided
-     * HardwareAuthToken is valid. See ISession#generateChallenge. If any of the above checks fail,
-     * the framework must be notified using ISessionCallback#onError with Error::UNABLE_TO_PROCESS.
-     *
-     * During enrollment, the HAL may notify the framework via ISessionCallback#onAcquired with
-     * messages that may be used to guide the user. This callback can be invoked multiple times if
-     * necessary. Similarly, the framework may be notified of enrollment progress changes via
-     * ISessionCallback#onEnrollmentProgress. Once the framework is notified that there are 0
-     * "remaining" steps, the framework may cache the "enrollmentId". See
-     * ISessionCallback#onEnrollmentProgress for more info.
-     *
-     * When a face is successfully added and before the framework is notified of remaining=0, the
-     * HAL must update and associate this (sensorId, userId) pair with a new entropy-encoded random
-     * identifier. See ISession#getAuthenticatorId for more information.
-     *
-     * Callbacks that signify the end of this operation's lifecycle:
-     *   - ISessionCallback#onError
-     *   - ISessionCallback#onEnrollmentProgress(enrollmentId, remaining=0)
-     *
-     * Other applicable callbacks:
-     *   - ISessionCallback#onAcquired
-     *
-     * @param hat See above documentation.
-     * @param enrollmentType See the EnrollmentType enum.
-     * @param features See the Feature enum.
-     * @param previewSurface A surface provided by the framework if SensorProps#halControlsPreview
-     *                       is set to true. The HAL must send the preview frames to previewSurface
-     *                       if it's not null.
-     * @return ICancellationSignal An object that can be used by the framework to cancel this
-     * operation.
+     * @deprecated use {@link enrollWithOptions} instead.
      */
     ICancellationSignal enroll(in HardwareAuthToken hat, in EnrollmentType type,
             in Feature[] features, in @nullable NativeHandle previewSurface);
@@ -456,7 +420,9 @@
     /* See ISession#authenticateWithContext(long) */
     ICancellationSignal authenticateWithContext(in long operationId, in OperationContext context);
 
-    /* See ISession#enroll(HardwareAuthToken, EnrollmentType, Feature[], NativeHandle) */
+    /*
+     * @deprecated use {@link enrollWithOptions} instead.
+     */
     ICancellationSignal enrollWithContext(in HardwareAuthToken hat, in EnrollmentType type,
             in Feature[] features, in @nullable NativeHandle previewSurface,
             in OperationContext context);
@@ -469,4 +435,41 @@
      * running when the context changes.
      */
     void onContextChanged(in OperationContext context);
+
+    /**
+     * enrollWithOptions:
+     *
+     * A request to add a face enrollment.
+     *
+     * At any point during enrollment, if a non-recoverable error occurs, the HAL must notify the
+     * framework via ISessionCallback#onError with the applicable enrollment-specific error.
+     *
+     * Before capturing face data, the HAL must first verify the authenticity and integrity of the
+     * provided HardwareAuthToken. In addition, it must check that the challenge within the provided
+     * HardwareAuthToken is valid. See ISession#generateChallenge. If any of the above checks fail,
+     * the framework must be notified using ISessionCallback#onError with Error::UNABLE_TO_PROCESS.
+     *
+     * During enrollment, the HAL may notify the framework via ISessionCallback#onAcquired with
+     * messages that may be used to guide the user. This callback can be invoked multiple times if
+     * necessary. Similarly, the framework may be notified of enrollment progress changes via
+     * ISessionCallback#onEnrollmentProgress. Once the framework is notified that there are 0
+     * "remaining" steps, the framework may cache the "enrollmentId". See
+     * ISessionCallback#onEnrollmentProgress for more info.
+     *
+     * When a face is successfully added and before the framework is notified of remaining=0, the
+     * HAL must update and associate this (sensorId, userId) pair with a new entropy-encoded random
+     * identifier. See ISession#getAuthenticatorId for more information.
+     *
+     * Callbacks that signify the end of this operation's lifecycle:
+     *   - ISessionCallback#onError
+     *   - ISessionCallback#onEnrollmentProgress(enrollmentId, remaining=0)
+     *
+     * Other applicable callbacks:
+     *   - ISessionCallback#onAcquired
+     *
+     * @param FaceEnrollOptions See {@link FaceEnrollOptions} for more detail.
+     * @return ICancellationSignal An object that can be used by the framework to cancel this
+     * operation.
+     */
+    ICancellationSignal enrollWithOptions(in FaceEnrollOptions options);
 }
diff --git a/biometrics/face/aidl/default/Android.bp b/biometrics/face/aidl/default/Android.bp
index 7bc2198..ecd0934 100644
--- a/biometrics/face/aidl/default/Android.bp
+++ b/biometrics/face/aidl/default/Android.bp
@@ -23,13 +23,11 @@
     init_rc: [":face-example.rc"],
     vintf_fragments: [":face-example.xml"],
     vendor: true,
+
     shared_libs: [
-        "libbase",
         "libbinder_ndk",
-        "android.hardware.biometrics.face-V3-ndk",
-        "android.hardware.biometrics.common-V3-ndk",
-        "android.hardware.biometrics.common.thread",
-        "android.hardware.biometrics.common.util",
+        "liblog",
+        "libnativewindow",
     ],
     srcs: [
         "main.cpp",
@@ -37,7 +35,20 @@
         "FakeFaceEngine.cpp",
         "Session.cpp",
     ],
-    static_libs: ["libandroid.hardware.biometrics.face.VirtualProps"],
+    include_dirs: [
+        "frameworks/native/aidl/gui",
+    ],
+    stl: "c++_static",
+    static_libs: [
+        "android.hardware.biometrics.common-V3-ndk",
+        "android.hardware.biometrics.common.thread",
+        "android.hardware.biometrics.common.util",
+        "android.hardware.biometrics.face-V4-ndk",
+        "android.hardware.common-V2-ndk",
+        "android.hardware.keymaster-V4-ndk",
+        "libandroid.hardware.biometrics.face.VirtualProps",
+        "libbase",
+    ],
 }
 
 sysprop_library {
@@ -56,10 +67,14 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
+        "libnativewindow",
+    ],
+    include_dirs: [
+        "frameworks/native/aidl/gui",
     ],
     static_libs: [
         "libandroid.hardware.biometrics.face.VirtualProps",
-        "android.hardware.biometrics.face-V3-ndk",
+        "android.hardware.biometrics.face-V4-ndk",
         "android.hardware.biometrics.common-V3-ndk",
         "android.hardware.keymaster-V4-ndk",
         "android.hardware.biometrics.common.util",
diff --git a/biometrics/face/aidl/default/FakeFaceEngine.cpp b/biometrics/face/aidl/default/FakeFaceEngine.cpp
index 0f088f4..578231d 100644
--- a/biometrics/face/aidl/default/FakeFaceEngine.cpp
+++ b/biometrics/face/aidl/default/FakeFaceEngine.cpp
@@ -136,56 +136,127 @@
                                       const std::future<void>& cancel) {
     BEGIN_OP(FaceHalProperties::operation_authenticate_latency().value_or(0));
 
-    // Signal to the framework that we have begun authenticating.
-    AuthenticationFrame frame;
-    frame.data.acquiredInfo = AcquiredInfo::START;
-    frame.data.vendorCode = 0;
-    cb->onAuthenticationFrame(frame);
-
-    // Also signal that we have opened the camera.
-    frame = {};
-    frame.data.acquiredInfo = AcquiredInfo::FIRST_FRAME_RECEIVED;
-    frame.data.vendorCode = 0;
-    cb->onAuthenticationFrame(frame);
-
-    auto now = Util::getSystemNanoTime();
-    int64_t duration = FaceHalProperties::operation_authenticate_duration().value_or(0);
-    if (duration > 0) {
-        do {
-            SLEEP_MS(5);
-        } while (!Util::hasElapsed(now, duration));
-    }
-
-    if (FaceHalProperties::operation_authenticate_fails().value_or(false)) {
-        LOG(ERROR) << "Fail: operation_authenticate_fails";
-        cb->onError(Error::VENDOR, 0 /* vendorError */);
-        return;
-    }
-
-    if (FaceHalProperties::lockout().value_or(false)) {
-        LOG(ERROR) << "Fail: lockout";
-        cb->onLockoutPermanent();
-        cb->onError(Error::HW_UNAVAILABLE, 0 /* vendorError */);
-        return;
-    }
-
-    if (shouldCancel(cancel)) {
-        LOG(ERROR) << "Fail: cancel";
-        cb->onError(Error::CANCELED, 0 /* vendorCode */);
-        return;
-    }
-
     auto id = FaceHalProperties::enrollment_hit().value_or(0);
     auto enrolls = FaceHalProperties::enrollments();
     auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();
-    if (id < 0 || !isEnrolled) {
-        LOG(ERROR) << (isEnrolled ? "invalid enrollment hit" : "Fail: not enrolled");
-        cb->onAuthenticationFailed();
+
+    auto vec2str = [](std::vector<AcquiredInfo> va) {
+        std::stringstream ss;
+        bool isFirst = true;
+        for (auto ac : va) {
+            if (!isFirst) ss << ",";
+            ss << std::to_string((int8_t)ac);
+            isFirst = false;
+        }
+        return ss.str();
+    };
+
+    // default behavior mimic face sensor in U
+    int64_t defaultAuthDuration = 500;
+    std::string defaultAcquiredInfo =
+            vec2str({AcquiredInfo::START, AcquiredInfo::FIRST_FRAME_RECEIVED});
+    if (!isEnrolled) {
+        std::vector<AcquiredInfo> v;
+        for (int i = 0; i < 56; i++) v.push_back(AcquiredInfo::NOT_DETECTED);
+        defaultAcquiredInfo += "," + vec2str(v);
+        defaultAuthDuration = 2100;
+    } else {
+        defaultAcquiredInfo += "," + vec2str({AcquiredInfo::TOO_BRIGHT, AcquiredInfo::TOO_BRIGHT,
+                                              AcquiredInfo::TOO_BRIGHT, AcquiredInfo::TOO_BRIGHT,
+                                              AcquiredInfo::GOOD, AcquiredInfo::GOOD});
+    }
+
+    int64_t now = Util::getSystemNanoTime();
+    int64_t duration =
+            FaceHalProperties::operation_authenticate_duration().value_or(defaultAuthDuration);
+    auto acquired =
+            FaceHalProperties::operation_authenticate_acquired().value_or(defaultAcquiredInfo);
+    auto acquiredInfos = Util::parseIntSequence(acquired);
+    int N = acquiredInfos.size();
+
+    if (N == 0) {
+        LOG(ERROR) << "Fail to parse authentiate acquired info: " + acquired;
         cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
         return;
     }
 
-    cb->onAuthenticationSucceeded(id, {} /* hat */);
+    int i = 0;
+    do {
+        if (FaceHalProperties::lockout().value_or(false)) {
+            LOG(ERROR) << "Fail: lockout";
+            cb->onLockoutPermanent();
+            cb->onError(Error::HW_UNAVAILABLE, 0 /* vendorError */);
+            return;
+        }
+
+        if (FaceHalProperties::operation_authenticate_fails().value_or(false)) {
+            LOG(ERROR) << "Fail: operation_authenticate_fails";
+            cb->onAuthenticationFailed();
+            return;
+        }
+
+        auto err = FaceHalProperties::operation_authenticate_error().value_or(0);
+        if (err != 0) {
+            LOG(ERROR) << "Fail: operation_authenticate_error";
+            auto ec = convertError(err);
+            cb->onError(ec.first, ec.second);
+            return; /* simply terminating current operation for any user inserted error,
+                            revisit if tests need*/
+        }
+
+        if (shouldCancel(cancel)) {
+            LOG(ERROR) << "Fail: cancel";
+            cb->onError(Error::CANCELED, 0 /* vendorCode */);
+            return;
+        }
+
+        if (i < N) {
+            auto ac = convertAcquiredInfo(acquiredInfos[i]);
+            AuthenticationFrame frame;
+            frame.data.acquiredInfo = ac.first;
+            frame.data.vendorCode = ac.second;
+            cb->onAuthenticationFrame(frame);
+            LOG(INFO) << "AcquiredInfo:" << i << ": (" << (int)ac.first << "," << (int)ac.second
+                      << ")";
+            i++;
+        }
+
+        SLEEP_MS(duration / N);
+    } while (!Util::hasElapsed(now, duration));
+
+    if (id > 0 && isEnrolled) {
+        cb->onAuthenticationSucceeded(id, {} /* hat */);
+        return;
+    } else {
+        LOG(ERROR) << "Fail: face not enrolled";
+        cb->onAuthenticationFailed();
+        cb->onError(Error::TIMEOUT, 0 /* vendorError*/);
+        return;
+    }
+}
+
+std::pair<AcquiredInfo, int32_t> FakeFaceEngine::convertAcquiredInfo(int32_t code) {
+    std::pair<AcquiredInfo, int32_t> res;
+    if (code > FACE_ACQUIRED_VENDOR_BASE) {
+        res.first = AcquiredInfo::VENDOR;
+        res.second = code - FACE_ACQUIRED_VENDOR_BASE;
+    } else {
+        res.first = (AcquiredInfo)code;
+        res.second = 0;
+    }
+    return res;
+}
+
+std::pair<Error, int32_t> FakeFaceEngine::convertError(int32_t code) {
+    std::pair<Error, int32_t> res;
+    if (code > FACE_ERROR_VENDOR_BASE) {
+        res.first = Error::VENDOR;
+        res.second = code - FACE_ERROR_VENDOR_BASE;
+    } else {
+        res.first = (Error)code;
+        res.second = 0;
+    }
+    return res;
 }
 
 void FakeFaceEngine::detectInteractionImpl(ISessionCallback* cb, const std::future<void>& cancel) {
@@ -315,4 +386,4 @@
     cb->onLockoutCleared();
 }
 
-}  // namespace aidl::android::hardware::biometrics::face
\ No newline at end of file
+}  // namespace aidl::android::hardware::biometrics::face
diff --git a/biometrics/face/aidl/default/FakeFaceEngine.h b/biometrics/face/aidl/default/FakeFaceEngine.h
index eb99441..06dd396 100644
--- a/biometrics/face/aidl/default/FakeFaceEngine.h
+++ b/biometrics/face/aidl/default/FakeFaceEngine.h
@@ -62,6 +62,12 @@
     void resetLockoutImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& /*hat*/);
 
     std::mt19937 mRandom;
+
+  private:
+    static constexpr int32_t FACE_ACQUIRED_VENDOR_BASE = 1000;
+    static constexpr int32_t FACE_ERROR_VENDOR_BASE = 1000;
+    std::pair<AcquiredInfo, int32_t> convertAcquiredInfo(int32_t code);
+    std::pair<Error, int32_t> convertError(int32_t code);
 };
 
 }  // namespace aidl::android::hardware::biometrics::face
diff --git a/biometrics/face/aidl/default/README.md b/biometrics/face/aidl/default/README.md
index 77bfe57..516a7aa 100644
--- a/biometrics/face/aidl/default/README.md
+++ b/biometrics/face/aidl/default/README.md
@@ -18,6 +18,7 @@
 Face VHAL enabling is gated by the following two AND conditions:
 1. The Face VHAL feature flag (as part of Trunk-development strategy) must be tured until the flags life-cycle ends.
 2. The Face VHAL must be enabled via sysprop
+See adb commands below
 
 ##Getting Stared
 
@@ -47,8 +48,7 @@
 $ adb shell setprop vendor.face.virtual.operation_authenticate_duration 800
 $ adb shell setprop vendor.face.virtual.enrollment_hit 1
 ```
-Note: At the initial phase of the Face VHAL development, Face-on-camera simulation is not supported, hence
-the authentication immediately occurrs  as soon as the authentication request arriving at VHAL.
+Refer to face.sysprop for full supported features of authentication, such as error and acquiredInfo insertion
 
 
 ## Enrollment via Setup
diff --git a/biometrics/face/aidl/default/Session.cpp b/biometrics/face/aidl/default/Session.cpp
index 6235b83..6f3f2fc 100644
--- a/biometrics/face/aidl/default/Session.cpp
+++ b/biometrics/face/aidl/default/Session.cpp
@@ -175,4 +175,10 @@
     return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus Session::enrollWithOptions(const FaceEnrollOptions& options,
+                                              std::shared_ptr<common::ICancellationSignal>* out) {
+    return enroll(options.hardwareAuthToken, options.enrollmentType, options.features,
+                  options.nativeHandlePreview, out);
+}
+
 }  // namespace aidl::android::hardware::biometrics::face
diff --git a/biometrics/face/aidl/default/Session.h b/biometrics/face/aidl/default/Session.h
index 7ca6a1f..ce6e7f1 100644
--- a/biometrics/face/aidl/default/Session.h
+++ b/biometrics/face/aidl/default/Session.h
@@ -19,6 +19,7 @@
 #include <random>
 
 #include <aidl/android/hardware/biometrics/face/BnSession.h>
+#include <aidl/android/hardware/biometrics/face/FaceEnrollOptions.h>
 #include <aidl/android/hardware/biometrics/face/ISessionCallback.h>
 
 #include "FakeFaceEngine.h"
@@ -88,6 +89,10 @@
 
     ndk::ScopedAStatus onContextChanged(const common::OperationContext& context) override;
 
+    ndk::ScopedAStatus enrollWithOptions(
+            const FaceEnrollOptions& options,
+            std::shared_ptr<common::ICancellationSignal>* out) override;
+
   private:
     std::unique_ptr<FakeFaceEngine> mEngine;
     std::shared_ptr<ISessionCallback> mCb;
diff --git a/biometrics/face/aidl/default/apex/Android.bp b/biometrics/face/aidl/default/apex/Android.bp
index 9c031a3..86c4e12 100644
--- a/biometrics/face/aidl/default/apex/Android.bp
+++ b/biometrics/face/aidl/default/apex/Android.bp
@@ -16,26 +16,14 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-apex_key {
-    name: "com.android.hardware.biometrics.face.virtual.key",
-    public_key: "com.android.hardware.biometrics.face.virtual.avbpubkey",
-    private_key: "com.android.hardware.biometrics.face.virtual.pem",
-}
-
-android_app_certificate {
-    name: "com.android.hardware.biometrics.face.virtual.certificate",
-    certificate: "com.android.hardware.biometrics.face.virtual",
-}
-
 apex {
     name: "com.android.hardware.biometrics.face.virtual",
     manifest: "manifest.json",
     file_contexts: "file_contexts",
-    key: "com.android.hardware.biometrics.face.virtual.key",
-    certificate: ":com.android.hardware.biometrics.face.virtual.certificate",
+    key: "com.android.hardware.key",
+    certificate: ":com.android.hardware.certificate",
     updatable: false,
     vendor: true,
-    use_vndk_as_stable: true,
 
     binaries: [
         // hal
diff --git a/biometrics/face/aidl/default/apex/com.android.hardware.biometrics.face.virtual.avbpubkey b/biometrics/face/aidl/default/apex/com.android.hardware.biometrics.face.virtual.avbpubkey
deleted file mode 100644
index 9f358ff..0000000
--- a/biometrics/face/aidl/default/apex/com.android.hardware.biometrics.face.virtual.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/biometrics/face/aidl/default/apex/com.android.hardware.biometrics.face.virtual.pem b/biometrics/face/aidl/default/apex/com.android.hardware.biometrics.face.virtual.pem
deleted file mode 100644
index ad8f57d..0000000
--- a/biometrics/face/aidl/default/apex/com.android.hardware.biometrics.face.virtual.pem
+++ /dev/null
@@ -1,52 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQCoZ28KVXxB9kkX
-zPhi8pVSmiMU0xchU4PkoOvFzmr9t5b7rA1zmzOLjkNWMrxt1XCUEfOgT3N50z5T
-484dpEZBrwIH31NqSOr/T8mG5frZAkNg0WqfgV+AG81mCuA+nG4a08fmDL4nEzVg
-irQvWpXzRCNIn+LEx+gG4I2KG8EGWDiFnzXwSFJG4n60rfLQNHgsejv/06BmPCnS
-gp06LhqJTO2oJKCJVQxfO0TPNUd9FSVSXdWuHcHE2gwoHM/7C/10Bdx8XXCMbAQR
-a4d3moH5oRuhh1841PtIQ0ozw/vHapCTmdMbjmgbwpXZ49DfvJl2cnz5c5G/CUO1
-9CkLDdxffsHh48jLUzH1nRgcWW2CAnSIs8nGjScyOKB+gn831ke8agJGbVHs1Cp/
-ONbMZeQIhEL0kderI1vVoufeBf38/I93rpz6PXp5NiTwOnVdEik595/W7oldr/rM
-taKvWwfnjml+Z1uzJWWDKPnS+onxBVcoBGFhiewIejPz3UjncMiHW5QAx/BzfGgh
-UmFO0vsh8qrEfAqT4AqL4uCuyVm0JmugdWKoeQISh5i4+HMmvPaudd+8yF0plr10
-+pyNGkLZm4NBJxOD7V0AMjfTIpKRbmV8xnCAnWQ+xx/qWxHAZ0wfbZXXF0m6L6po
-dVwJJI7EnuQbAkbXnzMtCTQToZTSVwIDAQABAoICAAGjGJNY15kYKO3xzXWMyfXT
-gl7L9Im1TNVwP3Dpu+S7U0NSd1w12Iju35znzlwhx7/j8skOrKubaZvM9ztJ79Yb
-1CDs3PWhP6OVJeGvvHU9zkp59RexQ5Ma8hXskIroE2WJ97bQgRkfi/r9x8wKc39T
-aYv/SrTC0SPr+YRFSjMvfV35kvLC759slgwkmsH6ZWatSeyhPooJfX1kTRBi08A2
-i4lOBD+Bhtn2jG/+1eYtFyYXVaHx/E9XfU6QhSPgIhBULdujPucmj6pc4yRYnKxA
-32QxGc35u0QHEqJ9/iWAoporIMAmU/Qp7phl9g+OvxrFkloMc3cp3GSMh8k2bJUY
-nRvk0IPG1bF7jwezHbQGTwTlguJlWPl3+v+qeKJQI4pov3Cz6aNrBmBfEbjwcJrf
-RvPNCQ2X1GciZcGoJxcHRgFNKarzb+m92qNRftr6YDBZM8PlvJgGhnTRkOuFmIZB
-WPRergEwaClp1DbQsXwKlWpGfSMLznj57oKT3MJ31R4pusfMDBS39p9jUFH7uO+p
-e/Wdy+RaKei4AYBZIc+ks8LJzeIG+YWD9kN0lPVvzoza6nJYGGNVe/bxErjpESsR
-fZDs5EMNPGsCQ56Tgt+vtHEGF3x7ufGMF1XqisCMlaxH03aT2N3Wi5um8jO2pfFC
-4NEBq4ixvlyefb0TER+5AoIBAQC6nSDDzeeseLOJ8QSb1kHHkvQOGb8kdYY/KJ51
-+uiwoEWOYAxKubqumo2rYaqcM0lbZKWb/9cgVK4LAzdqY4z3BINA7YRzXSUXdb8T
-rbqtg33Yj7f1KBiBqns2FMktfIZ+6JUDALb+CD7rHGpi62RDd8JaKL8wugqMBmwM
-YHBfzECVSjDosjGpbILbDJPTfiEQLyPEoJxJ48Z3Xg6l+0BSZrrH9FqGVgsvEQ7f
-zhZ6rpJIefb1cFjwIRFORS4tMqS1keu+dugyUBs83AOp5kaq6O31n5hDAOyZVFsw
-HSBu8pbWAceWJrF7R1Am23063hIztjPbU9yeN2mvhVQSgO4PAoIBAQDnBQHt3prI
-AajCYbHmk0v1WT2kDD5yEX/fZzifTU5k/+0Jvixpa0KOR2qaUXyx4ocIT6F1hR89
-VXGby2UG6SlvuwXMzwmoVf5sueQ6wshu3fCaS8BYNpJnOAfgLTnUpKIWITDm/2Hj
-4NCFYL77EfBXXhFH7lLPhiPcTWQlPDuFCXU/BJuVhJbpd+GtF1+loiNQArg9nmKb
-9Ac7ccR9UBO/XSQN1th4+yaxyGQKaiYsBqsy8SPp2ynTdGXT72LePqSUrkNsjgE7
-PkzgX0pBZw8upBXk+8ByfIaaQONRbCuMQEXj6B66szaWeR2hDfaoDOk3/w6JN93r
-yPKfk4TFNB85AoIBAGoiHVVfUOjViP7l9cIPvD+eQ3GVkRFSSfS3zE+7UQXLUWPl
-GniRYywUuIgFNvw5avowpsOvYRGBN68JuEWosq52gZO2wkK+ce8Cx5aQkwBGLZey
-PWSP1khAxmx+q+BT10ZsTvtzN6AI3ofnFFaIG/EHNqECVaKH3KHAsUjkvGSvjPeb
-R2/AkOAT1+RvJc/+Bx3mQYh99AVOJz0SYHBkEjQLOyWnwqhuXVP6dqQw2LYTfRz9
-SMhUijCgDfCfBeEs0WJ2yEX96Jdc2fDmDKtfTUe8zEGK8BUDfIzD3kzh8+VF0SWL
-w5CRFxXO/DXtVS7ayC1i7eFKs8nEKDZsNOGFNF8CggEAKXRMlFKNk7Y4gijls2pb
-Bvusg/NugSmCuKPdFTjaCGWkM0tczM3ic4V9K5PTvFfZwzQG1P++S1M5v6sPxd2x
-AcudjtLX+Mz1iq0QtzqcnMhWlFljenDQdJUpVKDI789bBn2OOOU6u5lr0YM6wfLG
-HedTUoUBdxuq860vez8DryuzTkuVX48bRWmtpVG8aAxgKctTJDt3lmSDp7cSeyoT
-YRNllNYoogzvNJew2+2QS/YmYk3DFAOvzbHlU9Jw+1BiWAutLZ2NuwPC58AxourL
-XqMzCpPiRKjzvlpGcCXo6pHd+Ld+TCI8eWPiXTQUPrOSZenuwdC0kcrNPrVJ7dkc
-gQKCAQEAmE6BTX7nn0HT859PtBlsy3rM8psihR4UYuYkTdE0+KF4hBRIP6uhMAVh
-vV6UMnt3QubKIekG14seGkwkBnEhv5reYWn/1+t3qP7qqvgndGgI2yPbzJx/cPMK
-+KKhRbBAIgkjiY6hlo+DhrNS5nuBjZS2q/NnkO4NK7qBHvpIYAnRZK9qNsT4KZm6
-EO9YlCCnoZ3EB7brNgBZkxoekZG4jTlpD0E7nTxPTF1iVWedKRfmLFxAiDaSz0eo
-9tbTaRQ6ybU6jl1hMg4aINjp4xl/ScKk51veKg5ptjpPtspIh7keJRIUz3qwhuvk
-ZJpVwCxgxAOagrQtvwdedbmvChAfGA==
------END PRIVATE KEY-----
diff --git a/biometrics/face/aidl/default/apex/com.android.hardware.biometrics.face.virtual.pk8 b/biometrics/face/aidl/default/apex/com.android.hardware.biometrics.face.virtual.pk8
deleted file mode 100644
index af0ff4e..0000000
--- a/biometrics/face/aidl/default/apex/com.android.hardware.biometrics.face.virtual.pk8
+++ /dev/null
Binary files differ
diff --git a/biometrics/face/aidl/default/apex/com.android.hardware.biometrics.face.virtual.x509.pem b/biometrics/face/aidl/default/apex/com.android.hardware.biometrics.face.virtual.x509.pem
deleted file mode 100644
index 3dc37ff..0000000
--- a/biometrics/face/aidl/default/apex/com.android.hardware.biometrics.face.virtual.x509.pem
+++ /dev/null
@@ -1,34 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIF7TCCA9UCFCmJBOOYRVUgdy8vKm8OQd0ii2pPMA0GCSqGSIb3DQEBCwUAMIGx
-MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
-bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEi
-MCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTEtMCsGA1UEAwwkY29t
-LmFuZHJvaWQuaGFyZHdhcmUuYmlvbWV0cmljcy5mYWNlMCAXDTIzMDUyNTA2NDIw
-MFoYDzQ3NjEwNDIwMDY0MjAwWjCBsTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh
-bGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJv
-aWQxEDAOBgNVBAsMB0FuZHJvaWQxIjAgBgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5k
-cm9pZC5jb20xLTArBgNVBAMMJGNvbS5hbmRyb2lkLmhhcmR3YXJlLmJpb21ldHJp
-Y3MuZmFjZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPadnCNPEvKN
-HxACZt3C+tE2rga6gZ1pxfvlwGo1UY9yAxcQjL22SW93OA6R3de5uvQUbHh7b9g5
-AbNYcRnxOMiuUK2XSGWGDscxuGpQVCph4G1rXCOtbTmz1Zb42vQLCCQHHyxzI7ZE
-9Y6+rzw4p3jtEyBSiiErk13GFZTzwcDzqsQs12vDn9ovSTTn5Nyy6Csz7+O2t4SN
-qU4S/uLE6b/TiXj7196r58DcGvJgKbzbbnv/DUHGfaESrVWYLk3xoX3X8VJvP7jI
-M9XoZBbbgFLgAP5DFEvf5QWS9tR092z0YXOGr+mLHtkJM7Or9nKOohm6aFfHbuvu
-pSWyR4FWnaFANQ8wLH+fH485fiO6juJdTuRWeQpZddv0/IgguZ2Plu7z6NHCf5+L
-KYFjQ5y716wOufIqHhRLqYnpsAG2AFaMuPKzsy2KMifqoyB4KYR24EA1MCT7vwqu
-Hurp+SJrmNdkFVXbhGVUfMKf4nRZ37b9Miie0ju0OxJ9C3zY5uR5BMnqEjqq/rCX
-pQh5o1y+bWacOQVp0iQWJHfy8KkjhhQ1Pd1VeoVk9l2DsAJWm6unwMvGHviRcvzg
-BejDOVcE0x4xDj+TwAu2X2+w2hbUSY9W6lmkX4nGHlbLz211bIBFq42PzAMpRnYq
-jLxml4VpO4UnhyRp7Ipps99s7/iMFOn7AgMBAAEwDQYJKoZIhvcNAQELBQADggIB
-AG/5t56hKYKORne0bwJpMSCWZNT6JcYjf6fEmNrvG3/gz9BCuj4osZjJHD2OSUJl
-F5yI52RgPrXK8bNqJOvX6MIc4Y2JoSI2Uz4J2vZ3QhRkPC9oqwk/Enz4GIVJ4Dnm
-/kgpBbN2SN0TjnmEooptly5tWb3IPTjqLZpaPIW1ntQeCAbgrYVHiMsvNe1BuBrn
-RNi1xqw3QGp2ZV/RfJ3MH6d49TswEL1gwiUeg3hw5eG7IDfLB/2IJBrff7kmPTQ6
-n+il4uXuXMmX70xSvJXwP/NinomJsORZ4Npbmp7xV/QJp9cNoxNAo3DJ4OHRAnA+
-L7E1KZ3+nSRxGcVlqBekmG6wH9U39NN+dh758aGdJOWqA+B+1PAGxkvFCNqLgTpm
-xNl62YiRRo4FiIAkntvs+JneMEphv/T5i824t2xFZD2lBuW8r54nWj5cpx8AU9W2
-rulR0jx7BnVdj6czn1/pPCIah8Os9pZM8q1CbF8vXD+QLAF0/NjPNomTEGd/M+V+
-sfn1OhLGF/E1kWyNeOmkvX26txQdY8k6jUdsAc5vmQqgwxdorjI38ynpYQnFwq2g
-eO4l62sx8icsSh2TRklWy8BwZpaCyO/WVv/FcjIUexYhmZ0EHpmQk/RAlD1f9wFy
-CciNa/Dm94AwJgZk9LcXye3BSvb1sKF6C7eYrW0eI95V
------END CERTIFICATE-----
diff --git a/biometrics/face/aidl/default/apex/manifest.json b/biometrics/face/aidl/default/apex/manifest.json
index ecdc299..e7d177b 100644
--- a/biometrics/face/aidl/default/apex/manifest.json
+++ b/biometrics/face/aidl/default/apex/manifest.json
@@ -1,4 +1,4 @@
 {
     "name": "com.android.hardware.biometrics.face.virtual",
-    "version": 1
+    "version": 2
 }
diff --git a/biometrics/face/aidl/default/face-example.xml b/biometrics/face/aidl/default/face-example.xml
index 1c5071a..2b39b3d 100644
--- a/biometrics/face/aidl/default/face-example.xml
+++ b/biometrics/face/aidl/default/face-example.xml
@@ -1,7 +1,7 @@
 <manifest version="1.0" type="device">
     <hal format="aidl">
         <name>android.hardware.biometrics.face</name>
-        <version>3</version>
+        <version>4</version>
         <fqname>IFace/virtual</fqname>
     </hal>
 </manifest>
diff --git a/biometrics/face/aidl/default/face.sysprop b/biometrics/face/aidl/default/face.sysprop
index 6b0f37f..be32015 100644
--- a/biometrics/face/aidl/default/face.sysprop
+++ b/biometrics/face/aidl/default/face.sysprop
@@ -157,3 +157,22 @@
     access: ReadWrite
     api_name: "operation_authenticate_duration"
 }
+
+# insert error for authenticate operations
+prop {
+    prop_name: "vendor.face.virtual.operation_authenticate_error"
+    type: Integer
+    scope: Internal
+    access: ReadWrite
+    api_name: "operation_authenticate_error"
+}
+
+# acquired info during authentication in format of sequence
+prop {
+    prop_name: "vendor.face.virtual.operation_authenticate_acquired"
+    type: String
+    scope: Internal
+    access: ReadWrite
+    api_name: "operation_authenticate_acquired"
+}
+
diff --git a/biometrics/face/aidl/default/tests/FakeFaceEngineTest.cpp b/biometrics/face/aidl/default/tests/FakeFaceEngineTest.cpp
index c8ad6b7..6897dc4 100644
--- a/biometrics/face/aidl/default/tests/FakeFaceEngineTest.cpp
+++ b/biometrics/face/aidl/default/tests/FakeFaceEngineTest.cpp
@@ -245,7 +245,7 @@
     FaceHalProperties::enrollments({3});
     FaceHalProperties::enrollment_hit(100);
     mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, mCancel.get_future());
-    ASSERT_EQ(Error::UNABLE_TO_PROCESS, mCallback->mError);
+    ASSERT_EQ(Error::TIMEOUT, mCallback->mError);
     ASSERT_TRUE(mCallback->mAuthenticateFailed);
 }
 
@@ -380,4 +380,4 @@
     ASSERT_FALSE(mCallback->mAuthenticateFailed);
 }
 
-}  // namespace aidl::android::hardware::biometrics::face
\ No newline at end of file
+}  // namespace aidl::android::hardware::biometrics::face
diff --git a/biometrics/fingerprint/aidl/default/Android.bp b/biometrics/fingerprint/aidl/default/Android.bp
index 3bb3f3a..a173a00 100644
--- a/biometrics/fingerprint/aidl/default/Android.bp
+++ b/biometrics/fingerprint/aidl/default/Android.bp
@@ -11,8 +11,6 @@
     name: "android.hardware.biometrics.fingerprint-service.example",
     vendor: true,
     relative_install_path: "hw",
-    init_rc: [":fingerprint-example.rc"],
-    vintf_fragments: [":fingerprint-example.xml"],
     local_include_dirs: ["include"],
     srcs: [
         "FakeLockoutTracker.cpp",
@@ -24,15 +22,21 @@
         "Session.cpp",
         "main.cpp",
     ],
+    stl: "c++_static",
     shared_libs: [
-        "libbase",
         "libbinder_ndk",
+        "liblog",
+    ],
+    static_libs: [
+        "libandroid.hardware.biometrics.fingerprint.VirtualProps",
+        "libbase",
         "android.hardware.biometrics.fingerprint-V3-ndk",
         "android.hardware.biometrics.common-V3-ndk",
         "android.hardware.biometrics.common.thread",
         "android.hardware.biometrics.common.util",
+        "android.hardware.keymaster-V4-ndk",
     ],
-    static_libs: ["libandroid.hardware.biometrics.fingerprint.VirtualProps"],
+    installable: false, // install APEX instead
 }
 
 cc_test {
@@ -143,12 +147,35 @@
     vendor: true,
 }
 
-filegroup {
+prebuilt_etc {
     name: "fingerprint-example.rc",
-    srcs: ["fingerprint-example.rc"],
+    src: "fingerprint-example.rc",
+    installable: false,
 }
 
-filegroup {
+prebuilt_etc {
     name: "fingerprint-example.xml",
-    srcs: ["fingerprint-example.xml"],
+    src: "fingerprint-example.xml",
+    sub_dir: "vintf",
+    installable: false,
+}
+
+apex {
+    name: "com.android.hardware.biometrics.fingerprint.virtual",
+    manifest: "apex_manifest.json",
+    file_contexts: "apex_file_contexts",
+    key: "com.android.hardware.key",
+    certificate: ":com.android.hardware.certificate",
+    updatable: false,
+    vendor: true,
+
+    binaries: [
+        "android.hardware.biometrics.fingerprint-service.example",
+    ],
+    prebuilts: [
+        // init_rc
+        "fingerprint-example.rc",
+        // vintf_fragment
+        "fingerprint-example.xml",
+    ],
 }
diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
index 54076c8..4e80052 100644
--- a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
+++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
@@ -32,7 +32,9 @@
 namespace aidl::android::hardware::biometrics::fingerprint {
 
 FakeFingerprintEngine::FakeFingerprintEngine()
-    : mRandom(std::mt19937::default_seed), mWorkMode(WorkMode::kIdle) {}
+    : mRandom(std::mt19937::default_seed),
+      mWorkMode(WorkMode::kIdle),
+      isLockoutTimerSupported(true) {}
 
 void FakeFingerprintEngine::generateChallengeImpl(ISessionCallback* cb) {
     BEGIN_OP(0);
@@ -142,7 +144,7 @@
         return true;
     }
     auto enrollmentId = std::stoi(parts[0]);
-    auto progress = parseEnrollmentCapture(parts[1]);
+    auto progress = Util::parseEnrollmentCapture(parts[1]);
     for (size_t i = 0; i < progress.size(); i += 2) {
         auto left = (progress.size() - i) / 2 - 1;
         auto duration = progress[i][0];
@@ -193,7 +195,7 @@
     int64_t now = Util::getSystemNanoTime();
     int64_t duration = FingerprintHalProperties::operation_authenticate_duration().value_or(10);
     auto acquired = FingerprintHalProperties::operation_authenticate_acquired().value_or("1");
-    auto acquiredInfos = parseIntSequence(acquired);
+    auto acquiredInfos = Util::parseIntSequence(acquired);
     int N = acquiredInfos.size();
 
     if (N == 0) {
@@ -271,7 +273,7 @@
             FingerprintHalProperties::operation_detect_interaction_duration().value_or(10);
 
     auto acquired = FingerprintHalProperties::operation_detect_interaction_acquired().value_or("1");
-    auto acquiredInfos = parseIntSequence(acquired);
+    auto acquiredInfos = Util::parseIntSequence(acquired);
     int N = acquiredInfos.size();
     int64_t now = Util::getSystemNanoTime();
 
@@ -305,15 +307,6 @@
         SLEEP_MS(duration / N);
     } while (!Util::hasElapsed(now, duration));
 
-    auto id = FingerprintHalProperties::enrollment_hit().value_or(0);
-    auto enrolls = FingerprintHalProperties::enrollments();
-    auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();
-    if (id <= 0 || !isEnrolled) {
-        LOG(ERROR) << "Fail: not enrolled";
-        cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
-        return true;
-    }
-
     cb->onInteractionDetected();
 
     return true;
@@ -386,7 +379,7 @@
         return;
     }
     clearLockout(cb);
-    isLockoutTimerAborted = true;
+    if (isLockoutTimerStarted) isLockoutTimerAborted = true;
 }
 
 void FakeFingerprintEngine::clearLockout(ISessionCallback* cb) {
@@ -450,76 +443,6 @@
     return SensorLocation();
 }
 
-std::vector<int32_t> FakeFingerprintEngine::parseIntSequence(const std::string& str,
-                                                             const std::string& sep) {
-    std::vector<std::string> seqs = Util::split(str, sep);
-    std::vector<int32_t> res;
-
-    for (const auto& seq : seqs) {
-        int32_t val;
-        if (ParseInt(seq, &val)) {
-            res.push_back(val);
-        } else {
-            LOG(WARNING) << "Invalid int sequence:" + str;
-            res.clear();
-            break;
-        }
-    }
-
-    return res;
-}
-
-bool FakeFingerprintEngine::parseEnrollmentCaptureSingle(const std::string& str,
-                                                         std::vector<std::vector<int32_t>>& res) {
-    std::vector<int32_t> defaultAcquiredInfo = {(int32_t)AcquiredInfo::GOOD};
-    bool aborted = true;
-
-    do {
-        std::smatch sms;
-        // Parses strings like "1000-[5,1]" or "500"
-        std::regex ex("((\\d+)(-\\[([\\d|,]+)\\])?)");
-        if (!regex_match(str.cbegin(), str.cend(), sms, ex)) break;
-        int32_t duration;
-        if (!ParseInt(sms.str(2), &duration)) break;
-        res.push_back({duration});
-        if (!sms.str(4).empty()) {
-            auto acqv = parseIntSequence(sms.str(4));
-            if (acqv.empty()) break;
-            res.push_back(acqv);
-        } else
-            res.push_back(defaultAcquiredInfo);
-        aborted = false;
-    } while (0);
-
-    return !aborted;
-}
-
-std::vector<std::vector<int32_t>> FakeFingerprintEngine::parseEnrollmentCapture(
-        const std::string& str) {
-    std::vector<std::vector<int32_t>> res;
-
-    std::string s(str);
-    s.erase(std::remove_if(s.begin(), s.end(), ::isspace), s.end());
-    bool aborted = false;
-    std::smatch sms;
-    // Parses strings like "1000-[5,1],500,800-[6,5,1]"
-    //                      ---------- --- -----------
-    //  into parts:             A       B       C
-    while (regex_search(s, sms, std::regex("^(,)?(\\d+(-\\[[\\d|,]+\\])?)"))) {
-        if (!parseEnrollmentCaptureSingle(sms.str(2), res)) {
-            aborted = true;
-            break;
-        }
-        s = sms.suffix();
-    }
-    if (aborted || s.length() != 0) {
-        res.clear();
-        LOG(ERROR) << "Failed to parse enrollment captures:" + str;
-    }
-
-    return res;
-}
-
 std::pair<AcquiredInfo, int32_t> FakeFingerprintEngine::convertAcquiredInfo(int32_t code) {
     std::pair<AcquiredInfo, int32_t> res;
     if (code > FINGERPRINT_ACQUIRED_VENDOR_BASE) {
@@ -603,6 +526,7 @@
     isLockoutTimerStarted = true;
 }
 void FakeFingerprintEngine::lockoutTimerExpired(ISessionCallback* cb) {
+    BEGIN_OP(0);
     if (!isLockoutTimerAborted) {
         clearLockout(cb);
     }
diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngineSide.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngineSide.cpp
index a78cdcd..acb792d 100644
--- a/biometrics/fingerprint/aidl/default/FakeFingerprintEngineSide.cpp
+++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngineSide.cpp
@@ -27,9 +27,7 @@
 
 namespace aidl::android::hardware::biometrics::fingerprint {
 
-FakeFingerprintEngineSide::FakeFingerprintEngineSide() : FakeFingerprintEngine() {
-    isLockoutTimerSupported = true;
-}
+FakeFingerprintEngineSide::FakeFingerprintEngineSide() : FakeFingerprintEngine() {}
 
 SensorLocation FakeFingerprintEngineSide::defaultSensorLocation() {
     return SensorLocation{.sensorLocationX = defaultSensorLocationX,
diff --git a/biometrics/fingerprint/aidl/default/README.md b/biometrics/fingerprint/aidl/default/README.md
index 823cd18..4b4533a 100644
--- a/biometrics/fingerprint/aidl/default/README.md
+++ b/biometrics/fingerprint/aidl/default/README.md
@@ -11,12 +11,6 @@
 following to your device's `.mk` file to include it:
 
 ```
-PRODUCT_PACKAGES_DEBUG += android.hardware.biometrics.fingerprint-service.example
-```
-
-or add the following to include it as an apex:
-
-```
 PRODUCT_PACKAGES_DEBUG += com.android.hardware.biometrics.fingerprint.virtual
 ```
 
diff --git a/biometrics/fingerprint/aidl/default/apex/Android.bp b/biometrics/fingerprint/aidl/default/apex/Android.bp
deleted file mode 100644
index ad36ae2..0000000
--- a/biometrics/fingerprint/aidl/default/apex/Android.bp
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (C) 2023 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-apex_key {
-    name: "com.android.hardware.biometrics.fingerprint.virtual.key",
-    public_key: "com.android.hardware.biometrics.fingerprint.virtual.avbpubkey",
-    private_key: "com.android.hardware.biometrics.fingerprint.virtual.pem",
-}
-
-android_app_certificate {
-    name: "com.android.hardware.biometrics.fingerprint.virtual.certificate",
-    certificate: "com.android.hardware.biometrics.fingerprint.virtual",
-}
-
-apex {
-    name: "com.android.hardware.biometrics.fingerprint.virtual",
-    manifest: "manifest.json",
-    file_contexts: "file_contexts",
-    key: "com.android.hardware.biometrics.fingerprint.virtual.key",
-    certificate: ":com.android.hardware.biometrics.fingerprint.virtual.certificate",
-    updatable: false,
-    use_vndk_as_stable: true,
-    vendor: true,
-
-    binaries: [
-        "android.hardware.biometrics.fingerprint-service.example",
-    ],
-    prebuilts: [
-        // init_rc
-        "fingerprint-example-apex.rc",
-        // vintf_fragment
-        "fingerprint-example-apex.xml",
-    ],
-
-    overrides: [
-        "android.hardware.biometrics.fingerprint-service.example",
-    ],
-}
-
-genrule {
-    name: "gen-fingerprint-example-apex.rc",
-    srcs: [":fingerprint-example.rc"],
-    out: ["fingerprint-example-apex.rc"],
-    cmd: "sed -e 's@/vendor/bin/@/apex/com.android.hardware.biometrics.fingerprint.virtual/bin/@' $(in) > $(out)",
-}
-
-prebuilt_etc {
-    name: "fingerprint-example-apex.rc",
-    src: ":gen-fingerprint-example-apex.rc",
-    installable: false,
-}
-
-prebuilt_etc {
-    name: "fingerprint-example-apex.xml",
-    src: ":fingerprint-example.xml",
-    sub_dir: "vintf",
-    installable: false,
-}
diff --git a/biometrics/fingerprint/aidl/default/apex/com.android.hardware.biometrics.fingerprint.virtual.avbpubkey b/biometrics/fingerprint/aidl/default/apex/com.android.hardware.biometrics.fingerprint.virtual.avbpubkey
deleted file mode 100644
index 9f2334a..0000000
--- a/biometrics/fingerprint/aidl/default/apex/com.android.hardware.biometrics.fingerprint.virtual.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/biometrics/fingerprint/aidl/default/apex/com.android.hardware.biometrics.fingerprint.virtual.pem b/biometrics/fingerprint/aidl/default/apex/com.android.hardware.biometrics.fingerprint.virtual.pem
deleted file mode 100644
index 14eb288..0000000
--- a/biometrics/fingerprint/aidl/default/apex/com.android.hardware.biometrics.fingerprint.virtual.pem
+++ /dev/null
@@ -1,52 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDwo6CxzAwmX46C
-N1j8zr0qf6uy1rDkG5r1s4faDNX/ThYpd69DWbVGf22yFO3KY3B+TmqKU+B0SiqY
-MHQjXc+UcTa0BKtPtQNSYFLK13/1rW69QrlLtcGyAp/CwksXYuuJ8Uzs28nQ5A+z
-fh6Vfsla9tUWDeWtN4oehmOwZ0ZKPBAKKocB5W+YJoIkdVnzkiOnWawk8Wn7a1N0
-jN5wFDO/99De+rTzYgKvV3Xb68H2UrIei4TW4wEpMZuDRUfbPlrSkJpdf+hYKKLb
-AkotUrwA1znYR3U9t0GIHwZ5sp+OQTCkTfJMg2c+Bpmkp3TAL24LdvkYVA2LZERB
-p7xNeACH210Bb8QOAEaiLyVWuL6iapXI67TOkZ5yk7heb5zCxRcpOMA4FQ0hWYoX
-AWstem9ADWvZgKe0BFGx7lhp5YmdBCzdNv0za3Va9EATJ0wgy4qMpZu3yjDPE9bq
-gdb3BQL3kiDHdfR/LF9bCIP9QEHg+1mWOZDO1x8lXCuQrgp+gnh9bbfX5g6lA8IQ
-dTfozjLQC3mVbl0P7/PIf9rnHjcqUh3/1RJH51tkgqBO8xn7D6AIOv665hI+O319
-PU4j26vLSfEu624IjEDK/gcgacuoYl3H3LMI5a2JIWFLOtpKRo9P2qFF702o8B97
-5slGDYnxpAAnEQQADuRYgDalDlFDNQIDAQABAoICABe7k4wleydHslbeXYzlWNu5
-prXnHaAJpvFHiQT00iAxU9c4IhVq4gl3ZNq03LTitMQIONK2rgLaE7RZxwJ77I6P
-0dzUPw8H47F6pX+y3EBfH/ZTf9HbNaS4RIhhQCWo0GEU5sjPbmqHK5NAw4Rr8jDh
-+icILNg2C42yJF/P96s3nD9cbV8/ARAI8DnnRv1SMuj825DzLEgrEBqFECUOoQH0
-T2nGYRVF28zuO8X6TPFdu4puqSXGUqV86oD6UrlpP2zX7Rl+lWwoadNeuPEaYUdV
-8rMFbScujSx/HtTezISroj/6HgT0yrhfz0RhbY7MvrYrwCppk8JlG6Q8BkK/rJGI
-lGw7nKIp43Tl8T2Rzw0I1dPwxCLMJuVErUclznN7lq25akG5XRwBAUQQE6nHO3GL
-jh7eFW7rEcpUBEKYTK1ZA8QrWGUW3WittDqZ6VQU/ZudVoTcgbW3YpUYq5z76O4u
-B6tqlmNtQfIi3LBh8CD19SIjV6KKVa0s++ArQEu/DWzmHWh/STZZk4b1DSdFYqm0
-zgylOVUfpcG10OxPOdvMEsA0VoXxxwl6Hx3DSEX6VxOQvBSkwhu+gw8u2keOsIYZ
-Ha2OxtG5FiEQqSa8YNN/0NDdOp1eEyqvdvT7o51cqHHPvlyhk7XL0AguErCtGggn
-TZ3rsUChlauG9GbJ4nOBAoIBAQD+ceJpOkMuw04dhIY5of3CcTTKGWdt2/9SP9PU
-ZORC6ywBhRORQBhyVZIERmxfJUGsmePHeIEQ5L8IniUDRTCKrL+J2bR8NdYZfvCJ
-9cYD2gjXmikFabKn6mkQz7JXjSr3Vamx2ueKuYqfo72aHVvhFv6Hi/ulbUPPZ15c
-gVo0iU5GRt/1XNyTksSiSKRyxXJYWqg5GD2JQ92Zbo5a5LhulC7wFF39jyfTe7K2
-mfrCI7dr+A0WtiTpbRH9EIU8CseIBgIEDTgMAxbBdUZpF8pecIAy0MLFefLA+8CK
-RN+8AI+HRHybjvuonOAfeEtQyyknzycxh20dyrd3FvBj40NBAoIBAQDyHCRuNwXA
-twETiysx1XStaoODLQkPxSqdFIBD+tfVR+E/3blVXkdrFfrTcr8NGdcgsJgQgYWr
-h9OENizcHg75gX6wdo3qYGQdZZxws/dQbNylObFYNkyFQrD1vkQzjZl5O4DaRJC7
-6YbJrrXZ44dgZoMo/M8nyNU5yaLvoOf4GV4bSiEfsx/MxWK7x6rCcpw9jpm+yQlB
-9NblSgWzfg0hmcRBn6haC3q45walBYGTVJfzTOMgn5bUmMxKqKlCXvp20BPLdclQ
-5Y14OqkqhnFeHpBSJ7iVI9BBy2nAsyk37NvYVg7mN8fGiWbCqurIrbPRGSCdohGr
-wY7zOVd1hmb1AoIBAGPhVJ0177VlmT5hDUeGXVR8l9pVipJHb7xbrc2MJUZXhpi6
-Imo8HNyU1pKzCkt3Foaoig99MDzvbkX1vlXATUPCeBWmzgCMKZUsjUO6pJZSenIX
-485qJWVg0Ql2Xm2bzqf0in50jbuZBd+QqRbcO3rqSdPvkULo11uNGi95320MERvp
-KnTolPWhAWsq1NLwyuf//lUbPNyrNUvLaDop2nQd2ycG97ZXAa00u3yOiS64UoIh
-hxHJQkgXNp5+Y66kFJtCsHvirIOams4qOQ979UaJJunLpQlby30R1gzw6FqmZbEV
-o0x1HjicDCaOVBJNDcTAvoPkw2KUdtxattafGYECggEBAPICm4/oREHdLKBCjszj
-mBv4yrkG/XXcGrql0YkiZzj0/v3+PtJMyYsLj4xpuPv5hodQvtBRCDLsNMyF8tWc
-3k8d2GvANh/AdpLEDVrDKkYka3Jldxa8QEU84vLiW/5EXtNGXYjQ3PRZfLiBgZnp
-zFraXeVMwC3+nNWE7vAloXrosJ8KvI2ZWgIwlH8sGU8BjZgiwSBqiGx7t4u/MG+5
-Yprhv8HxPDG2I9hMZuHx3RJOjw1PIAJuRDEDA8LlUTvdAPRfDkpk1PWeYIl76blu
-Zkg0uQLGXcYG5JfAI1fSPzN9+kwHyiDqRTH6CtQwUTyEFajAO1AWvx82/hO2j+wU
-izkCggEANyPfsBjEUIZjwBuaHajm/tkcQAo3F4EgIkb1XR/EnfaaJp4I9S6hJ0vv
-5/fQtASn+JHjuIRk5l7g9N7lU+W+SiPvSxm1zZv8zLkqJpbKpMh7VIxT9joZ3E3/
-rzRLL60zYJ42hdulSFLoO1qCMErifBiTIwIZu7p6qKRH4+vqappb9QTPPlyAFFT6
-3UJfs49HGqd6gTyN7TSNxaya+ZBaLgSXhmExY/OtZazQn/iJl/dYpyYvmJdzNpd+
-XELU0IUcKivJaueCqK8NfEqfHz28GHdAkwHd0CzGnciF4tn9K2Sg8+X9jISk/Usx
-qHAY4JU3ldxQzDUZCz5VCz372pgXkQ==
------END PRIVATE KEY-----
diff --git a/biometrics/fingerprint/aidl/default/apex/com.android.hardware.biometrics.fingerprint.virtual.pk8 b/biometrics/fingerprint/aidl/default/apex/com.android.hardware.biometrics.fingerprint.virtual.pk8
deleted file mode 100644
index ab59820..0000000
--- a/biometrics/fingerprint/aidl/default/apex/com.android.hardware.biometrics.fingerprint.virtual.pk8
+++ /dev/null
Binary files differ
diff --git a/biometrics/fingerprint/aidl/default/apex/com.android.hardware.biometrics.fingerprint.virtual.x509.pem b/biometrics/fingerprint/aidl/default/apex/com.android.hardware.biometrics.fingerprint.virtual.x509.pem
deleted file mode 100644
index 6d10157..0000000
--- a/biometrics/fingerprint/aidl/default/apex/com.android.hardware.biometrics.fingerprint.virtual.x509.pem
+++ /dev/null
@@ -1,35 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIGCzCCA/MCFFuIt0T1K9U92QfzZI3RpCyRp1ruMA0GCSqGSIb3DQEBCwUAMIHA
-MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
-bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEi
-MCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTE8MDoGA1UEAwwzY29t
-LmFuZHJvaWQuaGFyZHdhcmUuYmlvbWV0cmljcy5maW5nZXJwcmludC52aXJ0dWFs
-MCAXDTIzMDUxMDA3MDkwMloYDzQ3NjEwNDA1MDcwOTAyWjCBwDELMAkGA1UEBhMC
-VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcx
-EDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNVBAsMB0FuZHJvaWQxIjAgBgkqhkiG9w0B
-CQEWE2FuZHJvaWRAYW5kcm9pZC5jb20xPDA6BgNVBAMMM2NvbS5hbmRyb2lkLmhh
-cmR3YXJlLmJpb21ldHJpY3MuZmluZ2VycHJpbnQudmlydHVhbDCCAiIwDQYJKoZI
-hvcNAQEBBQADggIPADCCAgoCggIBAMKXBcAw2Cs68KUMt5Hw2LVwkgEDiwqaXkG9
-V0SBK9y/q+6JCoxs9+4NsDPAEzHB+UHpqJ2i07VW1YV9F+2V93KCy/0fUjXIWmu8
-P0ixb+t8wlHZM2zYXQe9PELUZ/ZlYjAxkVKJjDmsCM8yzpRk+g3KKswlYwzyBoat
-qukrtvAaNKlRGJmjeStEo2o4qgQQUq96NAvSt3d6PsrNdFbXxqX61JT1dT8Kgxhk
-+oSAkV2/C2knQp/8ME1oJrK/D+glynXVqVkvYYplxp+GCIZUs/DOJcHyb+ZrNLJP
-f2zr4yDoB2pnV3G9VcjbdznWc661Wg+B2yZEvLVbOMiqaMRlpHzNczghowCy3xoq
-42IUmp3HLak0DUmrrUZDnJAAT/KEZxh/PwLeAcmUrQCbUiG5lN/njuZ5LjJ8gdcg
-v0RDPSIIszamrf2l+xlaI34iXS7xHf3OLTbgst2L7LhW5qmLsB1SZzAYGIUYnRWZ
-aqzaFIAWt28S0yhpOt+qS7b1l+fvMb09jiKfxzkYQh2bB0HrpbTl2x390q2GW6yE
-jmraC+nTiAVDCUnDjOVji7nXDloSmK+MDD/DaDQ47PoYE3hBqc0fsRr2aIlvMOvb
-m+4VhO85gCuJAK02XuixLPo6ZqBAEFNwQ4NDcuOHuODaaJ/amTxQBXRpNXUSnhXy
-ejpwspHrAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAGl3KGKMAWpefnRSQWs0n8kD
-eKYbzbe6mG0O5lqx5FKpJZpIki4RH1Yh5ZDR/NIeF2RNhlb/dqo99TKbCEGAoMB9
-R7EXDhyMcEXr5ATCA6iurhMKgkcLfOz6HkTI6k3wlPNuvx4iZGHD2KEvZiZGmae1
-dnK9iVtNs6ccyC0+V3y3Yt1fvjzp9SWcPpBXiO5QNf0sHxtzc32xXsBH0aLwLjDZ
-BCytwfEvq2S25v0r9m0fKquDBoFnk68cqClpNQZ9Ky0k8fGgOiQ5/jnVmgPmTheG
-mBcdPeUrhxGNs1vax/i/ysT4AVmDzIVW0uXVouhVeMQzykuy1+Ywa/Rn0jLxeNF2
-X3ooOE+EF8u9Pxf8ILRnfqok3VRuYLH6neNknTSKTx1aQAh9XbBpTUa/eCq3LDCP
-L6hSXYWjk9e5txbn0cNw9WuKMUg+Z9Qms3aVRFcvBxZQySEvf8FhgSrQbqbCbzhf
-dI0/ouW5w9iHUOh/FDvfETeZCeeTS+EOvGOqknzO8Y7PiChlgFsoMvC1GpHZ1ADy
-3xKSh15G92JCiv89CK2VvM8QDFh8ErQmSLjhMl700CLYis+AAZhCKOhAo573zj2u
-dZf29S+o3SEBhsl5snVGJW13Bu7BjxQtscCwKOv0g1cCkrqgcm2bMuNhpTK7rhMP
-i4hGSvbdGC27BtXbsiVX
------END CERTIFICATE-----
diff --git a/biometrics/fingerprint/aidl/default/apex/file_contexts b/biometrics/fingerprint/aidl/default/apex_file_contexts
similarity index 100%
rename from biometrics/fingerprint/aidl/default/apex/file_contexts
rename to biometrics/fingerprint/aidl/default/apex_file_contexts
diff --git a/biometrics/fingerprint/aidl/default/apex/manifest.json b/biometrics/fingerprint/aidl/default/apex_manifest.json
similarity index 100%
rename from biometrics/fingerprint/aidl/default/apex/manifest.json
rename to biometrics/fingerprint/aidl/default/apex_manifest.json
diff --git a/biometrics/fingerprint/aidl/default/fingerprint-example.rc b/biometrics/fingerprint/aidl/default/fingerprint-example.rc
index ee4713c..da4ea45 100644
--- a/biometrics/fingerprint/aidl/default/fingerprint-example.rc
+++ b/biometrics/fingerprint/aidl/default/fingerprint-example.rc
@@ -1,4 +1,4 @@
-service vendor.fingerprint-example /vendor/bin/hw/android.hardware.biometrics.fingerprint-service.example
+service vendor.fingerprint-example /apex/com.android.hardware.biometrics.fingerprint.virtual/bin/hw/android.hardware.biometrics.fingerprint-service.example
     class hal
     user nobody
     group nobody
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
index 2450115..15d8360 100644
--- a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
@@ -68,10 +68,6 @@
 
     virtual void fingerDownAction();
 
-    std::vector<int32_t> parseIntSequence(const std::string& str, const std::string& sep = ",");
-
-    std::vector<std::vector<int32_t>> parseEnrollmentCapture(const std::string& str);
-
     int32_t getLatency(const std::vector<std::optional<std::int32_t>>& latencyVec);
 
     std::mt19937 mRandom;
@@ -110,8 +106,6 @@
     static constexpr int32_t FINGERPRINT_ERROR_VENDOR_BASE = 1000;
     std::pair<AcquiredInfo, int32_t> convertAcquiredInfo(int32_t code);
     std::pair<Error, int32_t> convertError(int32_t code);
-    bool parseEnrollmentCaptureSingle(const std::string& str,
-                                      std::vector<std::vector<int32_t>>& res);
     int32_t getRandomInRange(int32_t bound1, int32_t bound2);
     bool checkSensorLockout(ISessionCallback*);
     void clearLockout(ISessionCallback* cb);
diff --git a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
index fe405f4..eedcae1 100644
--- a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
+++ b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
@@ -357,7 +357,7 @@
     FingerprintHalProperties::enrollment_hit({});
     mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
     mEngine.fingerDownAction();
-    ASSERT_EQ(0, mCallback->mInteractionDetectedCount);
+    ASSERT_EQ(1, mCallback->mInteractionDetectedCount);
 }
 
 TEST_F(FakeFingerprintEngineTest, InteractionDetectNotEnrolled) {
@@ -365,7 +365,7 @@
     FingerprintHalProperties::enrollment_hit(25);
     mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
     mEngine.fingerDownAction();
-    ASSERT_EQ(0, mCallback->mInteractionDetectedCount);
+    ASSERT_EQ(1, mCallback->mInteractionDetectedCount);
 }
 
 TEST_F(FakeFingerprintEngineTest, InteractionDetectError) {
@@ -421,29 +421,29 @@
 
 TEST_F(FakeFingerprintEngineTest, parseIntSequence) {
     std::vector<int32_t> seqV;
-    seqV = mEngine.parseIntSequence("");
+    seqV = Util::parseIntSequence("");
     ASSERT_EQ(0, seqV.size());
-    seqV = mEngine.parseIntSequence("2");
+    seqV = Util::parseIntSequence("2");
     ASSERT_EQ(1, seqV.size());
     ASSERT_EQ(2, seqV[0]);
-    seqV = mEngine.parseIntSequence("2,3,4");
+    seqV = Util::parseIntSequence("2,3,4");
     std::vector<int32_t> expV{2, 3, 4};
     ASSERT_EQ(expV, seqV);
-    seqV = mEngine.parseIntSequence("2,3,a");
+    seqV = Util::parseIntSequence("2,3,a");
     ASSERT_EQ(0, seqV.size());
-    seqV = mEngine.parseIntSequence("2, 3, 4");
+    seqV = Util::parseIntSequence("2, 3, 4");
     ASSERT_EQ(expV, seqV);
-    seqV = mEngine.parseIntSequence("123,456");
+    seqV = Util::parseIntSequence("123,456");
     ASSERT_EQ(2, seqV.size());
     std::vector<int32_t> expV1{123, 456};
     ASSERT_EQ(expV1, seqV);
-    seqV = mEngine.parseIntSequence("12f3,456");
+    seqV = Util::parseIntSequence("12f3,456");
     ASSERT_EQ(0, seqV.size());
 }
 
 TEST_F(FakeFingerprintEngineTest, parseEnrollmentCaptureOk) {
     std::vector<std::vector<int32_t>> ecV;
-    ecV = mEngine.parseEnrollmentCapture("100,200,300");
+    ecV = Util::parseEnrollmentCapture("100,200,300");
     ASSERT_EQ(6, ecV.size());
     std::vector<std::vector<int32_t>> expE{{100}, {200}, {300}};
     std::vector<int32_t> defC{1};
@@ -451,26 +451,26 @@
         ASSERT_EQ(expE[i / 2], ecV[i]);
         ASSERT_EQ(defC, ecV[i + 1]);
     }
-    ecV = mEngine.parseEnrollmentCapture("100");
+    ecV = Util::parseEnrollmentCapture("100");
     ASSERT_EQ(2, ecV.size());
     ASSERT_EQ(expE[0], ecV[0]);
     ASSERT_EQ(defC, ecV[1]);
 
-    ecV = mEngine.parseEnrollmentCapture("100-[5,6,7]");
+    ecV = Util::parseEnrollmentCapture("100-[5,6,7]");
     std::vector<int32_t> expC{5, 6, 7};
     ASSERT_EQ(2, ecV.size());
     for (int i = 0; i < ecV.size(); i += 2) {
         ASSERT_EQ(expE[i / 2], ecV[i]);
         ASSERT_EQ(expC, ecV[i + 1]);
     }
-    ecV = mEngine.parseEnrollmentCapture("100-[5,6,7], 200, 300-[9,10]");
+    ecV = Util::parseEnrollmentCapture("100-[5,6,7], 200, 300-[9,10]");
     std::vector<std::vector<int32_t>> expC1{{5, 6, 7}, {1}, {9, 10}};
     ASSERT_EQ(6, ecV.size());
     for (int i = 0; i < ecV.size(); i += 2) {
         ASSERT_EQ(expE[i / 2], ecV[i]);
         ASSERT_EQ(expC1[i / 2], ecV[i + 1]);
     }
-    ecV = mEngine.parseEnrollmentCapture("100-[5,6,7], 200-[2,1], 300-[9]");
+    ecV = Util::parseEnrollmentCapture("100-[5,6,7], 200-[2,1], 300-[9]");
     std::vector<std::vector<int32_t>> expC2{{5, 6, 7}, {2, 1}, {9}};
     ASSERT_EQ(ecV.size(), 6);
     for (int i = 0; i < ecV.size(); i += 2) {
@@ -484,7 +484,7 @@
                                     "100,2x0,300", "200-[f]", "a,b"};
     std::vector<std::vector<int32_t>> ecV;
     for (const auto& s : badStr) {
-        ecV = mEngine.parseEnrollmentCapture(s);
+        ecV = Util::parseEnrollmentCapture(s);
         ASSERT_EQ(ecV.size(), 0);
     }
 }
@@ -508,7 +508,7 @@
 TEST_F(FakeFingerprintEngineTest, lockoutTimer) {
     mEngine.startLockoutTimer(200, mCallback.get());
     ASSERT_TRUE(mEngine.getLockoutTimerStarted());
-    std::this_thread::sleep_for(std::chrono::milliseconds(210));
+    std::this_thread::sleep_for(std::chrono::milliseconds(250));
     ASSERT_FALSE(mEngine.getLockoutTimerStarted());
     ASSERT_TRUE(mCallback->mLockoutCleared);
 }
diff --git a/bluetooth/aidl/default/BluetoothHci.cpp b/bluetooth/aidl/default/BluetoothHci.cpp
index 9862e9e..a247cb0 100644
--- a/bluetooth/aidl/default/BluetoothHci.cpp
+++ b/bluetooth/aidl/default/BluetoothHci.cpp
@@ -320,6 +320,7 @@
   {
     std::lock_guard<std::mutex> guard(mStateMutex);
     mState = HalState::READY;
+    mH4 = nullptr;
   }
   return ndk::ScopedAStatus::ok();
 }
@@ -346,13 +347,16 @@
 
 ndk::ScopedAStatus BluetoothHci::send(PacketType type,
     const std::vector<uint8_t>& v) {
-  if (mH4 == nullptr) {
-    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-  }
   if (v.empty()) {
     ALOGE("Packet is empty, no data was found to be sent");
     return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
   }
+
+  std::lock_guard<std::mutex> guard(mStateMutex);
+  if (mH4 == nullptr) {
+    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+  }
+
   mH4->Send(type, v);
   return ndk::ScopedAStatus::ok();
 }
diff --git a/bluetooth/audio/aidl/Android.bp b/bluetooth/audio/aidl/Android.bp
index 1028fae..feed6f5 100644
--- a/bluetooth/audio/aidl/Android.bp
+++ b/bluetooth/audio/aidl/Android.bp
@@ -27,10 +27,12 @@
     host_supported: true,
     srcs: ["android/hardware/bluetooth/audio/*.aidl"],
     stability: "vintf",
+    defaults: [
+        "latest_android_hardware_audio_common_import_interface",
+    ],
     imports: [
         "android.hardware.common-V2",
         "android.hardware.common.fmq-V1",
-        "android.hardware.audio.common-V2",
     ],
     backend: {
         cpp: {
@@ -75,6 +77,23 @@
         },
 
     ],
-    frozen: true,
+    frozen: false,
 
 }
+
+// Note: This should always be one version ahead of the last frozen version
+latest_android_hardware_bluetooth_audio = "android.hardware.bluetooth.audio-V4"
+
+cc_defaults {
+    name: "latest_android_hardware_bluetooth_audio_ndk_shared",
+    shared_libs: [
+        latest_android_hardware_bluetooth_audio + "-ndk",
+    ],
+}
+
+cc_defaults {
+    name: "latest_android_hardware_bluetooth_audio_ndk_static",
+    static_libs: [
+        latest_android_hardware_bluetooth_audio + "-ndk",
+    ],
+}
diff --git a/bluetooth/audio/aidl/default/Android.bp b/bluetooth/audio/aidl/default/Android.bp
index e4c2844..40aea32 100644
--- a/bluetooth/audio/aidl/default/Android.bp
+++ b/bluetooth/audio/aidl/default/Android.bp
@@ -11,6 +11,9 @@
     name: "android.hardware.bluetooth.audio-impl",
     vendor: true,
     vintf_fragments: ["bluetooth_audio.xml"],
+    defaults: [
+        "latest_android_hardware_bluetooth_audio_ndk_shared",
+    ],
     srcs: [
         "BluetoothAudioProvider.cpp",
         "BluetoothAudioProviderFactory.cpp",
@@ -29,7 +32,6 @@
         "libcutils",
         "libfmq",
         "liblog",
-        "android.hardware.bluetooth.audio-V3-ndk",
         "libbluetooth_audio_session_aidl",
     ],
 }
diff --git a/bluetooth/audio/aidl/default/bluetooth_audio.xml b/bluetooth/audio/aidl/default/bluetooth_audio.xml
index c0bc55e..3561dd1 100644
--- a/bluetooth/audio/aidl/default/bluetooth_audio.xml
+++ b/bluetooth/audio/aidl/default/bluetooth_audio.xml
@@ -1,7 +1,7 @@
 <manifest version="1.0" type="device">
     <hal format="aidl">
         <name>android.hardware.bluetooth.audio</name>
-        <version>3</version>
+        <version>4</version>
         <fqname>IBluetoothAudioProviderFactory/default</fqname>
     </hal>
 </manifest>
diff --git a/bluetooth/audio/aidl/vts/Android.bp b/bluetooth/audio/aidl/vts/Android.bp
index fa85fa8..884062a 100644
--- a/bluetooth/audio/aidl/vts/Android.bp
+++ b/bluetooth/audio/aidl/vts/Android.bp
@@ -10,17 +10,17 @@
 cc_test {
     name: "VtsHalBluetoothAudioTargetTest",
     defaults: [
+        "latest_android_hardware_audio_common_ndk_static",
+        "latest_android_hardware_bluetooth_audio_ndk_static",
+        "latest_android_media_audio_common_types_ndk_static",
         "VtsHalTargetTestDefaults",
         "use_libaidlvintf_gtest_helper_static",
     ],
     tidy_timeout_srcs: ["VtsHalBluetoothAudioTargetTest.cpp"],
     srcs: ["VtsHalBluetoothAudioTargetTest.cpp"],
     static_libs: [
-        "android.hardware.audio.common-V2-ndk",
-        "android.hardware.bluetooth.audio-V3-ndk",
         "android.hardware.common-V2-ndk",
         "android.hardware.common.fmq-V1-ndk",
-        "android.media.audio.common.types-V2-ndk",
     ],
     shared_libs: [
         "libbase",
diff --git a/bluetooth/audio/utils/Android.bp b/bluetooth/audio/utils/Android.bp
index a09e7fe..75081d6 100644
--- a/bluetooth/audio/utils/Android.bp
+++ b/bluetooth/audio/utils/Android.bp
@@ -48,6 +48,9 @@
         "libhardware_headers",
         "libxsdc-utils",
     ],
+    defaults: [
+        "latest_android_hardware_bluetooth_audio_ndk_shared",
+    ],
     shared_libs: [
         "android.hardware.bluetooth.audio@2.0",
         "android.hardware.bluetooth.audio@2.1",
@@ -56,7 +59,6 @@
         "libbinder_ndk",
         "libfmq",
         "liblog",
-        "android.hardware.bluetooth.audio-V3-ndk",
         "libhidlbase",
         "libxml2",
     ],
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
index 3ed9e07..b4cba49 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
@@ -20,8 +20,6 @@
 
 #include <aidl/android/hardware/bluetooth/audio/AacCapabilities.h>
 #include <aidl/android/hardware/bluetooth/audio/AacObjectType.h>
-#include <aidl/android/hardware/bluetooth/audio/AptxAdaptiveLeCapabilities.h>
-#include <aidl/android/hardware/bluetooth/audio/AptxAdaptiveLeConfiguration.h>
 #include <aidl/android/hardware/bluetooth/audio/AptxCapabilities.h>
 #include <aidl/android/hardware/bluetooth/audio/ChannelMode.h>
 #include <aidl/android/hardware/bluetooth/audio/LdacCapabilities.h>
@@ -100,55 +98,6 @@
 
 std::vector<LeAudioCodecCapabilitiesSetting> kDefaultOffloadLeAudioCapabilities;
 
-static const UnicastCapability kInvalidUnicastCapability = {
-    .codecType = CodecType::UNKNOWN};
-
-static const AptxAdaptiveLeCapabilities
-    kDefaultOffloadAptxAdaptiveLeCapability_48k = {
-        .samplingFrequencyHz = {48000},
-        .frameDurationUs = {10000},
-        .octetsPerFrame = {816}};
-
-static const AptxAdaptiveLeCapabilities
-    kDefaultOffloadAptxAdaptiveLeCapability_96k = {
-        .samplingFrequencyHz = {96000},
-        .frameDurationUs = {10000},
-        .octetsPerFrame = {816}};
-
-static const AptxAdaptiveLeCapabilities
-    kDefaultOffloadAptxAdaptiveLeXCapability_48k = {
-        .samplingFrequencyHz = {48000},
-        .frameDurationUs = {10000},
-        .octetsPerFrame = {816}};
-
-static const AptxAdaptiveLeCapabilities
-    kDefaultOffloadAptxAdaptiveLeXCapability_96k = {
-        .samplingFrequencyHz = {96000},
-        .frameDurationUs = {10000},
-        .octetsPerFrame = {816}};
-
-static const BroadcastCapability kInvalidBroadcastCapability = {
-    .codecType = CodecType::UNKNOWN};
-
-static AudioLocation stereoAudio = static_cast<AudioLocation>(
-    static_cast<uint8_t>(AudioLocation::FRONT_LEFT) |
-    static_cast<uint8_t>(AudioLocation::FRONT_RIGHT));
-
-static const std::vector<AptxAdaptiveLeCapabilities>
-    supportedAptxAdaptiveLeCapabilityList = {
-        kDefaultOffloadAptxAdaptiveLeCapability_48k,
-        kDefaultOffloadAptxAdaptiveLeCapability_96k,
-        kDefaultOffloadAptxAdaptiveLeXCapability_48k,
-        kDefaultOffloadAptxAdaptiveLeXCapability_96k};
-
-// Stores the supported setting of audio location, connected device, and the
-// channel count for each device
-std::vector<std::tuple<AudioLocation, uint8_t, uint8_t>>
-    supportedDeviceSetting = {
-        // Stereo, one connected device for both L and R
-        std::make_tuple(stereoAudio, 1, 2),
-};
-
 template <class T>
 bool BluetoothAudioCodecs::ContainedInVector(
     const std::vector<T>& vector, const typename identity<T>::type& target) {
@@ -458,32 +407,6 @@
     kDefaultOffloadLeAudioCapabilities =
         BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities(
             le_audio_offload_setting);
-
-    for (auto [audioLocation, deviceCnt, channelCount] :
-         supportedDeviceSetting) {
-      for (auto capability : supportedAptxAdaptiveLeCapabilityList) {
-        for (auto codec_type :
-             {CodecType::APTX_ADAPTIVE_LE, CodecType::APTX_ADAPTIVE_LEX}) {
-          if (session_type ==
-              SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
-            UnicastCapability aptx_adaptive_le_cap = {
-                .codecType = codec_type,
-                .supportedChannel = audioLocation,
-                .deviceCount = deviceCnt,
-                .channelCountPerDevice = channelCount,
-                .leAudioCodecCapabilities =
-                    UnicastCapability::LeAudioCodecCapabilities(capability),
-            };
-
-            // Adds the capability for encode only
-            kDefaultOffloadLeAudioCapabilities.push_back(
-                {.unicastEncodeCapability = aptx_adaptive_le_cap,
-                 .unicastDecodeCapability = kInvalidUnicastCapability,
-                 .broadcastCapability = kInvalidBroadcastCapability});
-          }
-        }
-      }
-    }
   }
   return kDefaultOffloadLeAudioCapabilities;
 }
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp
index 0bcafa3..e5cf53c 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp
@@ -588,15 +588,32 @@
                << " has NO session";
     return std::vector<LatencyMode>();
   }
-  if (low_latency_allowed_) return latency_modes_;
-  std::vector<LatencyMode> modes;
-  for (LatencyMode mode : latency_modes_) {
-    if (mode == LatencyMode::LOW_LATENCY)
-      // ignore those low latency mode if Bluetooth stack doesn't allow
-      continue;
-    modes.push_back(mode);
+
+  std::vector<LatencyMode> supported_latency_modes;
+  if (session_type_ ==
+      SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
+    for (LatencyMode mode : latency_modes_) {
+      if (mode == LatencyMode::LOW_LATENCY) {
+        // LOW_LATENCY is not supported for LE_HARDWARE_OFFLOAD_ENC sessions
+        continue;
+      }
+      supported_latency_modes.push_back(mode);
+    }
+  } else {
+    for (LatencyMode mode : latency_modes_) {
+      if (!low_latency_allowed_ && mode == LatencyMode::LOW_LATENCY) {
+        // ignore LOW_LATENCY mode if Bluetooth stack doesn't allow
+        continue;
+      }
+      if (mode == LatencyMode::DYNAMIC_SPATIAL_AUDIO_SOFTWARE ||
+          mode == LatencyMode::DYNAMIC_SPATIAL_AUDIO_HARDWARE) {
+        // DSA_SW and DSA_HW only supported for LE_HARDWARE_OFFLOAD_ENC sessions
+        continue;
+      }
+      supported_latency_modes.push_back(mode);
+    }
   }
-  return modes;
+  return supported_latency_modes;
 }
 
 void BluetoothAudioSession::SetLatencyMode(const LatencyMode& latency_mode) {
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp
index 0a804bb..26da5fb 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp
+++ b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp
@@ -256,6 +256,15 @@
         strategy_configuration_iter->second.getConnectedDevice(),
         strategy_configuration_iter->second.getChannelCount(),
         ComposeLc3Capability(codec_configuration_iter->second));
+  } else if (codec_type == CodecType::APTX_ADAPTIVE_LE ||
+             codec_type == CodecType::APTX_ADAPTIVE_LEX) {
+    return ComposeUnicastCapability(
+        codec_type,
+        GetAudioLocation(
+            strategy_configuration_iter->second.getAudioLocation()),
+        strategy_configuration_iter->second.getConnectedDevice(),
+        strategy_configuration_iter->second.getChannelCount(),
+        ComposeAptxAdaptiveLeCapability(codec_configuration_iter->second));
   }
   return {.codecType = CodecType::UNKNOWN};
 }
@@ -330,6 +339,14 @@
           .octetsPerFrame = {codec_configuration.getOctetsPerCodecFrame()}};
 }
 
+AptxAdaptiveLeCapabilities
+BluetoothLeAudioCodecsProvider::ComposeAptxAdaptiveLeCapability(
+    const setting::CodecConfiguration& codec_configuration) {
+  return {.samplingFrequencyHz = {codec_configuration.getSamplingFrequency()},
+          .frameDurationUs = {codec_configuration.getFrameDurationUs()},
+          .octetsPerFrame = {codec_configuration.getOctetsPerCodecFrame()}};
+}
+
 AudioLocation BluetoothLeAudioCodecsProvider::GetAudioLocation(
     const setting::AudioLocation& audio_location) {
   switch (audio_location) {
@@ -347,6 +364,10 @@
   switch (codec_type) {
     case setting::CodecType::LC3:
       return CodecType::LC3;
+    case setting::CodecType::APTX_ADAPTIVE_LE:
+      return CodecType::APTX_ADAPTIVE_LE;
+    case setting::CodecType::APTX_ADAPTIVE_LEX:
+      return CodecType::APTX_ADAPTIVE_LEX;
     default:
       return CodecType::UNKNOWN;
   }
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.h b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.h
index 06e4595..654e70c 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.h
+++ b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.h
@@ -84,6 +84,9 @@
   static inline Lc3Capabilities ComposeLc3Capability(
       const setting::CodecConfiguration& codec_configuration);
 
+  static inline AptxAdaptiveLeCapabilities ComposeAptxAdaptiveLeCapability(
+      const setting::CodecConfiguration& codec_configuration);
+
   static inline AudioLocation GetAudioLocation(
       const setting::AudioLocation& audio_location);
   static inline CodecType GetCodecType(const setting::CodecType& codec_type);
diff --git a/bluetooth/audio/utils/le_audio_codec_capabilities/le_audio_codec_capabilities.xml b/bluetooth/audio/utils/le_audio_codec_capabilities/le_audio_codec_capabilities.xml
index c8d1af0..eaace78 100644
--- a/bluetooth/audio/utils/le_audio_codec_capabilities/le_audio_codec_capabilities.xml
+++ b/bluetooth/audio/utils/le_audio_codec_capabilities/le_audio_codec_capabilities.xml
@@ -31,6 +31,10 @@
     <scenario encode="OneChanMono_16_2" decode="invalid"/>
     <scenario encode="TwoChanStereo_16_2" decode="invalid"/>
     <scenario encode="OneChanStereo_16_2" decode="invalid"/>
+    <scenario encode="APEX_ADAPTIVE_LE_TwoChanStereo_48" decode="invalid"/>
+    <scenario encode="APEX_ADAPTIVE_LE_TwoChanStereo_96" decode="invalid"/>
+    <scenario encode="APEX_ADAPTIVE_LEX_TwoChanStereo_48" decode="invalid"/>
+    <scenario encode="APEX_ADAPTIVE_LEX_TwoChanStereo_96" decode="invalid"/>
     <!-- encode and decode -->
     <scenario encode="OneChanStereo_16_1" decode="OneChanStereo_16_1"/>
     <scenario encode="OneChanStereo_16_1" decode="OneChanMono_16_1"/>
@@ -51,10 +55,18 @@
     <configuration name="TwoChanStereo_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
     <configuration name="OneChanStereo_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="STEREO_ONE_CIS_PER_DEVICE"/>
     <configuration name="BcastStereo_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="BROADCAST_STEREO"/>
+    <configuration name="APEX_ADAPTIVE_LE_TwoChanStereo_48" codecConfiguration="APTX_ADAPTIVE_LE_48k" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
+    <configuration name="APEX_ADAPTIVE_LE_TwoChanStereo_96" codecConfiguration="APTX_ADAPTIVE_LE_96k" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
+    <configuration name="APEX_ADAPTIVE_LEX_TwoChanStereo_48" codecConfiguration="APTX_ADAPTIVE_LEX_48k" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
+    <configuration name="APEX_ADAPTIVE_LEX_TwoChanStereo_96" codecConfiguration="APTX_ADAPTIVE_LEX_96k" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/>
   </configurationList>
   <codecConfigurationList>
     <codecConfiguration name="LC3_16k_1" codec="LC3" samplingFrequency="16000" frameDurationUs="7500" octetsPerCodecFrame="30"/>
     <codecConfiguration name="LC3_16k_2" codec="LC3" samplingFrequency="16000" frameDurationUs="10000" octetsPerCodecFrame="40"/>
+    <codecConfiguration name="APTX_ADAPTIVE_LE_48k" codec="APTX_ADAPTIVE_LE" samplingFrequency="48000" frameDurationUs="10000" octetsPerCodecFrame="816"/>
+    <codecConfiguration name="APTX_ADAPTIVE_LE_96k" codec="APTX_ADAPTIVE_LE" samplingFrequency="96000" frameDurationUs="10000" octetsPerCodecFrame="816"/>
+    <codecConfiguration name="APTX_ADAPTIVE_LEX_48k" codec="APTX_ADAPTIVE_LEX" samplingFrequency="48000" frameDurationUs="10000" octetsPerCodecFrame="816"/>
+    <codecConfiguration name="APTX_ADAPTIVE_LEX_96k" codec="APTX_ADAPTIVE_LEX" samplingFrequency="96000" frameDurationUs="10000" octetsPerCodecFrame="816"/>
   </codecConfigurationList>
   <strategyConfigurationList>
     <strategyConfiguration name="STEREO_ONE_CIS_PER_DEVICE" audioLocation="STEREO" connectedDevice="2" channelCount="1"/>
diff --git a/bluetooth/audio/utils/le_audio_codec_capabilities/le_audio_codec_capabilities.xsd b/bluetooth/audio/utils/le_audio_codec_capabilities/le_audio_codec_capabilities.xsd
index 8c2d6a1..03c8ade 100644
--- a/bluetooth/audio/utils/le_audio_codec_capabilities/le_audio_codec_capabilities.xsd
+++ b/bluetooth/audio/utils/le_audio_codec_capabilities/le_audio_codec_capabilities.xsd
@@ -70,6 +70,8 @@
   <xs:simpleType name="codecType">
     <xs:restriction base="xs:string">
       <xs:enumeration value="LC3"/>
+      <xs:enumeration value="APTX_ADAPTIVE_LE"/>
+      <xs:enumeration value="APTX_ADAPTIVE_LEX"/>
     </xs:restriction>
   </xs:simpleType>
 </xs:schema>
diff --git a/bluetooth/audio/utils/le_audio_codec_capabilities/schema/current.txt b/bluetooth/audio/utils/le_audio_codec_capabilities/schema/current.txt
index 3cef417..a882174 100644
--- a/bluetooth/audio/utils/le_audio_codec_capabilities/schema/current.txt
+++ b/bluetooth/audio/utils/le_audio_codec_capabilities/schema/current.txt
@@ -32,6 +32,8 @@
 
   public enum CodecType {
     method public String getRawName();
+    enum_constant public static final aidl.android.hardware.bluetooth.audio.setting.CodecType APTX_ADAPTIVE_LE;
+    enum_constant public static final aidl.android.hardware.bluetooth.audio.setting.CodecType APTX_ADAPTIVE_LEX;
     enum_constant public static final aidl.android.hardware.bluetooth.audio.setting.CodecType LC3;
   }
 
diff --git a/broadcastradio/aidl/Android.bp b/broadcastradio/aidl/Android.bp
index 3f89029..e8bc5eb 100644
--- a/broadcastradio/aidl/Android.bp
+++ b/broadcastradio/aidl/Android.bp
@@ -43,6 +43,6 @@
             imports: [],
         },
     ],
-    frozen: true,
+    frozen: false,
 
 }
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/AmFmRegionConfig.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/AmFmRegionConfig.aidl
index fe8489c..b96def3 100644
--- a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/AmFmRegionConfig.aidl
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/AmFmRegionConfig.aidl
@@ -37,8 +37,8 @@
   android.hardware.broadcastradio.AmFmBandRange[] ranges;
   int fmDeemphasis;
   int fmRds;
-  const int DEEMPHASIS_D50 = (1 << 0);
-  const int DEEMPHASIS_D75 = (1 << 1);
-  const int RDS = (1 << 0);
-  const int RBDS = (1 << 1);
+  const int DEEMPHASIS_D50 = (1 << 0) /* 1 */;
+  const int DEEMPHASIS_D75 = (1 << 1) /* 2 */;
+  const int RDS = (1 << 0) /* 1 */;
+  const int RBDS = (1 << 1) /* 2 */;
 }
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ConfigFlag.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ConfigFlag.aidl
index 98af437..d6d33bc 100644
--- a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ConfigFlag.aidl
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ConfigFlag.aidl
@@ -35,6 +35,9 @@
 @Backing(type="int") @JavaDerive(equals=true, toString=true) @VintfStability
 enum ConfigFlag {
   FORCE_MONO = 1,
+  /**
+   * @deprecated Use {link #FORCE_ANALOG_FM} instead
+   */
   FORCE_ANALOG,
   FORCE_DIGITAL,
   RDS_AF,
@@ -43,4 +46,6 @@
   DAB_FM_LINKING,
   DAB_DAB_SOFT_LINKING,
   DAB_FM_SOFT_LINKING,
+  FORCE_ANALOG_FM,
+  FORCE_ANALOG_AM,
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/HdSubChannel.aidl
similarity index 84%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
copy to broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/HdSubChannel.aidl
index b9df1f5..dd06134 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/HdSubChannel.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,15 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
-/* @hide */
-@Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+package android.hardware.broadcastradio;
+@Backing(type="int") @JavaDerive(equals=true, toString=true) @VintfStability
+enum HdSubChannel {
+  HD1 = 0,
+  HD2 = 1,
+  HD3 = 2,
+  HD4 = 3,
+  HD5 = 4,
+  HD6 = 5,
+  HD7 = 6,
+  HD8 = 7,
 }
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/IdentifierType.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/IdentifierType.aidl
index 4df272c..ed41af0 100644
--- a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/IdentifierType.aidl
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/IdentifierType.aidl
@@ -47,6 +47,13 @@
   DAB_FREQUENCY_KHZ,
   DRMO_SERVICE_ID,
   DRMO_FREQUENCY_KHZ,
-  SXM_SERVICE_ID = (DRMO_FREQUENCY_KHZ + 2),
+  /**
+   * @deprecated SiriusXM Satellite Radio is not supported.
+   */
+  SXM_SERVICE_ID = (DRMO_FREQUENCY_KHZ + 2) /* 12 */,
+  /**
+   * @deprecated SiriusXM Satellite Radio is not supported.
+   */
   SXM_CHANNEL,
+  HD_STATION_LOCATION,
 }
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Metadata.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Metadata.aidl
index e02b6b1..b4a1efa 100644
--- a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Metadata.aidl
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Metadata.aidl
@@ -50,4 +50,12 @@
   String dabServiceNameShort;
   String dabComponentName;
   String dabComponentNameShort;
+  String genre;
+  String commentShortDescription;
+  String commentActualText;
+  String commercial;
+  String[] ufids;
+  String hdStationNameShort;
+  String hdStationNameLong;
+  int hdSubChannelsAvailable;
 }
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramInfo.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramInfo.aidl
index b14023a..997cdd7 100644
--- a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramInfo.aidl
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramInfo.aidl
@@ -42,10 +42,13 @@
   int signalQuality;
   android.hardware.broadcastradio.Metadata[] metadata;
   android.hardware.broadcastradio.VendorKeyValue[] vendorInfo;
-  const int FLAG_LIVE = (1 << 0);
-  const int FLAG_MUTED = (1 << 1);
-  const int FLAG_TRAFFIC_PROGRAM = (1 << 2);
-  const int FLAG_TRAFFIC_ANNOUNCEMENT = (1 << 3);
-  const int FLAG_TUNABLE = (1 << 4);
-  const int FLAG_STEREO = (1 << 5);
+  const int FLAG_LIVE = (1 << 0) /* 1 */;
+  const int FLAG_MUTED = (1 << 1) /* 2 */;
+  const int FLAG_TRAFFIC_PROGRAM = (1 << 2) /* 4 */;
+  const int FLAG_TRAFFIC_ANNOUNCEMENT = (1 << 3) /* 8 */;
+  const int FLAG_TUNABLE = (1 << 4) /* 16 */;
+  const int FLAG_STEREO = (1 << 5) /* 32 */;
+  const int FLAG_SIGNAL_ACQUISITION = (1 << 6) /* 64 */;
+  const int FLAG_HD_SIS_ACQUISITION = (1 << 7) /* 128 */;
+  const int FLAG_HD_AUDIO_ACQUISITION = (1 << 8) /* 256 */;
 }
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Result.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Result.aidl
index 8af74c7..b0fc018 100644
--- a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Result.aidl
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Result.aidl
@@ -34,7 +34,7 @@
 package android.hardware.broadcastradio;
 @Backing(type="int") @JavaDerive(equals=true, toString=true) @VintfStability
 enum Result {
-  OK,
+  OK = 0,
   INTERNAL_ERROR,
   INVALID_ARGUMENTS,
   INVALID_STATE,
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/ConfigFlag.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/ConfigFlag.aidl
index 11da39c..ddf60e0 100644
--- a/broadcastradio/aidl/android/hardware/broadcastradio/ConfigFlag.aidl
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/ConfigFlag.aidl
@@ -36,10 +36,12 @@
      * Forces the analog playback for the supporting radio technology.
      *
      * User may disable digital playback for FM HD Radio or hybrid FM/DAB with
-     * this option. This is purely user choice, ie. does not reflect digital-
+     * this option. This is purely user choice, i.e. does not reflect digital-
      * analog handover state managed from the HAL implementation side.
      *
-     * Some radio technologies may not support this, ie. DAB.
+     * Some radio technologies may not support this, i.e. DAB.
+     *
+     * @deprecated Use {link #FORCE_ANALOG_FM} instead
      */
     FORCE_ANALOG,
 
@@ -89,4 +91,26 @@
      * Enables DAB-FM soft-linking (related content).
      */
     DAB_FM_SOFT_LINKING,
+
+    /**
+     * Forces the FM analog playback for the supporting radio technology.
+     *
+     * User may disable FM digital playback for FM HD Radio or hybrid FM/DAB
+     * with this option. This is purely user choice, i.e. does not reflect
+     * digital-analog handover state managed from the HAL implementation side.
+     *
+     * Some radio technologies may not support this, i.e. DAB.
+     */
+    FORCE_ANALOG_FM,
+
+    /**
+     * Forces the AM analog playback for the supporting radio technology.
+     *
+     * User may disable AM digital playback for AM HD Radio or hybrid AM/DAB
+     * with this option. This is purely user choice, i.e. does not reflect
+     * digital-analog handover state managed from the HAL implementation side.
+     *
+     * Some radio technologies may not support this, i.e. DAB.
+     */
+    FORCE_ANALOG_AM,
 }
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/HdSubChannel.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/HdSubChannel.aidl
new file mode 100644
index 0000000..46a3e0c
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/HdSubChannel.aidl
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.broadcastradio;
+
+/**
+ * Index of HD radio subchannel.
+ */
+@VintfStability
+@Backing(type="int")
+@JavaDerive(equals=true, toString=true)
+enum HdSubChannel {
+    /**
+     * Index of HD radio subchannel 1.
+     *
+     * <p>There are at most 8 HD radio subchannels of 1-based om HD radio standard. It is
+     * converted to 0-based index. 0 is the index of main program service (MPS). 1 to 7
+     * are indexes of additional supplemental program services (SPS).
+     */
+    HD1 = 0,
+    /**
+     * {@see HD1}
+     */
+    HD2 = 1,
+    /**
+     * {@see HD1}
+     */
+    HD3 = 2,
+    /**
+     * {@see HD1}
+     */
+    HD4 = 3,
+    /**
+     * {@see HD1}
+     */
+    HD5 = 4,
+    /**
+     * {@see HD1}
+     */
+    HD6 = 5,
+    /**
+     * {@see HD1}
+     */
+    HD7 = 6,
+    /**
+     * {@see HD1}
+     */
+    HD8 = 7,
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl
index 646c502..4a95a41 100644
--- a/broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl
@@ -154,11 +154,42 @@
 
     /**
      * 32bit primary identifier for SiriusXM Satellite Radio.
+     *
+     * @deprecated SiriusXM Satellite Radio is not supported.
      */
     SXM_SERVICE_ID = DRMO_FREQUENCY_KHZ + 2,
 
     /**
      * 0-999 range
+     *
+     * @deprecated SiriusXM Satellite Radio is not supported.
      */
     SXM_CHANNEL,
+
+    /**
+     * 64bit additional identifier for HD Radio representing station location.
+     *
+     * Consists of (from the LSB):
+     * - 4 bit: Bits 0:3 of altitude
+     * - 13 bit: Fractional bits of longitude
+     * - 8 bit: Integer bits of longitude
+     * - 1 bit: 0 for east and 1 for west for longitude
+     * - 1 bit: 0, representing latitude
+     * - 5 bit: pad of zeros separating longitude and latitude
+     * - 4 bit: Bits 4:7 of altitude
+     * - 13 bit: Fractional bits of latitude
+     * - 8 bit: Integer bits of latitude
+     * - 1 bit: 0 for north and 1 for south for latitude
+     * - 1 bit: 1, representing latitude
+     * - 5 bit: pad of zeros
+     *
+     * This format is defined in NRSC-5-C document: SY_IDD_1020s.
+     *
+     * Due to Station ID abuse, some HD_STATION_ID_EXT identifiers may be not
+     * globally unique. To provide a best-effort solution, the station’s
+     * broadcast antenna containing the latitude and longitude may be carried
+     * as additional identifier and may be used by the tuner hardware to
+     * double-check tuning.
+     */
+    HD_STATION_LOCATION,
 }
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/Metadata.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/Metadata.aidl
index 7769b8c..0ce967f 100644
--- a/broadcastradio/aidl/android/hardware/broadcastradio/Metadata.aidl
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/Metadata.aidl
@@ -116,4 +116,70 @@
      * <p>Note: The string must be up to 8 characters long.
      */
     String dabComponentNameShort;
+
+    /**
+     * Genre of the current audio piece (string)
+     *
+     * <p>(see NRSC-G200-A and id3v2.3.0 for more info)
+     */
+    String genre;
+
+    /**
+     * Short context description of comment (string)
+     *
+     * <p>Comment could relate to the current audio program content, or it might
+     * be unrelated information that the station chooses to send. It is
+     * composed of short content description and actual text (see NRSC-G200-A
+     * and id3v2.3.0 for more info).
+     */
+    String commentShortDescription;
+
+    /**
+     * Actual text of comment (string)
+     *
+     * @see #commentShortDescription
+     */
+    String commentActualText;
+
+    /**
+     * Commercial (string)
+     *
+     * <p>Commercial is application specific and generally used to facilitate the
+     * sale of products and services (see NRSC-G200-A and id3v2.3.0 for more info).
+     */
+    String commercial;
+
+    /**
+     * HD Unique File Identifiers (Array of strings)
+     *
+     * <p>Unique File Identifier (UFID) can be used to transmit an alphanumeric
+     * identifier of the current content, or of an advertised product or service
+     * (see NRSC-G200-A and id3v2.3.0 for more info).
+     */
+    String[] ufids;
+
+    /**
+     * HD short station name or HD universal short station name
+     *
+     * <p>It can be up to 12 characters (see SY_IDD_1020s for more info).
+     */
+    String hdStationNameShort;
+
+    /**
+     * HD long station name, HD station slogan or HD station message
+     *
+     * <p>(see SY_IDD_1020s for more info)
+     */
+    String hdStationNameLong;
+
+    /**
+     * Bit mask of all HD Radio subchannels available (uint8_t)
+     *
+     * <p>Bit {@link HdSubChannel#HD1} from LSB represents the availability
+     * of HD-1 subchannel (main program service, MPS). Bits
+     * {@link HdSubChannel#HD2} to {@link HdSubChannel#HD8} from LSB represent
+     * HD-2 to HD-8 subchannel (supplemental program services, SPS)
+     * respectively.
+     */
+    int hdSubChannelsAvailable;
 }
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl
index 7632c81..d4ccd01 100644
--- a/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl
@@ -71,6 +71,23 @@
     const int FLAG_STEREO = 1 << 5;
 
     /**
+     * A signal has been acquired if this bit is set.
+     */
+
+    const int FLAG_SIGNAL_ACQUISITION = 1 << 6;
+    /**
+     * An HD Station Information Service (SIS) information is available if this
+     * bit is set.
+     */
+
+    const int FLAG_HD_SIS_ACQUISITION = 1 << 7;
+
+    /**
+     * An HD digital audio is available if this bit is set.
+     */
+    const int FLAG_HD_AUDIO_ACQUISITION = 1 << 8;
+
+    /**
      * An identifier used to point at the program (primarily to tune to it).
      *
      * This field is required - its type field must not be set to
@@ -153,7 +170,8 @@
      *
      * It can be a combination of {@link #FLAG_LIVE}, {@link #FLAG_MUTED},
      * {@link #FLAG_TRAFFIC_PROGRAM}, {@link #FLAG_TRAFFIC_ANNOUNCEMENT},
-     * {@link #FLAG_TUNABLE}, and {@link #FLAG_STEREO}.
+     * {@link #FLAG_TUNABLE}, {@link #FLAG_STEREO}, {@link #FLAG_SIGNAL_ACQUISITION},
+     * {@link #FLAG_HD_SIS_ACQUISITION}, and {@link #FLAG_HD_AUDIO_ACQUISITION}.
      */
     int infoFlags;
 
diff --git a/broadcastradio/aidl/default/Android.bp b/broadcastradio/aidl/default/Android.bp
index 1d1bef7..743365a 100644
--- a/broadcastradio/aidl/default/Android.bp
+++ b/broadcastradio/aidl/default/Android.bp
@@ -26,11 +26,11 @@
 cc_defaults {
     name: "BroadcastRadioHalDefaults",
     static_libs: [
-        "android.hardware.broadcastradio@common-utils-aidl-lib",
+        "android.hardware.broadcastradio@common-utils-aidl-lib-V2",
         "android.hardware.broadcastradio@common-utils-lib",
     ],
     shared_libs: [
-        "android.hardware.broadcastradio-V1-ndk",
+        "android.hardware.broadcastradio-V2-ndk",
         "libbase",
         "libbinder_ndk",
         "liblog",
@@ -62,7 +62,7 @@
 
 cc_library {
     name: "DefaultBroadcastRadioHal",
-    vendor: true,
+    vendor_available: true,
     export_include_dirs: ["."],
     defaults: [
         "BroadcastRadioHalDefaults",
@@ -76,14 +76,15 @@
 
 cc_fuzz {
     name: "android.hardware.broadcastradio-service.default_fuzzer",
-    vendor: true,
+    // TODO(b/307611931): avoid fuzzing on vendor until hermiticity issue is fixed
+    // vendor: true,
     defaults: [
         "BroadcastRadioHalDefaults",
         "service_fuzzer_defaults",
     ],
     static_libs: [
         "DefaultBroadcastRadioHal",
-        "android.hardware.broadcastradio-V1-ndk",
+        "android.hardware.broadcastradio-V2-ndk",
     ],
     srcs: [
         "fuzzer.cpp",
diff --git a/broadcastradio/aidl/default/BroadcastRadio.cpp b/broadcastradio/aidl/default/BroadcastRadio.cpp
index 8584921..63073d7 100644
--- a/broadcastradio/aidl/default/BroadcastRadio.cpp
+++ b/broadcastradio/aidl/default/BroadcastRadio.cpp
@@ -16,6 +16,7 @@
 
 #include "BroadcastRadio.h"
 #include <broadcastradio-utils-aidl/Utils.h>
+#include <broadcastradio-utils-aidl/UtilsV2.h>
 #include "resources.h"
 
 #include <aidl/android/hardware/broadcastradio/IdentifierType.h>
@@ -221,7 +222,7 @@
                 resultToInt(Result::NOT_SUPPORTED), "selector is not supported");
     }
 
-    if (!utils::isValid(program)) {
+    if (!utils::isValidV2(program)) {
         LOG(ERROR) << __func__ << ": selector is not valid: " << program.toString();
         return ScopedAStatus::fromServiceSpecificErrorWithMessage(
                 resultToInt(Result::INVALID_ARGUMENTS), "selector is not valid");
@@ -328,7 +329,7 @@
     std::optional<AmFmBandRange> range = getAmFmRangeLocked();
     if (!range) {
         LOG(ERROR) << __func__ << ": can't find current band or tune operation is in process";
-        ScopedAStatus::fromServiceSpecificErrorWithMessage(
+        return ScopedAStatus::fromServiceSpecificErrorWithMessage(
                 resultToInt(Result::INTERNAL_ERROR),
                 "can't find current band or tune operation is in process");
     }
diff --git a/broadcastradio/aidl/default/broadcastradio-default.xml b/broadcastradio/aidl/default/broadcastradio-default.xml
index 1555822..a57b724 100644
--- a/broadcastradio/aidl/default/broadcastradio-default.xml
+++ b/broadcastradio/aidl/default/broadcastradio-default.xml
@@ -1,6 +1,7 @@
 <manifest version="1.0" type="device">
     <hal format="aidl">
         <name>android.hardware.broadcastradio</name>
+        <version>2</version>
         <fqname>IBroadcastRadio/amfm</fqname>
         <fqname>IBroadcastRadio/dab</fqname>
     </hal>
diff --git a/broadcastradio/aidl/vts/Android.bp b/broadcastradio/aidl/vts/Android.bp
index b60387e..87e48a9 100644
--- a/broadcastradio/aidl/vts/Android.bp
+++ b/broadcastradio/aidl/vts/Android.bp
@@ -35,8 +35,8 @@
         "libxml2",
     ],
     static_libs: [
-        "android.hardware.broadcastradio-V1-ndk",
-        "android.hardware.broadcastradio@common-utils-aidl-lib",
+        "android.hardware.broadcastradio-V2-ndk",
+        "android.hardware.broadcastradio@common-utils-aidl-lib-V2",
         "android.hardware.broadcastradio@vts-utils-lib",
         "libgmock",
     ],
diff --git a/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp b/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp
index 790d60b..72869cc 100644
--- a/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp
+++ b/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp
@@ -32,6 +32,7 @@
 #include <aidl/Gtest.h>
 #include <aidl/Vintf.h>
 #include <broadcastradio-utils-aidl/Utils.h>
+#include <broadcastradio-utils-aidl/UtilsV2.h>
 #include <cutils/bitops.h>
 #include <gmock/gmock.h>
 
@@ -50,7 +51,6 @@
 using ::aidl::android::hardware::broadcastradio::utils::resultToInt;
 using ::ndk::ScopedAStatus;
 using ::ndk::SharedRefBase;
-using ::std::string;
 using ::std::vector;
 using ::testing::_;
 using ::testing::AnyNumber;
@@ -73,20 +73,29 @@
         ConfigFlag::DAB_FM_SOFT_LINKING,
 };
 
-void printSkipped(const string& msg) {
+constexpr int32_t kAidlVersion1 = 1;
+constexpr int32_t kAidlVersion2 = 2;
+
+void printSkipped(const std::string& msg) {
     const auto testInfo = testing::UnitTest::GetInstance()->current_test_info();
     LOG(INFO) << "[  SKIPPED ] " << testInfo->test_case_name() << "." << testInfo->name()
               << " with message: " << msg;
 }
 
-bool isValidAmFmFreq(int64_t freq) {
+bool isValidAmFmFreq(int64_t freq, int aidlVersion) {
     ProgramIdentifier id = bcutils::makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, freq);
-    return bcutils::isValid(id);
+    if (aidlVersion == kAidlVersion1) {
+        return bcutils::isValid(id);
+    } else if (aidlVersion == kAidlVersion2) {
+        return bcutils::isValidV2(id);
+    }
+    LOG(ERROR) << "Unknown AIDL version " << aidlVersion;
+    return false;
 }
 
-void validateRange(const AmFmBandRange& range) {
-    EXPECT_TRUE(isValidAmFmFreq(range.lowerBound));
-    EXPECT_TRUE(isValidAmFmFreq(range.upperBound));
+void validateRange(const AmFmBandRange& range, int aidlVersion) {
+    EXPECT_TRUE(isValidAmFmFreq(range.lowerBound, aidlVersion));
+    EXPECT_TRUE(isValidAmFmFreq(range.upperBound, aidlVersion));
     EXPECT_LT(range.lowerBound, range.upperBound);
     EXPECT_GT(range.spacing, 0u);
     EXPECT_EQ((range.upperBound - range.lowerBound) % range.spacing, 0u);
@@ -142,7 +151,7 @@
 
 class TunerCallbackImpl final : public BnTunerCallback {
   public:
-    TunerCallbackImpl();
+    explicit TunerCallbackImpl(int32_t aidlVersion);
     ScopedAStatus onTuneFailed(Result result, const ProgramSelector& selector) override;
     ScopedAStatus onCurrentProgramInfoChanged(const ProgramInfo& info) override;
     ScopedAStatus onProgramListUpdated(const ProgramListChunk& chunk) override;
@@ -160,6 +169,7 @@
 
   private:
     std::mutex mLock;
+    int32_t mCallbackAidlVersion;
     bool mAntennaConnectionState GUARDED_BY(mLock);
     ProgramInfo mCurrentProgramInfo GUARDED_BY(mLock);
     bcutils::ProgramInfoSet mProgramList GUARDED_BY(mLock);
@@ -171,7 +181,7 @@
     MOCK_METHOD1(onListUpdated, ScopedAStatus(const vector<Announcement>&));
 };
 
-class BroadcastRadioHalTest : public testing::TestWithParam<string> {
+class BroadcastRadioHalTest : public testing::TestWithParam<std::string> {
   protected:
     void SetUp() override;
     void TearDown() override;
@@ -183,14 +193,17 @@
     std::shared_ptr<IBroadcastRadio> mModule;
     Properties mProperties;
     std::shared_ptr<TunerCallbackImpl> mCallback;
+    int32_t mAidlVersion;
 };
 
-MATCHER_P(InfoHasId, id, string(negation ? "does not contain" : "contains") + " " + id.toString()) {
+MATCHER_P(InfoHasId, id,
+          std::string(negation ? "does not contain" : "contains") + " " + id.toString()) {
     vector<int> ids = bcutils::getAllIds(arg.selector, id.type);
     return ids.end() != find(ids.begin(), ids.end(), id.value);
 }
 
-TunerCallbackImpl::TunerCallbackImpl() {
+TunerCallbackImpl::TunerCallbackImpl(int32_t aidlVersion) {
+    mCallbackAidlVersion = aidlVersion;
     mAntennaConnectionState = true;
 }
 
@@ -230,7 +243,12 @@
                 physically > IdentifierType::SXM_CHANNEL);
 
     if (logically == IdentifierType::AMFM_FREQUENCY_KHZ) {
-        std::optional<string> ps = bcutils::getMetadataString(info, Metadata::rdsPs);
+        std::optional<std::string> ps;
+        if (mCallbackAidlVersion == kAidlVersion1) {
+            ps = bcutils::getMetadataString(info, Metadata::rdsPs);
+        } else {
+            ps = bcutils::getMetadataStringV2(info, Metadata::rdsPs);
+        }
         if (ps.has_value()) {
             EXPECT_NE(::android::base::Trim(*ps), "")
                     << "Don't use empty RDS_PS as an indicator of missing RSD PS data.";
@@ -323,9 +341,13 @@
     EXPECT_FALSE(mProperties.product.empty());
     EXPECT_GT(mProperties.supportedIdentifierTypes.size(), 0u);
 
-    mCallback = SharedRefBase::make<TunerCallbackImpl>();
+    // get AIDL HAL version
+    ASSERT_TRUE(mModule->getInterfaceVersion(&mAidlVersion).isOk());
+    EXPECT_GE(mAidlVersion, kAidlVersion1);
+    EXPECT_LE(mAidlVersion, kAidlVersion2);
 
     // set callback
+    mCallback = SharedRefBase::make<TunerCallbackImpl>(mAidlVersion);
     EXPECT_TRUE(mModule->setTunerCallback(mCallback).isOk());
 }
 
@@ -443,7 +465,7 @@
 
     EXPECT_GT(config.ranges.size(), 0u);
     for (const auto& range : config.ranges) {
-        validateRange(range);
+        validateRange(range, mAidlVersion);
         EXPECT_EQ(range.seekSpacing % range.spacing, 0u);
         EXPECT_GE(range.seekSpacing, range.spacing);
     }
@@ -494,7 +516,7 @@
     EXPECT_GT(config.ranges.size(), 0u);
 
     for (const auto& range : config.ranges) {
-        validateRange(range);
+        validateRange(range, mAidlVersion);
         EXPECT_EQ(range.seekSpacing, 0u);
     }
 }
@@ -522,11 +544,17 @@
     std::regex re("^[A-Z0-9][A-Z0-9 ]{0,5}[A-Z0-9]$");
 
     for (const auto& entry : config) {
-        EXPECT_TRUE(std::regex_match(string(entry.label), re));
+        EXPECT_TRUE(std::regex_match(std::string(entry.label), re));
 
         ProgramIdentifier id =
                 bcutils::makeIdentifier(IdentifierType::DAB_FREQUENCY_KHZ, entry.frequencyKhz);
-        EXPECT_TRUE(bcutils::isValid(id));
+        if (mAidlVersion == kAidlVersion1) {
+            EXPECT_TRUE(bcutils::isValid(id));
+        } else if (mAidlVersion == kAidlVersion2) {
+            EXPECT_TRUE(bcutils::isValidV2(id));
+        } else {
+            LOG(ERROR) << "Unknown callback AIDL version " << mAidlVersion;
+        }
     }
 }
 
@@ -1175,10 +1203,21 @@
             continue;
         }
 
-        std::optional<string> name = bcutils::getMetadataString(program, Metadata::programName);
-        if (!name) {
-            name = bcutils::getMetadataString(program, Metadata::rdsPs);
+        std::optional<std::string> name;
+        if (mAidlVersion == kAidlVersion1) {
+            name = bcutils::getMetadataString(program, Metadata::programName);
+            if (!name) {
+                name = bcutils::getMetadataString(program, Metadata::rdsPs);
+            }
+        } else if (mAidlVersion == kAidlVersion2) {
+            name = bcutils::getMetadataStringV2(program, Metadata::programName);
+            if (!name) {
+                name = bcutils::getMetadataStringV2(program, Metadata::rdsPs);
+            }
+        } else {
+            LOG(ERROR) << "Unknown HAL AIDL version " << mAidlVersion;
         }
+
         ASSERT_TRUE(name.has_value());
 
         ProgramIdentifier expectedId = bcutils::makeHdRadioStationName(*name);
diff --git a/broadcastradio/common/utilsaidl/Android.bp b/broadcastradio/common/utilsaidl/Android.bp
index fa6de19..4ec635b 100644
--- a/broadcastradio/common/utilsaidl/Android.bp
+++ b/broadcastradio/common/utilsaidl/Android.bp
@@ -25,6 +25,29 @@
 
 cc_library_static {
     name: "android.hardware.broadcastradio@common-utils-aidl-lib",
+    defaults: [
+        "VtsBroadcastRadioDefaults",
+    ],
+    shared_libs: [
+        "android.hardware.broadcastradio-V1-ndk",
+    ],
+}
+
+cc_library_static {
+    name: "android.hardware.broadcastradio@common-utils-aidl-lib-V2",
+    defaults: [
+        "VtsBroadcastRadioDefaults",
+    ],
+    srcs: [
+        "src/UtilsV2.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.broadcastradio-V2-ndk",
+    ],
+}
+
+cc_defaults {
+    name: "VtsBroadcastRadioDefaults",
     vendor_available: true,
     relative_install_path: "hw",
     cflags: [
@@ -37,11 +60,10 @@
         "-std=c++1z",
     ],
     srcs: [
-        "Utils.cpp",
+        "src/Utils.cpp",
     ],
     export_include_dirs: ["include"],
     shared_libs: [
-        "android.hardware.broadcastradio-V1-ndk",
         "libbase",
     ],
     static_libs: [
diff --git a/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h b/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
index ee85a17..b6fb33f 100644
--- a/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
+++ b/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include <aidl/android/hardware/broadcastradio/IdentifierType.h>
-#include <aidl/android/hardware/broadcastradio/Metadata.h>
 #include <aidl/android/hardware/broadcastradio/ProgramFilter.h>
 #include <aidl/android/hardware/broadcastradio/ProgramIdentifier.h>
 #include <aidl/android/hardware/broadcastradio/ProgramInfo.h>
diff --git a/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/UtilsV2.h b/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/UtilsV2.h
new file mode 100644
index 0000000..e411aa4
--- /dev/null
+++ b/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/UtilsV2.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/broadcastradio/IdentifierType.h>
+#include <aidl/android/hardware/broadcastradio/Metadata.h>
+#include <aidl/android/hardware/broadcastradio/ProgramIdentifier.h>
+#include <aidl/android/hardware/broadcastradio/ProgramInfo.h>
+#include <aidl/android/hardware/broadcastradio/ProgramSelector.h>
+
+namespace aidl::android::hardware::broadcastradio {
+
+namespace utils {
+
+bool isValidV2(const ProgramIdentifier& id);
+bool isValidV2(const ProgramSelector& sel);
+std::optional<std::string> getMetadataStringV2(const ProgramInfo& info, const Metadata::Tag& tag);
+
+}  // namespace utils
+
+}  // namespace aidl::android::hardware::broadcastradio
diff --git a/broadcastradio/common/utilsaidl/Utils.cpp b/broadcastradio/common/utilsaidl/src/Utils.cpp
similarity index 95%
rename from broadcastradio/common/utilsaidl/Utils.cpp
rename to broadcastradio/common/utilsaidl/src/Utils.cpp
index de4f529..2875318 100644
--- a/broadcastradio/common/utilsaidl/Utils.cpp
+++ b/broadcastradio/common/utilsaidl/src/Utils.cpp
@@ -31,7 +31,6 @@
 namespace {
 
 using ::android::base::EqualsIgnoreCase;
-using ::std::string;
 using ::std::vector;
 
 const int64_t kValueForNotFoundIdentifier = 0;
@@ -207,7 +206,7 @@
     uint64_t val = static_cast<uint64_t>(id.value);
     bool valid = true;
 
-    auto expect = [&valid](bool condition, const string& message) {
+    auto expect = [&valid](bool condition, const std::string& message) {
         if (!condition) {
             valid = false;
             LOG(ERROR) << "identifier not valid, expected " << message;
@@ -278,9 +277,9 @@
         case IdentifierType::SXM_CHANNEL:
             expect(val < 1000u, "SXM channel < 1000");
             break;
-        case IdentifierType::VENDOR_START:
-        case IdentifierType::VENDOR_END:
-            // skip
+        default:
+            expect(id.type >= IdentifierType::VENDOR_START && id.type <= IdentifierType::VENDOR_END,
+                   "Undefined identifier type");
             break;
     }
 
@@ -452,10 +451,10 @@
     return metadataString;
 }
 
-ProgramIdentifier makeHdRadioStationName(const string& name) {
+ProgramIdentifier makeHdRadioStationName(const std::string& name) {
     constexpr size_t maxlen = 8;
 
-    string shortName;
+    std::string shortName;
     shortName.reserve(maxlen);
 
     const auto& loc = std::locale::classic();
@@ -484,7 +483,7 @@
     return static_cast<IdentifierType>(typeAsInt);
 }
 
-bool parseArgInt(const string& s, int* out) {
+bool parseArgInt(const std::string& s, int* out) {
     return ::android::base::ParseInt(s, out);
 }
 
@@ -492,7 +491,7 @@
     return ::android::base::ParseInt(s, out);
 }
 
-bool parseArgBool(const string& s, bool* out) {
+bool parseArgBool(const std::string& s, bool* out) {
     if (EqualsIgnoreCase(s, "true")) {
         *out = true;
     } else if (EqualsIgnoreCase(s, "false")) {
@@ -503,7 +502,7 @@
     return true;
 }
 
-bool parseArgDirection(const string& s, bool* out) {
+bool parseArgDirection(const std::string& s, bool* out) {
     if (EqualsIgnoreCase(s, "up")) {
         *out = true;
     } else if (EqualsIgnoreCase(s, "down")) {
@@ -514,8 +513,8 @@
     return true;
 }
 
-bool parseArgIdentifierTypeArray(const string& s, vector<IdentifierType>* out) {
-    for (const string& val : ::android::base::Split(s, ",")) {
+bool parseArgIdentifierTypeArray(const std::string& s, vector<IdentifierType>* out) {
+    for (const std::string& val : ::android::base::Split(s, ",")) {
         int outInt;
         if (!parseArgInt(val, &outInt)) {
             return false;
@@ -526,8 +525,8 @@
 }
 
 bool parseProgramIdentifierList(const std::string& s, vector<ProgramIdentifier>* out) {
-    for (const string& idStr : ::android::base::Split(s, ",")) {
-        const vector<string> idStrPair = ::android::base::Split(idStr, ":");
+    for (const std::string& idStr : ::android::base::Split(s, ",")) {
+        const vector<std::string> idStrPair = ::android::base::Split(idStr, ":");
         if (idStrPair.size() != 2) {
             return false;
         }
diff --git a/broadcastradio/common/utilsaidl/src/UtilsV2.cpp b/broadcastradio/common/utilsaidl/src/UtilsV2.cpp
new file mode 100644
index 0000000..ef739df
--- /dev/null
+++ b/broadcastradio/common/utilsaidl/src/UtilsV2.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BcRadioAidlDef.utilsV2"
+
+#include "broadcastradio-utils-aidl/UtilsV2.h"
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+namespace aidl::android::hardware::broadcastradio {
+
+namespace utils {
+
+bool isValidV2(const ProgramIdentifier& id) {
+    uint64_t val = static_cast<uint64_t>(id.value);
+    bool valid = true;
+
+    auto expect = [&valid](bool condition, const std::string& message) {
+        if (!condition) {
+            valid = false;
+            LOG(ERROR) << "identifier not valid, expected " << message;
+        }
+    };
+
+    switch (id.type) {
+        case IdentifierType::INVALID:
+            expect(false, "IdentifierType::INVALID");
+            break;
+        case IdentifierType::DAB_FREQUENCY_KHZ:
+            expect(val > 100000u, "f > 100MHz");
+            [[fallthrough]];
+        case IdentifierType::AMFM_FREQUENCY_KHZ:
+        case IdentifierType::DRMO_FREQUENCY_KHZ:
+            expect(val > 100u, "f > 100kHz");
+            expect(val < 10000000u, "f < 10GHz");
+            break;
+        case IdentifierType::RDS_PI:
+            expect(val != 0u, "RDS PI != 0");
+            expect(val <= 0xFFFFu, "16bit id");
+            break;
+        case IdentifierType::HD_STATION_ID_EXT: {
+            uint64_t stationId = val & 0xFFFFFFFF;  // 32bit
+            val >>= 32;
+            uint64_t subchannel = val & 0xF;  // 4bit
+            val >>= 4;
+            uint64_t freq = val & 0x3FFFF;  // 18bit
+            expect(stationId != 0u, "HD station id != 0");
+            expect(subchannel < 8u, "HD subch < 8");
+            expect(freq > 100u, "f > 100kHz");
+            expect(freq < 10000000u, "f < 10GHz");
+            break;
+        }
+        case IdentifierType::HD_STATION_NAME: {
+            while (val > 0) {
+                char ch = static_cast<char>(val & 0xFF);
+                val >>= 8;
+                expect((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z'),
+                       "HD_STATION_NAME does not match [A-Z0-9]+");
+            }
+            break;
+        }
+        case IdentifierType::DAB_SID_EXT: {
+            uint64_t sid = val & 0xFFFFFFFF;  // 32bit
+            val >>= 32;
+            uint64_t ecc = val & 0xFF;  // 8bit
+            expect(sid != 0u, "DAB SId != 0");
+            expect(ecc >= 0xA0u && ecc <= 0xF6u, "Invalid ECC, see ETSI TS 101 756 V2.1.1");
+            break;
+        }
+        case IdentifierType::DAB_ENSEMBLE:
+            expect(val != 0u, "DAB ensemble != 0");
+            expect(val <= 0xFFFFu, "16bit id");
+            break;
+        case IdentifierType::DAB_SCID:
+            expect(val > 0xFu, "12bit SCId (not 4bit SCIdS)");
+            expect(val <= 0xFFFu, "12bit id");
+            break;
+        case IdentifierType::DRMO_SERVICE_ID:
+            expect(val != 0u, "DRM SId != 0");
+            expect(val <= 0xFFFFFFu, "24bit id");
+            break;
+        case IdentifierType::SXM_SERVICE_ID:
+            expect(val != 0u, "SXM SId != 0");
+            expect(val <= 0xFFFFFFFFu, "32bit id");
+            break;
+        case IdentifierType::SXM_CHANNEL:
+            expect(val < 1000u, "SXM channel < 1000");
+            break;
+        case IdentifierType::HD_STATION_LOCATION: {
+            uint64_t latitudeBit = val & 0x1;
+            expect(latitudeBit == 1u, "Latitude comes first");
+            val >>= 27;
+            uint64_t latitudePad = val & 0x1Fu;
+            expect(latitudePad == 0u, "Latitude padding");
+            val >>= 5;
+            uint64_t longitudeBit = val & 0x1;
+            expect(longitudeBit == 1u, "Longitude comes next");
+            val >>= 27;
+            uint64_t longitudePad = val & 0x1Fu;
+            expect(longitudePad == 0u, "Latitude padding");
+            break;
+        }
+        default:
+            expect(id.type >= IdentifierType::VENDOR_START && id.type <= IdentifierType::VENDOR_END,
+                   "Undefined identifier type");
+            break;
+    }
+
+    return valid;
+}
+
+bool isValidV2(const ProgramSelector& sel) {
+    if (sel.primaryId.type != IdentifierType::AMFM_FREQUENCY_KHZ &&
+        sel.primaryId.type != IdentifierType::RDS_PI &&
+        sel.primaryId.type != IdentifierType::HD_STATION_ID_EXT &&
+        sel.primaryId.type != IdentifierType::DAB_SID_EXT &&
+        sel.primaryId.type != IdentifierType::DRMO_SERVICE_ID &&
+        sel.primaryId.type != IdentifierType::SXM_SERVICE_ID &&
+        (sel.primaryId.type < IdentifierType::VENDOR_START ||
+         sel.primaryId.type > IdentifierType::VENDOR_END)) {
+        return false;
+    }
+    return isValidV2(sel.primaryId);
+}
+
+std::optional<std::string> getMetadataStringV2(const ProgramInfo& info, const Metadata::Tag& tag) {
+    auto isRdsPs = [tag](const Metadata& item) { return item.getTag() == tag; };
+
+    auto it = std::find_if(info.metadata.begin(), info.metadata.end(), isRdsPs);
+    if (it == info.metadata.end()) {
+        return std::nullopt;
+    }
+
+    std::string metadataString;
+    switch (it->getTag()) {
+        case Metadata::rdsPs:
+            metadataString = it->get<Metadata::rdsPs>();
+            break;
+        case Metadata::rdsPty:
+            metadataString = std::to_string(it->get<Metadata::rdsPty>());
+            break;
+        case Metadata::rbdsPty:
+            metadataString = std::to_string(it->get<Metadata::rbdsPty>());
+            break;
+        case Metadata::rdsRt:
+            metadataString = it->get<Metadata::rdsRt>();
+            break;
+        case Metadata::songTitle:
+            metadataString = it->get<Metadata::songTitle>();
+            break;
+        case Metadata::songArtist:
+            metadataString = it->get<Metadata::songArtist>();
+            break;
+        case Metadata::songAlbum:
+            metadataString = it->get<Metadata::songAlbum>();
+            break;
+        case Metadata::stationIcon:
+            metadataString = std::to_string(it->get<Metadata::stationIcon>());
+            break;
+        case Metadata::albumArt:
+            metadataString = std::to_string(it->get<Metadata::albumArt>());
+            break;
+        case Metadata::programName:
+            metadataString = it->get<Metadata::programName>();
+            break;
+        case Metadata::dabEnsembleName:
+            metadataString = it->get<Metadata::dabEnsembleName>();
+            break;
+        case Metadata::dabEnsembleNameShort:
+            metadataString = it->get<Metadata::dabEnsembleNameShort>();
+            break;
+        case Metadata::dabServiceName:
+            metadataString = it->get<Metadata::dabServiceName>();
+            break;
+        case Metadata::dabServiceNameShort:
+            metadataString = it->get<Metadata::dabServiceNameShort>();
+            break;
+        case Metadata::dabComponentName:
+            metadataString = it->get<Metadata::dabComponentName>();
+            break;
+        case Metadata::dabComponentNameShort:
+            metadataString = it->get<Metadata::dabComponentNameShort>();
+            break;
+        case Metadata::genre:
+            metadataString = it->get<Metadata::genre>();
+            break;
+        case Metadata::commentShortDescription:
+            metadataString = it->get<Metadata::commentShortDescription>();
+            break;
+        case Metadata::commentActualText:
+            metadataString = it->get<Metadata::commentActualText>();
+            break;
+        case Metadata::commercial:
+            metadataString = it->get<Metadata::commercial>();
+            break;
+        case Metadata::ufids: {
+            auto& ufids = it->get<Metadata::ufids>();
+            metadataString = "[";
+            for (const auto& ufid : ufids) {
+                metadataString += std::string(ufid) + ",";
+            }
+            if (ufids.empty()) {
+                metadataString += "]";
+            } else {
+                metadataString[metadataString.size() - 1] = ']';
+            }
+        } break;
+        case Metadata::hdStationNameShort:
+            metadataString = it->get<Metadata::hdStationNameShort>();
+            break;
+        case Metadata::hdStationNameLong:
+            metadataString = it->get<Metadata::hdStationNameLong>();
+            break;
+        case Metadata::hdSubChannelsAvailable:
+            metadataString = std::to_string(it->get<Metadata::hdSubChannelsAvailable>());
+            break;
+        default:
+            LOG(ERROR) << "Metadata " << it->toString() << " is not converted.";
+            return std::nullopt;
+    }
+    return metadataString;
+}
+
+}  // namespace utils
+
+}  // namespace aidl::android::hardware::broadcastradio
diff --git a/camera/common/default/Android.bp b/camera/common/default/Android.bp
index e8c8f9d..b5d3095 100644
--- a/camera/common/default/Android.bp
+++ b/camera/common/default/Android.bp
@@ -30,13 +30,12 @@
         "libgralloctypes",
         "libhardware",
         "libcamera_metadata",
-        "android.hardware.graphics.mapper@2.0",
-        "android.hardware.graphics.mapper@3.0",
-        "android.hardware.graphics.mapper@4.0",
         "libexif",
+        "libui",
     ],
     include_dirs: ["system/media/private/camera/include"],
     export_include_dirs: ["include"],
+    export_shared_lib_headers: ["libui"],
 }
 
 // NOTE: Deprecated module kept for compatibility reasons.
diff --git a/camera/common/default/HandleImporter.cpp b/camera/common/default/HandleImporter.cpp
index 1145baa..9c579e5 100644
--- a/camera/common/default/HandleImporter.cpp
+++ b/camera/common/default/HandleImporter.cpp
@@ -17,9 +17,10 @@
 #define LOG_TAG "HandleImporter"
 #include "HandleImporter.h"
 
+#include <aidl/android/hardware/graphics/common/Smpte2086.h>
 #include <gralloctypes/Gralloc4.h>
 #include <log/log.h>
-#include "aidl/android/hardware/graphics/common/Smpte2086.h"
+#include <ui/GraphicBufferMapper.h>
 
 namespace android {
 namespace hardware {
@@ -31,12 +32,6 @@
 using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
 using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
 using aidl::android::hardware::graphics::common::Smpte2086;
-using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
-using MapperErrorV2 = android::hardware::graphics::mapper::V2_0::Error;
-using MapperErrorV3 = android::hardware::graphics::mapper::V3_0::Error;
-using MapperErrorV4 = android::hardware::graphics::mapper::V4_0::Error;
-using IMapperV3 = android::hardware::graphics::mapper::V3_0::IMapper;
-using IMapperV4 = android::hardware::graphics::mapper::V4_0::IMapper;
 
 HandleImporter::HandleImporter() : mInitialized(false) {}
 
@@ -45,51 +40,20 @@
         return;
     }
 
-    mMapperV4 = IMapperV4::getService();
-    if (mMapperV4 != nullptr) {
-        mInitialized = true;
-        return;
-    }
-
-    mMapperV3 = IMapperV3::getService();
-    if (mMapperV3 != nullptr) {
-        mInitialized = true;
-        return;
-    }
-
-    mMapperV2 = IMapper::getService();
-    if (mMapperV2 == nullptr) {
-        ALOGE("%s: cannnot acccess graphics mapper HAL!", __FUNCTION__);
-        return;
-    }
-
+    GraphicBufferMapper::preloadHal();
     mInitialized = true;
     return;
 }
 
 void HandleImporter::cleanup() {
-    mMapperV4.clear();
-    mMapperV3.clear();
-    mMapperV2.clear();
     mInitialized = false;
 }
 
-template <class M, class E>
-bool HandleImporter::importBufferInternal(const sp<M> mapper, buffer_handle_t& handle) {
-    E error;
+bool HandleImporter::importBufferInternal(buffer_handle_t& handle) {
     buffer_handle_t importedHandle;
-    auto ret = mapper->importBuffer(
-            hidl_handle(handle), [&](const auto& tmpError, const auto& tmpBufferHandle) {
-                error = tmpError;
-                importedHandle = static_cast<buffer_handle_t>(tmpBufferHandle);
-            });
-
-    if (!ret.isOk()) {
-        ALOGE("%s: mapper importBuffer failed: %s", __FUNCTION__, ret.description().c_str());
-        return false;
-    }
-
-    if (error != E::NONE) {
+    auto status = GraphicBufferMapper::get().importBufferNoValidate(handle, &importedHandle);
+    if (status != OK) {
+        ALOGE("%s: mapper importBuffer failed: %d", __FUNCTION__, status);
         return false;
     }
 
@@ -97,172 +61,34 @@
     return true;
 }
 
-template <class M, class E>
-YCbCrLayout HandleImporter::lockYCbCrInternal(const sp<M> mapper, buffer_handle_t& buf,
-                                              uint64_t cpuUsage,
-                                              const IMapper::Rect& accessRegion) {
-    hidl_handle acquireFenceHandle;
-    auto buffer = const_cast<native_handle_t*>(buf);
-    YCbCrLayout layout = {};
+android_ycbcr HandleImporter::lockYCbCr(buffer_handle_t& buf, uint64_t cpuUsage,
+                                        const android::Rect& accessRegion) {
+    Mutex::Autolock lock(mLock);
 
-    typename M::Rect accessRegionCopy = {accessRegion.left, accessRegion.top, accessRegion.width,
-                                         accessRegion.height};
-    mapper->lockYCbCr(buffer, cpuUsage, accessRegionCopy, acquireFenceHandle,
-                      [&](const auto& tmpError, const auto& tmpLayout) {
-                          if (tmpError == E::NONE) {
-                              // Member by member copy from different versions of YCbCrLayout.
-                              layout.y = tmpLayout.y;
-                              layout.cb = tmpLayout.cb;
-                              layout.cr = tmpLayout.cr;
-                              layout.yStride = tmpLayout.yStride;
-                              layout.cStride = tmpLayout.cStride;
-                              layout.chromaStep = tmpLayout.chromaStep;
-                          } else {
-                              ALOGE("%s: failed to lockYCbCr error %d!", __FUNCTION__, tmpError);
-                          }
-                      });
+    if (!mInitialized) {
+        initializeLocked();
+    }
+    android_ycbcr layout;
+
+    status_t status = GraphicBufferMapper::get().lockYCbCr(buf, cpuUsage, accessRegion, &layout);
+
+    if (status != OK) {
+        ALOGE("%s: failed to lockYCbCr error %d!", __FUNCTION__, status);
+    }
+
     return layout;
 }
 
-bool isMetadataPesent(const sp<IMapperV4> mapper, const buffer_handle_t& buf,
-                      MetadataType metadataType) {
-    auto buffer = const_cast<native_handle_t*>(buf);
-    bool ret = false;
-    hidl_vec<uint8_t> vec;
-    mapper->get(buffer, metadataType, [&](const auto& tmpError, const auto& tmpMetadata) {
-        if (tmpError == MapperErrorV4::NONE) {
-            vec = tmpMetadata;
-        } else {
-            ALOGE("%s: failed to get metadata %d!", __FUNCTION__, tmpError);
-        }
-    });
-
-    if (vec.size() > 0) {
-        if (metadataType == gralloc4::MetadataType_Smpte2086) {
-            std::optional<Smpte2086> realSmpte2086;
-            gralloc4::decodeSmpte2086(vec, &realSmpte2086);
-            ret = realSmpte2086.has_value();
-        } else if (metadataType == gralloc4::MetadataType_Smpte2094_10) {
-            std::optional<std::vector<uint8_t>> realSmpte2094_10;
-            gralloc4::decodeSmpte2094_10(vec, &realSmpte2094_10);
-            ret = realSmpte2094_10.has_value();
-        } else if (metadataType == gralloc4::MetadataType_Smpte2094_40) {
-            std::optional<std::vector<uint8_t>> realSmpte2094_40;
-            gralloc4::decodeSmpte2094_40(vec, &realSmpte2094_40);
-            ret = realSmpte2094_40.has_value();
-        } else {
-            ALOGE("%s: Unknown metadata type!", __FUNCTION__);
-        }
-    }
-
-    return ret;
-}
-
-std::vector<PlaneLayout> getPlaneLayouts(const sp<IMapperV4> mapper, buffer_handle_t& buf) {
-    auto buffer = const_cast<native_handle_t*>(buf);
+std::vector<PlaneLayout> getPlaneLayouts(buffer_handle_t& buf) {
     std::vector<PlaneLayout> planeLayouts;
-    hidl_vec<uint8_t> encodedPlaneLayouts;
-    mapper->get(buffer, gralloc4::MetadataType_PlaneLayouts,
-                [&](const auto& tmpError, const auto& tmpEncodedPlaneLayouts) {
-                    if (tmpError == MapperErrorV4::NONE) {
-                        encodedPlaneLayouts = tmpEncodedPlaneLayouts;
-                    } else {
-                        ALOGE("%s: failed to get plane layouts %d!", __FUNCTION__, tmpError);
-                    }
-                });
-
-    gralloc4::decodePlaneLayouts(encodedPlaneLayouts, &planeLayouts);
+    status_t status = GraphicBufferMapper::get().getPlaneLayouts(buf, &planeLayouts);
+    if (status != OK) {
+        ALOGE("%s: failed to get PlaneLayouts! Status %d", __FUNCTION__, status);
+    }
 
     return planeLayouts;
 }
 
-template <>
-YCbCrLayout HandleImporter::lockYCbCrInternal<IMapperV4, MapperErrorV4>(
-        const sp<IMapperV4> mapper, buffer_handle_t& buf, uint64_t cpuUsage,
-        const IMapper::Rect& accessRegion) {
-    hidl_handle acquireFenceHandle;
-    auto buffer = const_cast<native_handle_t*>(buf);
-    YCbCrLayout layout = {};
-    void* mapped = nullptr;
-
-    typename IMapperV4::Rect accessRegionV4 = {accessRegion.left, accessRegion.top,
-                                               accessRegion.width, accessRegion.height};
-    mapper->lock(buffer, cpuUsage, accessRegionV4, acquireFenceHandle,
-                 [&](const auto& tmpError, const auto& tmpPtr) {
-                     if (tmpError == MapperErrorV4::NONE) {
-                         mapped = tmpPtr;
-                     } else {
-                         ALOGE("%s: failed to lock error %d!", __FUNCTION__, tmpError);
-                     }
-                 });
-
-    if (mapped == nullptr) {
-        return layout;
-    }
-
-    std::vector<PlaneLayout> planeLayouts = getPlaneLayouts(mapper, buf);
-    for (const auto& planeLayout : planeLayouts) {
-        for (const auto& planeLayoutComponent : planeLayout.components) {
-            const auto& type = planeLayoutComponent.type;
-
-            if (!gralloc4::isStandardPlaneLayoutComponentType(type)) {
-                continue;
-            }
-
-            uint8_t* data = reinterpret_cast<uint8_t*>(mapped);
-            data += planeLayout.offsetInBytes;
-            data += planeLayoutComponent.offsetInBits / 8;
-
-            switch (static_cast<PlaneLayoutComponentType>(type.value)) {
-                case PlaneLayoutComponentType::Y:
-                    layout.y = data;
-                    layout.yStride = planeLayout.strideInBytes;
-                    break;
-                case PlaneLayoutComponentType::CB:
-                    layout.cb = data;
-                    layout.cStride = planeLayout.strideInBytes;
-                    layout.chromaStep = planeLayout.sampleIncrementInBits / 8;
-                    break;
-                case PlaneLayoutComponentType::CR:
-                    layout.cr = data;
-                    layout.cStride = planeLayout.strideInBytes;
-                    layout.chromaStep = planeLayout.sampleIncrementInBits / 8;
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-
-    return layout;
-}
-
-template <class M, class E>
-int HandleImporter::unlockInternal(const sp<M> mapper, buffer_handle_t& buf) {
-    int releaseFence = -1;
-    auto buffer = const_cast<native_handle_t*>(buf);
-
-    mapper->unlock(buffer, [&](const auto& tmpError, const auto& tmpReleaseFence) {
-        if (tmpError == E::NONE) {
-            auto fenceHandle = tmpReleaseFence.getNativeHandle();
-            if (fenceHandle) {
-                if (fenceHandle->numInts != 0 || fenceHandle->numFds != 1) {
-                    ALOGE("%s: bad release fence numInts %d numFds %d", __FUNCTION__,
-                          fenceHandle->numInts, fenceHandle->numFds);
-                    return;
-                }
-                releaseFence = dup(fenceHandle->data[0]);
-                if (releaseFence < 0) {
-                    ALOGE("%s: bad release fence FD %d", __FUNCTION__, releaseFence);
-                }
-            }
-        } else {
-            ALOGE("%s: failed to unlock error %d!", __FUNCTION__, tmpError);
-        }
-    });
-    return releaseFence;
-}
-
 // In IComposer, any buffer_handle_t is owned by the caller and we need to
 // make a clone for hwcomposer2.  We also need to translate empty handle
 // to nullptr.  This function does that, in-place.
@@ -277,20 +103,7 @@
         initializeLocked();
     }
 
-    if (mMapperV4 != nullptr) {
-        return importBufferInternal<IMapperV4, MapperErrorV4>(mMapperV4, handle);
-    }
-
-    if (mMapperV3 != nullptr) {
-        return importBufferInternal<IMapperV3, MapperErrorV3>(mMapperV3, handle);
-    }
-
-    if (mMapperV2 != nullptr) {
-        return importBufferInternal<IMapper, MapperErrorV2>(mMapperV2, handle);
-    }
-
-    ALOGE("%s: mMapperV4, mMapperV3 and mMapperV2 are all null!", __FUNCTION__);
-    return false;
+    return importBufferInternal(handle);
 }
 
 void HandleImporter::freeBuffer(buffer_handle_t handle) {
@@ -303,21 +116,9 @@
         initializeLocked();
     }
 
-    if (mMapperV4 != nullptr) {
-        auto ret = mMapperV4->freeBuffer(const_cast<native_handle_t*>(handle));
-        if (!ret.isOk()) {
-            ALOGE("%s: mapper freeBuffer failed: %s", __FUNCTION__, ret.description().c_str());
-        }
-    } else if (mMapperV3 != nullptr) {
-        auto ret = mMapperV3->freeBuffer(const_cast<native_handle_t*>(handle));
-        if (!ret.isOk()) {
-            ALOGE("%s: mapper freeBuffer failed: %s", __FUNCTION__, ret.description().c_str());
-        }
-    } else {
-        auto ret = mMapperV2->freeBuffer(const_cast<native_handle_t*>(handle));
-        if (!ret.isOk()) {
-            ALOGE("%s: mapper freeBuffer failed: %s", __FUNCTION__, ret.description().c_str());
-        }
+    status_t status = GraphicBufferMapper::get().freeBuffer(handle);
+    if (status != OK) {
+        ALOGE("%s: mapper freeBuffer failed. Status %d", __FUNCTION__, status);
     }
 }
 
@@ -345,12 +146,12 @@
 }
 
 void* HandleImporter::lock(buffer_handle_t& buf, uint64_t cpuUsage, size_t size) {
-    IMapper::Rect accessRegion{0, 0, static_cast<int>(size), 1};
+    android::Rect accessRegion{0, 0, static_cast<int>(size), 1};
     return lock(buf, cpuUsage, accessRegion);
 }
 
 void* HandleImporter::lock(buffer_handle_t& buf, uint64_t cpuUsage,
-                           const IMapper::Rect& accessRegion) {
+                           const android::Rect& accessRegion) {
     Mutex::Autolock lock(mLock);
 
     if (!mInitialized) {
@@ -358,81 +159,18 @@
     }
 
     void* ret = nullptr;
-
-    if (mMapperV4 == nullptr && mMapperV3 == nullptr && mMapperV2 == nullptr) {
-        ALOGE("%s: mMapperV4, mMapperV3 and mMapperV2 are all null!", __FUNCTION__);
-        return ret;
-    }
-
-    hidl_handle acquireFenceHandle;
-    auto buffer = const_cast<native_handle_t*>(buf);
-    if (mMapperV4 != nullptr) {
-        IMapperV4::Rect accessRegionV4{accessRegion.left, accessRegion.top, accessRegion.width,
-                                       accessRegion.height};
-
-        mMapperV4->lock(buffer, cpuUsage, accessRegionV4, acquireFenceHandle,
-                        [&](const auto& tmpError, const auto& tmpPtr) {
-                            if (tmpError == MapperErrorV4::NONE) {
-                                ret = tmpPtr;
-                            } else {
-                                ALOGE("%s: failed to lock error %d!", __FUNCTION__, tmpError);
-                            }
-                        });
-    } else if (mMapperV3 != nullptr) {
-        IMapperV3::Rect accessRegionV3{accessRegion.left, accessRegion.top, accessRegion.width,
-                                       accessRegion.height};
-
-        mMapperV3->lock(buffer, cpuUsage, accessRegionV3, acquireFenceHandle,
-                        [&](const auto& tmpError, const auto& tmpPtr, const auto& /*bytesPerPixel*/,
-                            const auto& /*bytesPerStride*/) {
-                            if (tmpError == MapperErrorV3::NONE) {
-                                ret = tmpPtr;
-                            } else {
-                                ALOGE("%s: failed to lock error %d!", __FUNCTION__, tmpError);
-                            }
-                        });
-    } else {
-        mMapperV2->lock(buffer, cpuUsage, accessRegion, acquireFenceHandle,
-                        [&](const auto& tmpError, const auto& tmpPtr) {
-                            if (tmpError == MapperErrorV2::NONE) {
-                                ret = tmpPtr;
-                            } else {
-                                ALOGE("%s: failed to lock error %d!", __FUNCTION__, tmpError);
-                            }
-                        });
+    status_t status = GraphicBufferMapper::get().lock(buf, cpuUsage, accessRegion, &ret);
+    if (status != OK) {
+        ALOGE("%s: failed to lock error %d!", __FUNCTION__, status);
     }
 
     ALOGV("%s: ptr %p accessRegion.top: %d accessRegion.left: %d accessRegion.width: %d "
           "accessRegion.height: %d",
-          __FUNCTION__, ret, accessRegion.top, accessRegion.left, accessRegion.width,
-          accessRegion.height);
+          __FUNCTION__, ret, accessRegion.top, accessRegion.left, accessRegion.width(),
+          accessRegion.height());
     return ret;
 }
 
-YCbCrLayout HandleImporter::lockYCbCr(buffer_handle_t& buf, uint64_t cpuUsage,
-                                      const IMapper::Rect& accessRegion) {
-    Mutex::Autolock lock(mLock);
-
-    if (!mInitialized) {
-        initializeLocked();
-    }
-
-    if (mMapperV4 != nullptr) {
-        return lockYCbCrInternal<IMapperV4, MapperErrorV4>(mMapperV4, buf, cpuUsage, accessRegion);
-    }
-
-    if (mMapperV3 != nullptr) {
-        return lockYCbCrInternal<IMapperV3, MapperErrorV3>(mMapperV3, buf, cpuUsage, accessRegion);
-    }
-
-    if (mMapperV2 != nullptr) {
-        return lockYCbCrInternal<IMapper, MapperErrorV2>(mMapperV2, buf, cpuUsage, accessRegion);
-    }
-
-    ALOGE("%s: mMapperV4, mMapperV3 and mMapperV2 are all null!", __FUNCTION__);
-    return {};
-}
-
 status_t HandleImporter::getMonoPlanarStrideBytes(buffer_handle_t& buf, uint32_t* stride /*out*/) {
     if (stride == nullptr) {
         return BAD_VALUE;
@@ -444,35 +182,26 @@
         initializeLocked();
     }
 
-    if (mMapperV4 != nullptr) {
-        std::vector<PlaneLayout> planeLayouts = getPlaneLayouts(mMapperV4, buf);
-        if (planeLayouts.size() != 1) {
-            ALOGE("%s: Unexpected number of planes %zu!", __FUNCTION__, planeLayouts.size());
-            return BAD_VALUE;
-        }
-
-        *stride = planeLayouts[0].strideInBytes;
-    } else {
-        ALOGE("%s: mMapperV4 is null! Query not supported!", __FUNCTION__);
-        return NO_INIT;
+    std::vector<PlaneLayout> planeLayouts = getPlaneLayouts(buf);
+    if (planeLayouts.size() != 1) {
+        ALOGE("%s: Unexpected number of planes %zu!", __FUNCTION__, planeLayouts.size());
+        return BAD_VALUE;
     }
 
+    *stride = planeLayouts[0].strideInBytes;
+
     return OK;
 }
 
 int HandleImporter::unlock(buffer_handle_t& buf) {
-    if (mMapperV4 != nullptr) {
-        return unlockInternal<IMapperV4, MapperErrorV4>(mMapperV4, buf);
-    }
-    if (mMapperV3 != nullptr) {
-        return unlockInternal<IMapperV3, MapperErrorV3>(mMapperV3, buf);
-    }
-    if (mMapperV2 != nullptr) {
-        return unlockInternal<IMapper, MapperErrorV2>(mMapperV2, buf);
+    int releaseFence = -1;
+
+    status_t status = GraphicBufferMapper::get().unlockAsync(buf, &releaseFence);
+    if (status != OK) {
+        ALOGE("%s: failed to unlock error %d!", __FUNCTION__, status);
     }
 
-    ALOGE("%s: mMapperV4, mMapperV3 and mMapperV2 are all null!", __FUNCTION__);
-    return -1;
+    return releaseFence;
 }
 
 bool HandleImporter::isSmpte2086Present(const buffer_handle_t& buf) {
@@ -481,14 +210,14 @@
     if (!mInitialized) {
         initializeLocked();
     }
-
-    if (mMapperV4 != nullptr) {
-        return isMetadataPesent(mMapperV4, buf, gralloc4::MetadataType_Smpte2086);
-    } else {
-        ALOGE("%s: mMapperV4 is null! Query not supported!", __FUNCTION__);
+    std::optional<ui::Smpte2086> metadata;
+    status_t status = GraphicBufferMapper::get().getSmpte2086(buf, &metadata);
+    if (status != OK) {
+        ALOGE("%s: Mapper failed to get Smpte2094_40 metadata! Status: %d", __FUNCTION__, status);
+        return false;
     }
 
-    return false;
+    return metadata.has_value();
 }
 
 bool HandleImporter::isSmpte2094_10Present(const buffer_handle_t& buf) {
@@ -498,13 +227,14 @@
         initializeLocked();
     }
 
-    if (mMapperV4 != nullptr) {
-        return isMetadataPesent(mMapperV4, buf, gralloc4::MetadataType_Smpte2094_10);
-    } else {
-        ALOGE("%s: mMapperV4 is null! Query not supported!", __FUNCTION__);
+    std::optional<std::vector<uint8_t>> metadata;
+    status_t status = GraphicBufferMapper::get().getSmpte2094_10(buf, &metadata);
+    if (status != OK) {
+        ALOGE("%s: Mapper failed to get Smpte2094_40 metadata! Status: %d", __FUNCTION__, status);
+        return false;
     }
 
-    return false;
+    return metadata.has_value();
 }
 
 bool HandleImporter::isSmpte2094_40Present(const buffer_handle_t& buf) {
@@ -514,13 +244,14 @@
         initializeLocked();
     }
 
-    if (mMapperV4 != nullptr) {
-        return isMetadataPesent(mMapperV4, buf, gralloc4::MetadataType_Smpte2094_40);
-    } else {
-        ALOGE("%s: mMapperV4 is null! Query not supported!", __FUNCTION__);
+    std::optional<std::vector<uint8_t>> metadata;
+    status_t status = GraphicBufferMapper::get().getSmpte2094_40(buf, &metadata);
+    if (status != OK) {
+        ALOGE("%s: Mapper failed to get Smpte2094_40 metadata! Status: %d", __FUNCTION__, status);
+        return false;
     }
 
-    return false;
+    return metadata.has_value();
 }
 
 }  // namespace helper
diff --git a/camera/common/default/include/HandleImporter.h b/camera/common/default/include/HandleImporter.h
index 5408ba9..df01202 100644
--- a/camera/common/default/include/HandleImporter.h
+++ b/camera/common/default/include/HandleImporter.h
@@ -17,15 +17,11 @@
 #ifndef CAMERA_COMMON_1_0_HANDLEIMPORTED_H
 #define CAMERA_COMMON_1_0_HANDLEIMPORTED_H
 
-#include <android/hardware/graphics/mapper/2.0/IMapper.h>
-#include <android/hardware/graphics/mapper/3.0/IMapper.h>
-#include <android/hardware/graphics/mapper/4.0/IMapper.h>
 #include <cutils/native_handle.h>
+#include <system/graphics.h>
+#include <ui/Rect.h>
 #include <utils/Mutex.h>
 
-using android::hardware::graphics::mapper::V2_0::IMapper;
-using android::hardware::graphics::mapper::V2_0::YCbCrLayout;
-
 namespace android {
 namespace hardware {
 namespace camera {
@@ -49,11 +45,11 @@
     void* lock(buffer_handle_t& buf, uint64_t cpuUsage, size_t size);
 
     // Locks 2-D buffer. Assumes caller has waited for acquire fences.
-    void* lock(buffer_handle_t& buf, uint64_t cpuUsage, const IMapper::Rect& accessRegion);
+    void* lock(buffer_handle_t& buf, uint64_t cpuUsage, const android::Rect& accessRegion);
 
     // Assumes caller has waited for acquire fences.
-    YCbCrLayout lockYCbCr(buffer_handle_t& buf, uint64_t cpuUsage,
-                          const IMapper::Rect& accessRegion);
+    android_ycbcr lockYCbCr(buffer_handle_t& buf, uint64_t cpuUsage,
+                            const android::Rect& accessRegion);
 
     // Query the stride of the first plane in bytes.
     status_t getMonoPlanarStrideBytes(buffer_handle_t& buf, uint32_t* stride /*out*/);
@@ -69,19 +65,11 @@
     void initializeLocked();
     void cleanup();
 
-    template <class M, class E>
-    bool importBufferInternal(const sp<M> mapper, buffer_handle_t& handle);
-    template <class M, class E>
-    YCbCrLayout lockYCbCrInternal(const sp<M> mapper, buffer_handle_t& buf, uint64_t cpuUsage,
-                                  const IMapper::Rect& accessRegion);
-    template <class M, class E>
-    int unlockInternal(const sp<M> mapper, buffer_handle_t& buf);
+    bool importBufferInternal(buffer_handle_t& handle);
+    int unlockInternal(buffer_handle_t& buf);
 
     Mutex mLock;
     bool mInitialized;
-    sp<IMapper> mMapperV2;
-    sp<graphics::mapper::V3_0::IMapper> mMapperV3;
-    sp<graphics::mapper::V4_0::IMapper> mMapperV4;
 };
 
 }  // namespace helper
diff --git a/camera/device/1.0/default/Android.bp b/camera/device/1.0/default/Android.bp
index 9ff6480..6992ff0 100644
--- a/camera/device/1.0/default/Android.bp
+++ b/camera/device/1.0/default/Android.bp
@@ -32,6 +32,7 @@
         "libgralloctypes",
         "libhardware",
         "libcamera_metadata",
+        "libui",
     ],
     static_libs: [
         "android.hardware.camera.common@1.0-helper",
diff --git a/camera/device/3.2/default/Android.bp b/camera/device/3.2/default/Android.bp
index a196291..adf834a 100644
--- a/camera/device/3.2/default/Android.bp
+++ b/camera/device/3.2/default/Android.bp
@@ -30,6 +30,7 @@
         "libhardware",
         "libcamera_metadata",
         "libfmq",
+        "libui",
     ],
     static_libs: [
         "android.hardware.camera.common@1.0-helper",
diff --git a/camera/device/3.4/default/Android.bp b/camera/device/3.4/default/Android.bp
index 9f0c777..100106e 100644
--- a/camera/device/3.4/default/Android.bp
+++ b/camera/device/3.4/default/Android.bp
@@ -106,6 +106,7 @@
         "libjpeg",
         "libexif",
         "libtinyxml2",
+        "libui",
     ],
     static_libs: [
         "android.hardware.camera.common@1.0-helper",
diff --git a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
index ca7186b..01b3d41 100644
--- a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
@@ -1574,14 +1574,23 @@
             } break;
             case PixelFormat::YCBCR_420_888:
             case PixelFormat::YV12: {
-                IMapper::Rect outRect {0, 0,
-                        static_cast<int32_t>(halBuf.width),
-                        static_cast<int32_t>(halBuf.height)};
-                YCbCrLayout outLayout = sHandleImporter.lockYCbCr(
-                        *(halBuf.bufPtr), halBuf.usage, outRect);
-                ALOGV("%s: outLayout y %p cb %p cr %p y_str %d c_str %d c_step %d",
-                        __FUNCTION__, outLayout.y, outLayout.cb, outLayout.cr,
-                        outLayout.yStride, outLayout.cStride, outLayout.chromaStep);
+                android::Rect outRect{0, 0, static_cast<int32_t>(halBuf.width),
+                                      static_cast<int32_t>(halBuf.height)};
+                android_ycbcr result =
+                        sHandleImporter.lockYCbCr(*(halBuf.bufPtr), halBuf.usage, outRect);
+                ALOGV("%s: outLayout y %p cb %p cr %p y_str %zu c_str %zu c_step %zu", __FUNCTION__,
+                      result.y, result.cb, result.cr, result.ystride, result.cstride,
+                      result.chroma_step);
+                if (result.ystride > UINT32_MAX || result.cstride > UINT32_MAX ||
+                    result.chroma_step > UINT32_MAX) {
+                    return onDeviceError("%s: lockYCbCr failed. Unexpected values!", __FUNCTION__);
+                }
+                YCbCrLayout outLayout = {.y = result.y,
+                                         .cb = result.cb,
+                                         .cr = result.cr,
+                                         .yStride = static_cast<uint32_t>(result.ystride),
+                                         .cStride = static_cast<uint32_t>(result.cstride),
+                                         .chromaStep = static_cast<uint32_t>(result.chroma_step)};
 
                 // Convert to output buffer size/format
                 uint32_t outputFourcc = getFourCcFromLayout(outLayout);
diff --git a/camera/device/3.5/default/Android.bp b/camera/device/3.5/default/Android.bp
index 9d27b32..bc15629 100644
--- a/camera/device/3.5/default/Android.bp
+++ b/camera/device/3.5/default/Android.bp
@@ -46,6 +46,7 @@
     ],
     shared_libs: [
         "libhidlbase",
+        "libui",
         "libutils",
         "libcutils",
         "camera.device@3.2-impl",
@@ -81,6 +82,7 @@
     ],
     shared_libs: [
         "libhidlbase",
+        "libui",
         "libutils",
         "libcutils",
         "camera.device@3.2-impl",
diff --git a/camera/device/3.6/default/Android.bp b/camera/device/3.6/default/Android.bp
index 89ee145..b4a486f 100644
--- a/camera/device/3.6/default/Android.bp
+++ b/camera/device/3.6/default/Android.bp
@@ -41,6 +41,7 @@
     ],
     shared_libs: [
         "libhidlbase",
+        "libui",
         "libutils",
         "libcutils",
         "camera.device@3.2-impl",
diff --git a/camera/device/3.6/default/ExternalCameraOfflineSession.cpp b/camera/device/3.6/default/ExternalCameraOfflineSession.cpp
index e606fda..1f1dfee 100644
--- a/camera/device/3.6/default/ExternalCameraOfflineSession.cpp
+++ b/camera/device/3.6/default/ExternalCameraOfflineSession.cpp
@@ -222,14 +222,23 @@
             } break;
             case PixelFormat::YCBCR_420_888:
             case PixelFormat::YV12: {
-                IMapper::Rect outRect {0, 0,
-                        static_cast<int32_t>(halBuf.width),
-                        static_cast<int32_t>(halBuf.height)};
-                YCbCrLayout outLayout = sHandleImporter.lockYCbCr(
-                        *(halBuf.bufPtr), halBuf.usage, outRect);
-                ALOGV("%s: outLayout y %p cb %p cr %p y_str %d c_str %d c_step %d",
-                        __FUNCTION__, outLayout.y, outLayout.cb, outLayout.cr,
-                        outLayout.yStride, outLayout.cStride, outLayout.chromaStep);
+                android::Rect outRect{0, 0, static_cast<int32_t>(halBuf.width),
+                                      static_cast<int32_t>(halBuf.height)};
+                android_ycbcr result =
+                        sHandleImporter.lockYCbCr(*(halBuf.bufPtr), halBuf.usage, outRect);
+                ALOGV("%s: outLayout y %p cb %p cr %p y_str %zu c_str %zu c_step %zu", __FUNCTION__,
+                      result.y, result.cb, result.cr, result.ystride, result.cstride,
+                      result.chroma_step);
+                if (result.ystride > UINT32_MAX || result.cstride > UINT32_MAX ||
+                    result.chroma_step > UINT32_MAX) {
+                    return onDeviceError("%s: lockYCbCr failed. Unexpected values!", __FUNCTION__);
+                }
+                YCbCrLayout outLayout = {.y = result.y,
+                                         .cb = result.cb,
+                                         .cr = result.cr,
+                                         .yStride = static_cast<uint32_t>(result.ystride),
+                                         .cStride = static_cast<uint32_t>(result.cstride),
+                                         .chromaStep = static_cast<uint32_t>(result.chroma_step)};
 
                 // Convert to output buffer size/format
                 uint32_t outputFourcc = V3_4::implementation::getFourCcFromLayout(outLayout);
diff --git a/camera/device/aidl/Android.bp b/camera/device/aidl/Android.bp
index 43a3934..1a665fb 100644
--- a/camera/device/aidl/Android.bp
+++ b/camera/device/aidl/Android.bp
@@ -18,7 +18,7 @@
         "android.hardware.common.fmq-V1",
         "android.hardware.camera.common-V1",
         "android.hardware.camera.metadata-V2",
-        "android.hardware.graphics.common-V4",
+        "android.hardware.graphics.common-V5",
     ],
     backend: {
         cpp: {
@@ -37,7 +37,7 @@
                 "android.hardware.common.fmq-V1",
                 "android.hardware.camera.common-V1",
                 "android.hardware.camera.metadata-V1",
-                "android.hardware.graphics.common-V4",
+                "android.hardware.graphics.common-V5",
             ],
         },
         {
@@ -47,7 +47,7 @@
                 "android.hardware.common.fmq-V1",
                 "android.hardware.camera.common-V1",
                 "android.hardware.camera.metadata-V2",
-                "android.hardware.graphics.common-V4",
+                "android.hardware.graphics.common-V5",
             ],
         },
 
diff --git a/camera/device/default/Android.bp b/camera/device/default/Android.bp
index b9f10d6..5fbcb5d 100644
--- a/camera/device/default/Android.bp
+++ b/camera/device/default/Android.bp
@@ -40,7 +40,7 @@
     shared_libs: [
         "android.hardware.camera.common-V1-ndk",
         "android.hardware.camera.device-V1-ndk",
-        "android.hardware.graphics.allocator-V1-ndk",
+        "android.hardware.graphics.allocator-V2-ndk",
         "android.hardware.graphics.mapper@2.0",
         "android.hardware.graphics.mapper@3.0",
         "android.hardware.graphics.mapper@4.0",
@@ -60,6 +60,7 @@
         "libsync",
         "libtinyxml2",
         "libutils",
+        "libui",
         "libyuv",
     ],
     static_libs: [
diff --git a/camera/device/default/ExternalCameraDeviceSession.cpp b/camera/device/default/ExternalCameraDeviceSession.cpp
index 95a36f0..a6ec4c7 100644
--- a/camera/device/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/default/ExternalCameraDeviceSession.cpp
@@ -789,9 +789,8 @@
                 outputBuffer.bufferId = buffer.bufferId;
                 outputBuffer.status = BufferStatus::ERROR;
                 if (buffer.acquireFence >= 0) {
-                    native_handle_t* handle = native_handle_create(/*numFds*/ 1, /*numInts*/ 0);
-                    handle->data[0] = buffer.acquireFence;
-                    outputBuffer.releaseFence = android::makeToAidl(handle);
+                    outputBuffer.releaseFence.fds.resize(1);
+                    outputBuffer.releaseFence.fds.at(0).set(buffer.acquireFence);
                 }
             } else {
                 offlineBuffers.push_back(buffer);
@@ -1769,9 +1768,8 @@
         result.outputBuffers[i].bufferId = req->buffers[i].bufferId;
         result.outputBuffers[i].status = BufferStatus::ERROR;
         if (req->buffers[i].acquireFence >= 0) {
-            native_handle_t* handle = native_handle_create(/*numFds*/ 1, /*numInts*/ 0);
-            handle->data[0] = req->buffers[i].acquireFence;
-            result.outputBuffers[i].releaseFence = ::android::makeToAidl(handle);
+            result.outputBuffers[i].releaseFence.fds.resize(1);
+            result.outputBuffers[i].releaseFence.fds.at(0).set(req->buffers[i].acquireFence);
         }
     }
 
@@ -1815,18 +1813,16 @@
         if (req->buffers[i].fenceTimeout) {
             result.outputBuffers[i].status = BufferStatus::ERROR;
             if (req->buffers[i].acquireFence >= 0) {
-                native_handle_t* handle = native_handle_create(/*numFds*/ 1, /*numInts*/ 0);
-                handle->data[0] = req->buffers[i].acquireFence;
-                result.outputBuffers[i].releaseFence = ::android::makeToAidl(handle);
+                result.outputBuffers[i].releaseFence.fds.resize(1);
+                result.outputBuffers[i].releaseFence.fds.at(0).set(req->buffers[i].acquireFence);
             }
             notifyError(req->frameNumber, req->buffers[i].streamId, ErrorCode::ERROR_BUFFER);
         } else {
             result.outputBuffers[i].status = BufferStatus::OK;
             // TODO: refactor
             if (req->buffers[i].acquireFence >= 0) {
-                native_handle_t* handle = native_handle_create(/*numFds*/ 1, /*numInts*/ 0);
-                handle->data[0] = req->buffers[i].acquireFence;
-                result.outputBuffers[i].releaseFence = ::android::makeToAidl(handle);
+                result.outputBuffers[i].releaseFence.fds.resize(1);
+                result.outputBuffers[i].releaseFence.fds.at(0).set(req->buffers[i].acquireFence);
             }
         }
     }
@@ -2882,13 +2878,23 @@
             } break;
             case PixelFormat::YCBCR_420_888:
             case PixelFormat::YV12: {
-                IMapper::Rect outRect{0, 0, static_cast<int32_t>(halBuf.width),
+                android::Rect outRect{0, 0, static_cast<int32_t>(halBuf.width),
                                       static_cast<int32_t>(halBuf.height)};
-                YCbCrLayout outLayout = sHandleImporter.lockYCbCr(
+                android_ycbcr result = sHandleImporter.lockYCbCr(
                         *(halBuf.bufPtr), static_cast<uint64_t>(halBuf.usage), outRect);
-                ALOGV("%s: outLayout y %p cb %p cr %p y_str %d c_str %d c_step %d", __FUNCTION__,
-                      outLayout.y, outLayout.cb, outLayout.cr, outLayout.yStride, outLayout.cStride,
-                      outLayout.chromaStep);
+                ALOGV("%s: outLayout y %p cb %p cr %p y_str %zu c_str %zu c_step %zu", __FUNCTION__,
+                      result.y, result.cb, result.cr, result.ystride, result.cstride,
+                      result.chroma_step);
+                if (result.ystride > UINT32_MAX || result.cstride > UINT32_MAX ||
+                    result.chroma_step > UINT32_MAX) {
+                    return onDeviceError("%s: lockYCbCr failed. Unexpected values!", __FUNCTION__);
+                }
+                YCbCrLayout outLayout = {.y = result.y,
+                                         .cb = result.cb,
+                                         .cr = result.cr,
+                                         .yStride = static_cast<uint32_t>(result.ystride),
+                                         .cStride = static_cast<uint32_t>(result.cstride),
+                                         .chromaStep = static_cast<uint32_t>(result.chroma_step)};
 
                 // Convert to output buffer size/format
                 uint32_t outputFourcc = getFourCcFromLayout(outLayout);
diff --git a/camera/device/default/ExternalCameraDeviceSession.h b/camera/device/default/ExternalCameraDeviceSession.h
index 836266f..736bfd1 100644
--- a/camera/device/default/ExternalCameraDeviceSession.h
+++ b/camera/device/default/ExternalCameraDeviceSession.h
@@ -24,6 +24,9 @@
 #include <aidl/android/hardware/camera/device/BufferRequest.h>
 #include <aidl/android/hardware/camera/device/Stream.h>
 #include <android-base/unique_fd.h>
+#include <android/hardware/graphics/mapper/2.0/IMapper.h>
+#include <android/hardware/graphics/mapper/3.0/IMapper.h>
+#include <android/hardware/graphics/mapper/4.0/IMapper.h>
 #include <fmq/AidlMessageQueue.h>
 #include <utils/Thread.h>
 #include <deque>
@@ -55,6 +58,7 @@
 using ::android::hardware::camera::common::helper::SimpleThread;
 using ::android::hardware::camera::external::common::ExternalCameraConfig;
 using ::android::hardware::camera::external::common::SizeHasher;
+using ::android::hardware::graphics::mapper::V2_0::YCbCrLayout;
 using ::ndk::ScopedAStatus;
 
 class ExternalCameraDeviceSession : public BnCameraDeviceSession, public OutputThreadInterface {
diff --git a/camera/device/default/ExternalCameraOfflineSession.cpp b/camera/device/default/ExternalCameraOfflineSession.cpp
index 4c7f732..53bd44f 100644
--- a/camera/device/default/ExternalCameraOfflineSession.cpp
+++ b/camera/device/default/ExternalCameraOfflineSession.cpp
@@ -486,13 +486,23 @@
             } break;
             case PixelFormat::YCBCR_420_888:
             case PixelFormat::YV12: {
-                IMapper::Rect outRect{0, 0, static_cast<int32_t>(halBuf.width),
+                android::Rect outRect{0, 0, static_cast<int32_t>(halBuf.width),
                                       static_cast<int32_t>(halBuf.height)};
-                YCbCrLayout outLayout = sHandleImporter.lockYCbCr(
+                android_ycbcr result = sHandleImporter.lockYCbCr(
                         *(halBuf.bufPtr), static_cast<uint64_t>(halBuf.usage), outRect);
-                ALOGV("%s: outLayout y %p cb %p cr %p y_str %d c_str %d c_step %d", __FUNCTION__,
-                      outLayout.y, outLayout.cb, outLayout.cr, outLayout.yStride, outLayout.cStride,
-                      outLayout.chromaStep);
+                ALOGV("%s: outLayout y %p cb %p cr %p y_str %zu c_str %zu c_step %zu", __FUNCTION__,
+                      result.y, result.cb, result.cr, result.ystride, result.cstride,
+                      result.chroma_step);
+                if (result.ystride > UINT32_MAX || result.cstride > UINT32_MAX ||
+                    result.chroma_step > UINT32_MAX) {
+                    return onDeviceError("%s: lockYCbCr failed. Unexpected values!", __FUNCTION__);
+                }
+                YCbCrLayout outLayout = {.y = result.y,
+                                         .cb = result.cb,
+                                         .cr = result.cr,
+                                         .yStride = static_cast<uint32_t>(result.ystride),
+                                         .cStride = static_cast<uint32_t>(result.cstride),
+                                         .chromaStep = static_cast<uint32_t>(result.chroma_step)};
 
                 // Convert to output buffer size/format
                 uint32_t outputFourcc = getFourCcFromLayout(outLayout);
@@ -544,4 +554,4 @@
 }  // namespace device
 }  // namespace camera
 }  // namespace hardware
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/camera/device/default/ExternalCameraUtils.cpp b/camera/device/default/ExternalCameraUtils.cpp
index cfb95f2..30c216f 100644
--- a/camera/device/default/ExternalCameraUtils.cpp
+++ b/camera/device/default/ExternalCameraUtils.cpp
@@ -402,7 +402,10 @@
         buffer_handle_t buf,
         /*out*/ buffer_handle_t** outBufPtr) {
     using ::aidl::android::hardware::camera::common::Status;
-    if (buf == nullptr && bufId == BUFFER_ID_NO_BUFFER) {
+    // AIDL does not have null NativeHandles. It sends empty handles instead.
+    // We check for when the buf is empty instead of when buf is null.
+    bool isBufEmpty = buf == nullptr || (buf->numFds == 0 && buf->numInts == 0);
+    if (isBufEmpty && bufId == BUFFER_ID_NO_BUFFER) {
         ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId);
         return Status::ILLEGAL_ARGUMENT;
     }
@@ -857,4 +860,4 @@
 }  // namespace device
 }  // namespace camera
 }  // namespace hardware
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/camera/device/default/ExternalCameraUtils.h b/camera/device/default/ExternalCameraUtils.h
index b37933c..d434905 100644
--- a/camera/device/default/ExternalCameraUtils.h
+++ b/camera/device/default/ExternalCameraUtils.h
@@ -25,7 +25,11 @@
 #include <aidl/android/hardware/camera/device/NotifyMsg.h>
 #include <aidl/android/hardware/graphics/common/BufferUsage.h>
 #include <aidl/android/hardware/graphics/common/PixelFormat.h>
+#include <android/hardware/graphics/mapper/2.0/IMapper.h>
+#include <android/hardware/graphics/mapper/3.0/IMapper.h>
+#include <android/hardware/graphics/mapper/4.0/IMapper.h>
 #include <tinyxml2.h>
+#include <map>
 #include <unordered_map>
 #include <unordered_set>
 
@@ -37,6 +41,8 @@
 using ::aidl::android::hardware::graphics::common::PixelFormat;
 using ::android::hardware::camera::common::V1_0::helper::CameraMetadata;
 using ::android::hardware::camera::common::V1_0::helper::HandleImporter;
+using ::android::hardware::graphics::mapper::V2_0::IMapper;
+using ::android::hardware::graphics::mapper::V2_0::YCbCrLayout;
 
 namespace android {
 namespace hardware {
diff --git a/camera/metadata/aidl/Android.bp b/camera/metadata/aidl/Android.bp
index 5872a86..2b2be55 100644
--- a/camera/metadata/aidl/Android.bp
+++ b/camera/metadata/aidl/Android.bp
@@ -11,7 +11,7 @@
     name: "android.hardware.camera.metadata",
     vendor_available: true,
     srcs: ["android/hardware/camera/metadata/*.aidl"],
-    frozen: true,
+    frozen: false,
     stability: "vintf",
     backend: {
         cpp: {
diff --git a/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataTag.aidl b/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataTag.aidl
index 71d4e41..0290aef 100644
--- a/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataTag.aidl
+++ b/camera/metadata/aidl/aidl_api/android.hardware.camera.metadata/current/android/hardware/camera/metadata/CameraMetadataTag.aidl
@@ -108,6 +108,11 @@
   ANDROID_FLASH_COLOR_TEMPERATURE,
   ANDROID_FLASH_MAX_ENERGY,
   ANDROID_FLASH_STATE,
+  ANDROID_FLASH_STRENGTH_LEVEL,
+  ANDROID_FLASH_SINGLE_STRENGTH_MAX_LEVEL,
+  ANDROID_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL,
+  ANDROID_FLASH_TORCH_STRENGTH_MAX_LEVEL,
+  ANDROID_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL,
   ANDROID_FLASH_INFO_AVAILABLE = android.hardware.camera.metadata.CameraMetadataSectionStart.ANDROID_FLASH_INFO_START /* 327680 */,
   ANDROID_FLASH_INFO_CHARGE_DURATION,
   ANDROID_FLASH_INFO_STRENGTH_MAXIMUM_LEVEL,
diff --git a/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataTag.aidl b/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataTag.aidl
index 56aa690..cfc9907 100644
--- a/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataTag.aidl
+++ b/camera/metadata/aidl/android/hardware/camera/metadata/CameraMetadataTag.aidl
@@ -560,6 +560,36 @@
      */
     ANDROID_FLASH_STATE,
     /**
+     * android.flash.strengthLevel [dynamic, int32, public]
+     *
+     * <p>Flash strength level to be used when manual flash control is active.</p>
+     */
+    ANDROID_FLASH_STRENGTH_LEVEL,
+    /**
+     * android.flash.singleStrengthMaxLevel [static, int32, public]
+     *
+     * <p>Maximum flash brightness level for manual flash control in SINGLE mode.</p>
+     */
+    ANDROID_FLASH_SINGLE_STRENGTH_MAX_LEVEL,
+    /**
+     * android.flash.singleStrengthDefaultLevel [static, int32, public]
+     *
+     * <p>Default flash brightness level for manual flash control in SINGLE mode.</p>
+     */
+    ANDROID_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL,
+    /**
+     * android.flash.torchStrengthMaxLevel [static, int32, public]
+     *
+     * <p>Maximum flash brightness level for manual flash control in TORCH mode</p>
+     */
+    ANDROID_FLASH_TORCH_STRENGTH_MAX_LEVEL,
+    /**
+     * android.flash.torchStrengthDefaultLevel [static, int32, public]
+     *
+     * <p>Default flash brightness level for manual flash control in TORCH mode</p>
+     */
+    ANDROID_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL,
+    /**
      * android.flash.info.available [static, enum, public]
      *
      * <p>Whether this camera device has a
@@ -1300,8 +1330,8 @@
     /**
      * android.sensor.frameDuration [dynamic, int64, public]
      *
-     * <p>Duration from start of frame exposure to
-     * start of next frame exposure.</p>
+     * <p>Duration from start of frame readout to
+     * start of next frame readout.</p>
      */
     ANDROID_SENSOR_FRAME_DURATION,
     /**
diff --git a/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp b/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp
index 04db7f3..2d919cc 100644
--- a/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp
+++ b/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp
@@ -95,13 +95,14 @@
 
 Return<Status> ExternalCameraProviderImpl_2_4::setCallback(
         const sp<ICameraProviderCallback>& callback) {
+    if (callback == nullptr) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
+
     {
         Mutex::Autolock _l(mLock);
         mCallbacks = callback;
     }
-    if (mCallbacks == nullptr) {
-        return Status::OK;
-    }
     // Send a callback for all devices to initialize
     {
         for (const auto& pair : mCameraStatusMap) {
diff --git a/camera/provider/2.4/default/LegacyCameraProviderImpl_2_4.cpp b/camera/provider/2.4/default/LegacyCameraProviderImpl_2_4.cpp
index 69318c7..07ed689 100644
--- a/camera/provider/2.4/default/LegacyCameraProviderImpl_2_4.cpp
+++ b/camera/provider/2.4/default/LegacyCameraProviderImpl_2_4.cpp
@@ -448,11 +448,11 @@
 // Methods from ::android::hardware::camera::provider::V2_4::ICameraProvider follow.
 Return<Status> LegacyCameraProviderImpl_2_4::setCallback(
         const sp<ICameraProviderCallback>& callback) {
+    if (callback == nullptr) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
     Mutex::Autolock _l(mCbLock);
     mCallbacks = callback;
-    if (mCallbacks == nullptr) {
-        return Status::OK;
-    }
     // Add and report all presenting external cameras.
     for (auto const& statusPair : mCameraStatusMap) {
         int id = std::stoi(statusPair.first);
diff --git a/camera/provider/2.7/default/ExternalCameraProviderImpl_2_7.cpp b/camera/provider/2.7/default/ExternalCameraProviderImpl_2_7.cpp
index 62ce074..eba49a5 100644
--- a/camera/provider/2.7/default/ExternalCameraProviderImpl_2_7.cpp
+++ b/camera/provider/2.7/default/ExternalCameraProviderImpl_2_7.cpp
@@ -91,11 +91,11 @@
 
 Return<Status> ExternalCameraProviderImpl_2_7::setCallback(
         const sp<ICameraProviderCallback>& callback) {
+    if (callback == nullptr) {
+        return Status::ILLEGAL_ARGUMENT;
+    }
     Mutex::Autolock _l(mLock);
     mCallbacks = callback;
-    if (mCallbacks == nullptr) {
-        return Status::OK;
-    }
     // Send a callback for all devices to initialize
     {
         for (const auto& pair : mCameraStatusMap) {
diff --git a/camera/provider/aidl/vts/Android.bp b/camera/provider/aidl/vts/Android.bp
index 59f6c66..cfcaec5 100644
--- a/camera/provider/aidl/vts/Android.bp
+++ b/camera/provider/aidl/vts/Android.bp
@@ -53,6 +53,7 @@
         "android.hardware.graphics.mapper@2.0",
         "android.hardware.graphics.mapper@3.0",
         "android.hardware.graphics.mapper@4.0",
+        "camera_platform_flags_c_lib",
     ],
 
     // Statically link to libs not guaranteed to be present on the device.
@@ -67,6 +68,7 @@
         "libhidlmemory",
         "libgralloctypes",
         "libaidlcommonsupport",
+        "libgtest",
     ],
 
     require_root: true,
diff --git a/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp b/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
index b6b5206..def6233 100644
--- a/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
+++ b/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <gtest/gtest.h>
 
 #include <aidl/Vintf.h>
 #include <aidl/android/hardware/camera/common/VendorTagSection.h>
@@ -29,6 +30,7 @@
 #include <hidl/GtestPrinter.h>
 #include <hidl/HidlSupport.h>
 #include <torch_provider_cb.h>
+#include <com_android_internal_camera_flags.h>
 #include <list>
 
 using ::aidl::android::hardware::camera::common::CameraDeviceStatus;
@@ -50,6 +52,7 @@
 const uint32_t kMaxStillHeight = 1536;
 
 const int64_t kEmptyFlushTimeoutMSec = 200;
+namespace flags = com::android::internal::camera::flags;
 
 const static std::vector<int64_t> kMandatoryUseCases = {
         ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT,
@@ -159,6 +162,28 @@
     }
 }
 
+// Validate the integrity of manual flash strength control metadata
+TEST_P(CameraAidlTest, validateManualFlashStrengthControlKeys) {
+    if (flags::camera_manual_flash_strength_control()) {
+        std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider);
+        for (const auto& name : cameraDeviceNames) {
+            ALOGI("validateManualFlashStrengthControlKeys: Testing camera device %s", name.c_str());
+            CameraMetadata meta;
+            std::shared_ptr<ICameraDevice> cameraDevice;
+            openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/,
+                    &cameraDevice /*out*/);
+            ndk::ScopedAStatus ret = cameraDevice->getCameraCharacteristics(&meta);
+            ASSERT_TRUE(ret.isOk());
+            const camera_metadata_t* staticMeta =
+                    reinterpret_cast<const camera_metadata_t*>(meta.metadata.data());
+            verifyManualFlashStrengthControlCharacteristics(staticMeta);
+        }
+    } else {
+        ALOGI("validateManualFlashStrengthControlKeys: Test skipped.\n");
+        GTEST_SKIP();
+    }
+}
+
 TEST_P(CameraAidlTest, systemCameraTest) {
     std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider);
     std::map<std::string, std::vector<SystemCameraKind>> hiddenPhysicalIdToLogicalMap;
diff --git a/camera/provider/aidl/vts/camera_aidl_test.cpp b/camera/provider/aidl/vts/camera_aidl_test.cpp
index 6a17453..e5bf637 100644
--- a/camera/provider/aidl/vts/camera_aidl_test.cpp
+++ b/camera/provider/aidl/vts/camera_aidl_test.cpp
@@ -1152,6 +1152,58 @@
     }
 }
 
+void CameraAidlTest::verifyManualFlashStrengthControlCharacteristics(
+        const camera_metadata_t* staticMeta) {
+    camera_metadata_ro_entry singleMaxEntry;
+    camera_metadata_ro_entry singleDefEntry;
+    camera_metadata_ro_entry torchMaxEntry;
+    camera_metadata_ro_entry torchDefEntry;
+    bool torch_supported = false;
+    int32_t singleMaxLevel = 0;
+    int32_t singleDefLevel = 0;
+    int32_t torchMaxLevel = 0;
+    int32_t torchDefLevel = 0;
+
+    // determine whether the device supports torch or not
+    torch_supported = isTorchSupported(staticMeta);
+
+    int singleMaxRetCode = find_camera_metadata_ro_entry(staticMeta,
+            ANDROID_FLASH_SINGLE_STRENGTH_MAX_LEVEL, &singleMaxEntry);
+    int singleDefRetCode = find_camera_metadata_ro_entry(staticMeta,
+            ANDROID_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL, &singleDefEntry);
+    int torchMaxRetCode = find_camera_metadata_ro_entry(staticMeta,
+            ANDROID_FLASH_TORCH_STRENGTH_MAX_LEVEL, &torchMaxEntry);
+    int torchDefRetCode = find_camera_metadata_ro_entry(staticMeta,
+            ANDROID_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL, &torchDefEntry);
+    if (torch_supported) {
+        if(singleMaxRetCode == 0 && singleDefRetCode == 0 && torchMaxRetCode == 0 &&
+                torchDefRetCode == 0) {
+            singleMaxLevel = *singleMaxEntry.data.i32;
+            singleDefLevel = *singleDefEntry.data.i32;
+            torchMaxLevel = *torchMaxEntry.data.i32;
+            torchDefLevel = *torchDefEntry.data.i32;
+            ASSERT_TRUE((singleMaxEntry.count == singleDefEntry.count == torchMaxEntry.count
+                    == torchDefEntry.count == 1));
+        } else {
+            ASSERT_TRUE((singleMaxEntry.count == singleDefEntry.count == torchMaxEntry.count
+                    == torchDefEntry.count == 0));
+        }
+        // if the device supports this feature default levels should be greater than 0
+        if (singleMaxLevel > 1) {
+            ASSERT_GT(torchMaxLevel, 1);
+            ASSERT_GT(torchDefLevel, 0);
+            ASSERT_GT(singleDefLevel, 0);
+            ASSERT_TRUE(torchDefLevel <= torchMaxLevel); // default levels should be <= max levels
+            ASSERT_TRUE(singleDefLevel <= singleMaxLevel);
+        }
+    } else {
+        ASSERT_TRUE(singleMaxRetCode != 0);
+        ASSERT_TRUE(singleDefRetCode != 0);
+        ASSERT_TRUE(torchMaxRetCode != 0);
+        ASSERT_TRUE(torchDefRetCode != 0);
+    }
+}
+
 void CameraAidlTest::verifyRecommendedConfigs(const CameraMetadata& chars) {
     size_t CONFIG_ENTRY_SIZE = 5;
     size_t CONFIG_ENTRY_TYPE_OFFSET = 3;
diff --git a/camera/provider/aidl/vts/camera_aidl_test.h b/camera/provider/aidl/vts/camera_aidl_test.h
index 3018d5a..0ebd4ef 100644
--- a/camera/provider/aidl/vts/camera_aidl_test.h
+++ b/camera/provider/aidl/vts/camera_aidl_test.h
@@ -262,6 +262,9 @@
 
     static void verifyMonochromeCharacteristics(const CameraMetadata& chars);
 
+    static void verifyManualFlashStrengthControlCharacteristics(
+            const camera_metadata_t* staticMeta);
+
     static void verifyMonochromeCameraResult(
             const ::android::hardware::camera::common::V1_0::helper::CameraMetadata& metadata);
 
diff --git a/camera/provider/default/ExternalCameraProvider.cpp b/camera/provider/default/ExternalCameraProvider.cpp
index 4d2c847..54875ab 100644
--- a/camera/provider/default/ExternalCameraProvider.cpp
+++ b/camera/provider/default/ExternalCameraProvider.cpp
@@ -75,15 +75,15 @@
 
 ndk::ScopedAStatus ExternalCameraProvider::setCallback(
         const std::shared_ptr<ICameraProviderCallback>& in_callback) {
+    if (in_callback == nullptr) {
+        return fromStatus(Status::ILLEGAL_ARGUMENT);
+    }
+
     {
         Mutex::Autolock _l(mLock);
         mCallback = in_callback;
     }
 
-    if (mCallback == nullptr) {
-        return fromStatus(Status::OK);
-    }
-
     for (const auto& pair : mCameraStatusMap) {
         mCallback->cameraDeviceStatusChange(pair.first, pair.second);
     }
diff --git a/cas/aidl/default/Android.bp b/cas/aidl/default/Android.bp
index 9d094e0..06e167c 100644
--- a/cas/aidl/default/Android.bp
+++ b/cas/aidl/default/Android.bp
@@ -57,7 +57,6 @@
     shared_libs: [
         "libbinder_ndk",
         "liblog",
-        "libvndksupport",
     ],
     header_libs: ["media_plugin_headers"],
 }
@@ -81,7 +80,8 @@
 
 cc_fuzz {
     name: "android.hardware.cas-service_fuzzer",
-    vendor: true,
+    // TODO(b/307611931): avoid fuzzing on vendor until hermiticity issue is fixed
+    // vendor: true,
 
     defaults: ["service_fuzzer_defaults"],
     srcs: ["fuzzer.cpp"],
diff --git a/cas/aidl/default/SharedLibrary.cpp b/cas/aidl/default/SharedLibrary.cpp
index 6322ff3..c12d17d 100644
--- a/cas/aidl/default/SharedLibrary.cpp
+++ b/cas/aidl/default/SharedLibrary.cpp
@@ -19,7 +19,6 @@
 #include "SharedLibrary.h"
 #include <dlfcn.h>
 #include <utils/Log.h>
-#include <vndksupport/linker.h>
 
 namespace aidl {
 namespace android {
@@ -27,12 +26,12 @@
 namespace cas {
 
 SharedLibrary::SharedLibrary(const String8& path) {
-    mLibHandle = android_load_sphal_library(path.c_str(), RTLD_NOW);
+    mLibHandle = dlopen(path.c_str(), RTLD_NOW);
 }
 
 SharedLibrary::~SharedLibrary() {
     if (mLibHandle != NULL) {
-        android_unload_sphal_library(mLibHandle);
+        dlclose(mLibHandle);
         mLibHandle = NULL;
     }
 }
diff --git a/cas/aidl/default/manifest.json b/cas/aidl/default/manifest.json
index 16b4f67..cdcecb2 100644
--- a/cas/aidl/default/manifest.json
+++ b/cas/aidl/default/manifest.json
@@ -1,9 +1,8 @@
 {
     "name": "com.android.hardware.cas",
     "version": 1,
-    // For CAS HAL to open plugins from /vendor/lib, "vendor" namespace should be imported.
-    // ":sphal" is an alias for the "vendor" namespace in Vendor APEX.
+    // For CAS HAL to open plugins from /vendor/lib/mediacas
     "requireNativeLibs": [
-        ":sphal"
+        ":mediacas"
     ]
 }
diff --git a/cas/aidl/default/service.cpp b/cas/aidl/default/service.cpp
index bed2f01..076c7bb 100644
--- a/cas/aidl/default/service.cpp
+++ b/cas/aidl/default/service.cpp
@@ -31,6 +31,7 @@
 
 int main() {
     ABinderProcess_setThreadPoolMaxThreadCount(8);
+    ABinderProcess_startThreadPool();
 
     // Setup hwbinder service
     std::shared_ptr<MediaCasService> service = ::ndk::SharedRefBase::make<MediaCasService>();
diff --git a/common/aidl/Android.bp b/common/aidl/Android.bp
index f3ea8e8..1457b8a 100644
--- a/common/aidl/Android.bp
+++ b/common/aidl/Android.bp
@@ -38,7 +38,7 @@
         },
         rust: {
             enabled: true,
-        }
+        },
     },
     frozen: true,
     versions: [
diff --git a/common/fmq/aidl/Android.bp b/common/fmq/aidl/Android.bp
index 3b022fc..6c37213 100644
--- a/common/fmq/aidl/Android.bp
+++ b/common/fmq/aidl/Android.bp
@@ -43,8 +43,9 @@
             min_sdk_version: "29",
         },
         rust: {
-            // FMQ is not supported in the rust backend
-            enabled: false,
+            // FMQ is not supported in the rust backend, but we need this AIDL interface for
+            // HardwareBuffer.
+            enabled: true,
         },
     },
     frozen: true,
diff --git a/compatibility_matrices/compatibility_matrix.8.xml b/compatibility_matrices/compatibility_matrix.8.xml
index 2a1f4a5..9057788 100644
--- a/compatibility_matrices/compatibility_matrix.8.xml
+++ b/compatibility_matrices/compatibility_matrix.8.xml
@@ -360,6 +360,18 @@
             <instance>software</instance>
         </interface>
     </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.media.omx</name>
+        <version>1.0</version>
+        <interface>
+            <name>IOmx</name>
+            <instance>default</instance>
+        </interface>
+        <interface>
+            <name>IOmxStore</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.memtrack</name>
         <version>1</version>
diff --git a/compatibility_matrices/compatibility_matrix.9.xml b/compatibility_matrices/compatibility_matrix.9.xml
index b0390e6..2a8b772 100644
--- a/compatibility_matrices/compatibility_matrix.9.xml
+++ b/compatibility_matrices/compatibility_matrix.9.xml
@@ -19,7 +19,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.audio.core</name>
-        <version>1</version>
+        <version>1-2</version>
         <interface>
             <name>IModule</name>
             <instance>default</instance>
@@ -38,7 +38,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.audio.effect</name>
-        <version>1</version>
+        <version>1-2</version>
         <interface>
             <name>IFactory</name>
             <instance>default</instance>
@@ -46,7 +46,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.audio.sounddose</name>
-        <version>1</version>
+        <version>1-2</version>
         <interface>
             <name>ISoundDoseFactory</name>
             <instance>default</instance>
@@ -62,7 +62,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.automotive.audiocontrol</name>
-        <version>2-3</version>
+        <version>2-4</version>
         <interface>
             <name>IAudioControl</name>
             <instance>default</instance>
@@ -125,7 +125,7 @@
     </hal>
     <hal format="aidl" optional="true" updatable-via-apex="true">
         <name>android.hardware.biometrics.face</name>
-        <version>3</version>
+        <version>3-4</version>
         <interface>
             <name>IFace</name>
             <instance>default</instance>
@@ -150,7 +150,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.bluetooth.audio</name>
-        <version>3</version>
+        <version>3-4</version>
         <interface>
             <name>IBluetoothAudioProviderFactory</name>
             <instance>default</instance>
@@ -165,6 +165,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.broadcastradio</name>
+        <version>1-2</version>
         <interface>
             <name>IBroadcastRadio</name>
             <regex-instance>.*</regex-instance>
@@ -248,15 +249,6 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <!-- Either the native or the HIDL mapper HAL must exist on the device -->
-    <hal format="hidl" optional="true">
-        <name>android.hardware.graphics.mapper</name>
-        <version>4.0</version>
-        <interface>
-            <name>IMapper</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.health</name>
         <version>1-2</version>
@@ -494,16 +486,6 @@
         </interface>
     </hal>
     <hal format="aidl" optional="true">
-        <name>android.hardware.radio.satellite</name>
-        <version>1</version>
-        <interface>
-            <name>IRadioSatellite</name>
-            <instance>slot1</instance>
-            <instance>slot2</instance>
-            <instance>slot3</instance>
-        </interface>
-    </hal>
-    <hal format="aidl" optional="true">
         <name>android.hardware.radio.ims.media</name>
         <version>2</version>
         <interface>
@@ -529,6 +511,14 @@
         </interface>
     </hal>
     <hal format="aidl" optional="true" updatable-via-apex="true">
+        <name>android.hardware.security.authgraph</name>
+        <version>1</version>
+        <interface>
+            <name>IAuthGraphKeyExchange</name>
+            <instance>nonsecure</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true" updatable-via-apex="true">
         <name>android.hardware.security.secureclock</name>
         <version>1</version>
         <interface>
@@ -555,7 +545,7 @@
     </hal>
     <hal format="aidl" optional="true">
          <name>android.hardware.soundtrigger3</name>
-         <version>1</version>
+         <version>1-2</version>
          <interface>
              <name>ISoundTriggerHw</name>
              <instance>default</instance>
@@ -619,7 +609,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.tv.input</name>
-        <version>1</version>
+        <version>1-2</version>
         <interface>
             <name>ITvInput</name>
             <instance>default</instance>
@@ -627,7 +617,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.usb</name>
-        <version>1-2</version>
+        <version>1-3</version>
         <interface>
             <name>IUsb</name>
             <instance>default</instance>
@@ -666,7 +656,7 @@
     </hal>
     <hal format="aidl" optional="true" updatable-via-apex="true">
         <name>android.hardware.wifi</name>
-        <version>1</version>
+        <version>1-2</version>
         <interface>
             <name>IWifi</name>
             <instance>default</instance>
@@ -690,13 +680,13 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.wifi.supplicant</name>
-        <version>2</version>
+        <version>2-3</version>
         <interface>
             <name>ISupplicant</name>
             <instance>default</instance>
         </interface>
     </hal>
-    <!-- Either the native or the HIDL mapper HAL must exist on the device -->
+    <!-- The native mapper HAL must exist on the device -->
     <hal format="native" optional="true">
         <name>mapper</name>
         <version>5.0</version>
diff --git a/compatibility_matrices/exclude/fcm_exclude.cpp b/compatibility_matrices/exclude/fcm_exclude.cpp
index d92c0b9..46f0e03 100644
--- a/compatibility_matrices/exclude/fcm_exclude.cpp
+++ b/compatibility_matrices/exclude/fcm_exclude.cpp
@@ -128,6 +128,7 @@
             "android.hardware.media.bufferpool2@",
             "android.hardware.radio@",
             "android.hardware.uwb.fira_android@",
+            "android.hardware.wifi.common@",
 
             // Test packages are exempted.
             "android.hardware.tests.",
@@ -142,6 +143,7 @@
 
             // AIDL
             "android.hardware.audio.core.sounddose@1",
+            "android.hardware.audio.core.sounddose@2",
 
             // Deprecated HALs.
             "android.hardware.bluetooth.audio@1",
diff --git a/drm/1.0/default/DrmPlugin.cpp b/drm/1.0/default/DrmPlugin.cpp
index dfa5d22..0a0a350 100644
--- a/drm/1.0/default/DrmPlugin.cpp
+++ b/drm/1.0/default/DrmPlugin.cpp
@@ -133,8 +133,8 @@
         Vector<KeyValue> infoMapVec;
         for (size_t i = 0; i < legacyInfoMap.size(); i++) {
             KeyValue keyValuePair;
-            keyValuePair.key = String8(legacyInfoMap.keyAt(i));
-            keyValuePair.value = String8(legacyInfoMap.valueAt(i));
+            keyValuePair.key = legacyInfoMap.keyAt(i);
+            keyValuePair.value = legacyInfoMap.valueAt(i);
             infoMapVec.push_back(keyValuePair);
         }
         _hidl_cb(toStatus(status), toHidlVec(infoMapVec));
diff --git a/drm/1.0/default/include/PluginLoader.h b/drm/1.0/default/include/PluginLoader.h
index 5130b16..a25dd41 100644
--- a/drm/1.0/default/include/PluginLoader.h
+++ b/drm/1.0/default/include/PluginLoader.h
@@ -50,7 +50,7 @@
                 String8 file(pEntry->d_name);
                 if (base::EndsWith(file.c_str(), ".so")) {
                     String8 path = pluginDir + "/" + pEntry->d_name;
-                    T *plugin = loadOne(path, entry);
+                    T* plugin = loadOne(path.c_str(), entry);
                     if (plugin) {
                         factories.push(plugin);
                     }
diff --git a/drm/1.0/vts/functional/Android.bp b/drm/1.0/vts/functional/Android.bp
index d6c37c5..e0c6fa5 100644
--- a/drm/1.0/vts/functional/Android.bp
+++ b/drm/1.0/vts/functional/Android.bp
@@ -111,13 +111,13 @@
             data: [":libvtswidevine-arm-prebuilts"],
         },
         arm64: {
-            data: [":libvtswidevine-arm64-prebuilts"],
+            data: [":libvtswidevine-arm64-prebuilts",":libvtswidevine-arm-prebuilts"],
         },
         x86: {
             data: [":libvtswidevine-x86-prebuilts"],
         },
         x86_64: {
-            data: [":libvtswidevine-x86_64-prebuilts"],
+            data: [":libvtswidevine-x86_64-prebuilts",":libvtswidevine-x86-prebuilts"],
         },
     },
     test_suites: [
diff --git a/drm/1.1/vts/functional/Android.bp b/drm/1.1/vts/functional/Android.bp
index 760b67e..b539fa2 100644
--- a/drm/1.1/vts/functional/Android.bp
+++ b/drm/1.1/vts/functional/Android.bp
@@ -82,13 +82,13 @@
             data: [":libvtswidevine-arm-prebuilts"],
         },
         arm64: {
-            data: [":libvtswidevine-arm64-prebuilts"],
+            data: [":libvtswidevine-arm64-prebuilts",":libvtswidevine-arm-prebuilts"],
         },
         x86: {
             data: [":libvtswidevine-x86-prebuilts"],
         },
         x86_64: {
-            data: [":libvtswidevine-x86_64-prebuilts"],
+            data: [":libvtswidevine-x86_64-prebuilts",":libvtswidevine-x86-prebuilts"],
         },
     },
     test_suites: [
diff --git a/drm/1.2/vts/functional/Android.bp b/drm/1.2/vts/functional/Android.bp
index 9a45051..9ceb1a3 100644
--- a/drm/1.2/vts/functional/Android.bp
+++ b/drm/1.2/vts/functional/Android.bp
@@ -100,13 +100,13 @@
             data: [":libvtswidevine-arm-prebuilts"],
         },
         arm64: {
-            data: [":libvtswidevine-arm64-prebuilts"],
+            data: [":libvtswidevine-arm64-prebuilts",":libvtswidevine-arm-prebuilts"],
         },
         x86: {
             data: [":libvtswidevine-x86-prebuilts"],
         },
         x86_64: {
-            data: [":libvtswidevine-x86_64-prebuilts"],
+            data: [":libvtswidevine-x86_64-prebuilts",":libvtswidevine-x86-prebuilts"],
         },
     },
     test_suites: [
diff --git a/drm/1.3/vts/functional/Android.bp b/drm/1.3/vts/functional/Android.bp
index cd1cc0f..3db23e3 100644
--- a/drm/1.3/vts/functional/Android.bp
+++ b/drm/1.3/vts/functional/Android.bp
@@ -85,13 +85,13 @@
             data: [":libvtswidevine-arm-prebuilts"],
         },
         arm64: {
-            data: [":libvtswidevine-arm64-prebuilts"],
+            data: [":libvtswidevine-arm64-prebuilts",":libvtswidevine-arm-prebuilts"],
         },
         x86: {
             data: [":libvtswidevine-x86-prebuilts"],
         },
         x86_64: {
-            data: [":libvtswidevine-x86_64-prebuilts"],
+            data: [":libvtswidevine-x86_64-prebuilts",":libvtswidevine-x86-prebuilts"],
         },
     },
     test_suites: [
diff --git a/drm/1.4/vts/functional/Android.bp b/drm/1.4/vts/functional/Android.bp
index e18d088..89edab7 100644
--- a/drm/1.4/vts/functional/Android.bp
+++ b/drm/1.4/vts/functional/Android.bp
@@ -88,13 +88,13 @@
             data: [":libvtswidevine-arm-prebuilts"],
         },
         arm64: {
-            data: [":libvtswidevine-arm64-prebuilts"],
+            data: [":libvtswidevine-arm64-prebuilts",":libvtswidevine-arm-prebuilts"],
         },
         x86: {
             data: [":libvtswidevine-x86-prebuilts"],
         },
         x86_64: {
-            data: [":libvtswidevine-x86_64-prebuilts"],
+            data: [":libvtswidevine-x86_64-prebuilts",":libvtswidevine-x86-prebuilts"],
         },
     },
     test_suites: [
diff --git a/drm/aidl/OWNERS b/drm/aidl/OWNERS
deleted file mode 100644
index e69de29..0000000
--- a/drm/aidl/OWNERS
+++ /dev/null
diff --git a/drm/aidl/vts/Android.bp b/drm/aidl/vts/Android.bp
index 190f60d..5139036 100644
--- a/drm/aidl/vts/Android.bp
+++ b/drm/aidl/vts/Android.bp
@@ -59,13 +59,13 @@
             data: [":libvtswidevine-arm-prebuilts"],
         },
         arm64: {
-            data: [":libvtswidevine-arm64-prebuilts"],
+            data: [":libvtswidevine-arm64-prebuilts", ":libvtswidevine-arm-prebuilts"],
         },
         x86: {
             data: [":libvtswidevine-x86-prebuilts"],
         },
         x86_64: {
-            data: [":libvtswidevine-x86_64-prebuilts"],
+            data: [":libvtswidevine-x86_64-prebuilts", ":libvtswidevine-x86-prebuilts"],
         },
     },
     test_suites: [
diff --git a/drm/aidl/vts/OWNERS b/drm/aidl/vts/OWNERS
deleted file mode 100644
index e69de29..0000000
--- a/drm/aidl/vts/OWNERS
+++ /dev/null
diff --git a/gatekeeper/aidl/Android.bp b/gatekeeper/aidl/Android.bp
index b050f6a..169a7d5 100644
--- a/gatekeeper/aidl/Android.bp
+++ b/gatekeeper/aidl/Android.bp
@@ -25,6 +25,9 @@
         cpp: {
             enabled: false,
         },
+        rust: {
+            enabled: true,
+        },
     },
     versions_with_info: [
         {
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssNavigationMessageCallback.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssNavigationMessageCallback.aidl
index 57d0e68..f6a8fef 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssNavigationMessageCallback.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssNavigationMessageCallback.aidl
@@ -63,7 +63,12 @@
       BDS_CNAV2 = 0x0504,
       GAL_I = 0x0601,
       GAL_F = 0x0602,
+      /**
+       * @deprecated Use IRN_L5 instead.
+       */
       IRN_L5CA = 0x0701,
+      IRN_L5 = 0x0702,
+      IRN_L1 = 0x0703,
     }
   }
 }
diff --git a/gnss/aidl/android/hardware/gnss/GnssSignalType.aidl b/gnss/aidl/android/hardware/gnss/GnssSignalType.aidl
index c66d9e8..69de750 100644
--- a/gnss/aidl/android/hardware/gnss/GnssSignalType.aidl
+++ b/gnss/aidl/android/hardware/gnss/GnssSignalType.aidl
@@ -46,27 +46,28 @@
     double carrierFrequencyHz;
 
     /**
-     * GNSS signal code type "A" representing GALILEO E1A, GALILEO E6A, IRNSS L5A SPS, IRNSS SA SPS,
+     * GNSS signal code type "A" representing GALILEO E1A, GALILEO E6A, NavIC L5A SPS, NavIC SA SPS,
      * GLONASS G1a L1OCd, GLONASS G2a L2CSI.
      */
     const @utf8InCpp String CODE_TYPE_A = "A";
 
     /**
-     * GNSS signal code type "B" representing GALILEO E1B, GALILEO E6B, IRNSS L5B RS (D),
-     * IRNSS SB RS (D), GLONASS G1a L1OCp, GLONASS G2a L2OCp, QZSS L1Sb.
+     * GNSS signal code type "B" representing GALILEO E1B, GALILEO E6B, NavIC L5B RS (D),
+     * NavIC SB RS (D), GLONASS G1a L1OCp, GLONASS G2a L2OCp, QZSS L1Sb.
      */
     const @utf8InCpp String CODE_TYPE_B = "B";
 
     /**
      * GNSS signal code type "C" representing GPS L1 C/A,  GPS L2 C/A, GLONASS G1 C/A,
-     * GLONASS G2 C/A, GALILEO E1C, GALILEO E6C, SBAS L1 C/A, QZSS L1 C/A, IRNSS L5C RS (P),
-     * IRNSS SC RS (P).
+     * GLONASS G2 C/A, GALILEO E1C, GALILEO E6C, SBAS L1 C/A, QZSS L1 C/A, NavIC L5C RS (P),
+     * NavIC SC RS (P).
      */
     const @utf8InCpp String CODE_TYPE_C = "C";
 
     /**
      * GNSS signal code type "D" representing GPS L2 (L1(C/A) + (P2-P1) (semi-codeless)),
-     * QZSS L5S(I), BDS B1C Data, BDS B2a Data, BDS B2b Data, BDS B2 (B2a+B2b) Data, BDS B3a Data.
+     * QZSS L5S(I), BDS B1C Data, BDS B2a Data, BDS B2b Data, BDS B2 (B2a+B2b) Data, BDS B3a Data,
+     * NavIC L1 Data.
      */
     const @utf8InCpp String CODE_TYPE_D = "D";
 
@@ -100,7 +101,7 @@
     /**
      * GNSS signal code type "P" representing GPS L1P, GPS L2P, GLONASS G1P, GLONASS G2P,
      * BDS B1C Pilot, BDS B2a Pilot, BDS B2b Pilot, BDS B2 (B2a+B2b) Pilot, BDS B3a Pilot,
-     * QZSS L5S(Q).
+     * QZSS L5S(Q), NavIC L1 Pilot.
      */
     const @utf8InCpp String CODE_TYPE_P = "P";
 
@@ -127,7 +128,7 @@
      * GALILEO E5a (I+Q), GALILEO E5b (I+Q), GALILEO E5a+b (I+Q), GALILEO E6 (B+C), SBAS L5 (I+Q),
      * QZSS L1C (D+P), QZSS L2C (M+L), QZSS L5 (I+Q), QZSS L6 (D+P), BDS B1 (I+Q),
      * BDS B1C Data+Pilot, BDS B2a Data+Pilot, BDS B2 (I+Q), BDS B2 (B2a+B2b) Data+Pilot,
-     * BDS B3 (I+Q), IRNSS L5 (B+C), IRNSS S (B+C).
+     * BDS B3 (I+Q), NavIC L5 (B+C), NavIC S (B+C), NavIC L1 Data+Pilot.
      */
     const @utf8InCpp String CODE_TYPE_X = "X";
 
diff --git a/gnss/aidl/android/hardware/gnss/IGnssNavigationMessageCallback.aidl b/gnss/aidl/android/hardware/gnss/IGnssNavigationMessageCallback.aidl
index 6990e19..b224b0b 100644
--- a/gnss/aidl/android/hardware/gnss/IGnssNavigationMessageCallback.aidl
+++ b/gnss/aidl/android/hardware/gnss/IGnssNavigationMessageCallback.aidl
@@ -100,8 +100,17 @@
             /** Galileo F/NAV message contained in the structure. */
             GAL_F = 0x0602,
 
-            /** IRNSS L5 C/A message contained in the structure. */
+            /**
+             * NavIC L5 C/A message contained in the structure.
+             * @deprecated Use IRN_L5 instead.
+             */
             IRN_L5CA = 0x0701,
+
+            /** NavIC L5 message contained in the structure. */
+            IRN_L5 = 0x0702,
+
+            /** NavIC L1 message contained in the structure. */
+            IRN_L1 = 0x0703,
         }
 
         /**
@@ -156,9 +165,12 @@
          *
          * - For Beidou CNAV1 this refers to the page type number in the range of 1-63.
          *
-         * - For IRNSS L5 C/A subframe 3 and 4, this value corresponds to the Message Id of the
+         * - For NavIC L5 subframe 3 and 4, this value corresponds to the Message Id of the
          *   navigation message, in the range of 1-63. (Subframe 1 and 2 does not contain a message
          *   type id and this value can be set to -1.)
+         * - For NavIC L1 subframe 3, this value corresponds to the Message Id of the navigation
+         *   message, in the range of 1-63. (Subframe 1 and 2 does not contain a message type id and
+         *   this value can be set to -1.)
          */
         int messageId;
 
@@ -187,8 +199,10 @@
          *
          * - For Beidou CNAV2, the submessage id corresponds to the message type, in the range 1-63.
          *
-         * - For IRNSS L5 C/A, the submessage id corresponds to the subframe number of the
-         *   navigation message, in the range of 1-4.
+         * - For NavIC L5, the submessage id corresponds to the subframe number of the navigation
+         *   message, in the range of 1-4.
+         * - For NavIC L1, the submessage id corresponds to the subframe number of the navigation
+         *   message, in the range of 1-3.
          */
         int submessageId;
 
@@ -196,7 +210,7 @@
          * The data of the reported GNSS message. The bytes (or words) are specified
          * using big endian format (MSB first).
          *
-         * - For GNSS L1 C/A, Beidou D1 & Beidou D2, each subframe contains 10 30-bit
+         * - For GNSS L1 C/A, NavIC L5, Beidou D1 & Beidou D2, each subframe contains 10 30-bit
          *   words. Each word (30 bits) must fit into the last 30 bits in a
          *   4-byte word (skip B31 and B32), with MSB first, for a total of 40
          *   bytes, covering a time period of 6, 6, and 0.6 seconds, respectively.
@@ -228,6 +242,10 @@
          * - For Beidou CNAV2, each subframe consists of 288 data bits, that should be fit into 36
          *   bytes.
          *
+         * - For NavIC L1, subframe #1 consists of 9 data bits that should be fit into 2 bytes (skip
+         *   B10-B16). subframe #2 consists of 600 bits that should be fit into 75 bytes. subframe
+         *   #3 consists of 274 data bits that should be fit into 35 bytes (skip B275-B280).
+         *
          * The data reported here must be the raw data as demodulated by the GNSS receiver, not data
          * received from an external source (i.e. not from a server download.)
          */
diff --git a/gnss/aidl/default/Android.bp b/gnss/aidl/default/Android.bp
index 542796f..822e8fc 100644
--- a/gnss/aidl/default/Android.bp
+++ b/gnss/aidl/default/Android.bp
@@ -26,12 +26,7 @@
 cc_binary {
     name: "android.hardware.gnss-service.example",
     relative_install_path: "hw",
-    init_rc: [
-        "gnss-default.rc",
-    ],
-    vintf_fragments: [
-        "gnss-default.xml",
-    ],
+    installable: false, // install APEX instead
     vendor: true,
     cflags: [
         "-Wall",
@@ -73,3 +68,35 @@
         "android.hardware.gnss@common-default-lib",
     ],
 }
+
+prebuilt_etc {
+    name: "gnss-default.rc",
+    src: "gnss-default.rc",
+    installable: false,
+}
+
+prebuilt_etc {
+    name: "gnss-default.xml",
+    src: "gnss-default.xml",
+    sub_dir: "vintf",
+    installable: false,
+}
+
+apex {
+    name: "com.android.hardware.gnss",
+    manifest: "apex_manifest.json",
+    file_contexts: "apex_file_contexts",
+    key: "com.android.hardware.key",
+    certificate: ":com.android.hardware.certificate",
+    updatable: false,
+    vendor: true,
+
+    binaries: [
+        "android.hardware.gnss-service.example",
+    ],
+    prebuilts: [
+        "gnss-default.rc",
+        "gnss-default.xml",
+        "android.hardware.location.gps.prebuilt.xml", // permission
+    ],
+}
diff --git a/gnss/aidl/default/Gnss.cpp b/gnss/aidl/default/Gnss.cpp
index f1b9cbf..c31f991 100644
--- a/gnss/aidl/default/Gnss.cpp
+++ b/gnss/aidl/default/Gnss.cpp
@@ -115,7 +115,9 @@
     mGnssMeasurementInterface->setLocationEnabled(true);
     this->reportGnssStatusValue(IGnssCallback::GnssStatusValue::SESSION_BEGIN);
     mThread = std::thread([this]() {
-        this->reportSvStatus();
+        if (!mGnssMeasurementEnabled || mMinIntervalMs <= mGnssMeasurementIntervalMs) {
+            this->reportSvStatus();
+        }
         if (!mFirstFixReceived) {
             std::this_thread::sleep_for(std::chrono::milliseconds(TTFF_MILLIS));
             mFirstFixReceived = true;
@@ -124,7 +126,9 @@
             if (!mIsActive) {
                 break;
             }
-            this->reportSvStatus();
+            if (!mGnssMeasurementEnabled || mMinIntervalMs <= mGnssMeasurementIntervalMs) {
+                this->reportSvStatus();
+            }
             this->reportNmea();
 
             auto currentLocation = getLocationFromHW();
@@ -386,4 +390,12 @@
     return ndk::ScopedAStatus::ok();
 }
 
+void Gnss::setGnssMeasurementEnabled(const bool enabled) {
+    mGnssMeasurementEnabled = enabled;
+}
+
+void Gnss::setGnssMeasurementInterval(const long intervalMs) {
+    mGnssMeasurementIntervalMs = intervalMs;
+}
+
 }  // namespace aidl::android::hardware::gnss
diff --git a/gnss/aidl/default/Gnss.h b/gnss/aidl/default/Gnss.h
index 00540cd..245d607 100644
--- a/gnss/aidl/default/Gnss.h
+++ b/gnss/aidl/default/Gnss.h
@@ -85,6 +85,8 @@
             override;
 
     void reportSvStatus() const;
+    void setGnssMeasurementEnabled(const bool enabled);
+    void setGnssMeasurementInterval(const long intervalMs);
     std::shared_ptr<GnssConfiguration> mGnssConfiguration;
     std::shared_ptr<GnssPowerIndication> mGnssPowerIndication;
     std::shared_ptr<GnssMeasurementInterface> mGnssMeasurementInterface;
@@ -101,10 +103,12 @@
     static std::shared_ptr<IGnssCallback> sGnssCallback;
 
     std::atomic<long> mMinIntervalMs;
+    std::atomic<long> mGnssMeasurementIntervalMs;
     std::atomic<bool> mIsActive;
     std::atomic<bool> mIsSvStatusActive;
     std::atomic<bool> mIsNmeaActive;
     std::atomic<bool> mFirstFixReceived;
+    std::atomic<bool> mGnssMeasurementEnabled;
     std::thread mThread;
     ::android::hardware::gnss::common::ThreadBlocker mThreadBlocker;
 
diff --git a/gnss/aidl/default/GnssMeasurementInterface.cpp b/gnss/aidl/default/GnssMeasurementInterface.cpp
index aab9e03..f324213 100644
--- a/gnss/aidl/default/GnssMeasurementInterface.cpp
+++ b/gnss/aidl/default/GnssMeasurementInterface.cpp
@@ -35,7 +35,9 @@
 std::shared_ptr<IGnssMeasurementCallback> GnssMeasurementInterface::sCallback = nullptr;
 
 GnssMeasurementInterface::GnssMeasurementInterface()
-    : mIntervalMs(1000), mLocationIntervalMs(1000), mFutures(std::vector<std::future<void>>()) {}
+    : mIntervalMs(1000), mLocationIntervalMs(1000) {
+    mThreads.reserve(2);
+}
 
 GnssMeasurementInterface::~GnssMeasurementInterface() {
     waitForStoppingThreads();
@@ -74,6 +76,7 @@
         stop();
     }
     mIntervalMs = std::max(options.intervalMs, 1000);
+    mGnss->setGnssMeasurementInterval(mIntervalMs);
     start(options.enableCorrVecOutputs, options.enableFullTracking);
 
     return ndk::ScopedAStatus::ok();
@@ -100,12 +103,13 @@
         ALOGD("restarting since measurement has started");
         stop();
     }
-    // Wait for stopping previous thread.
-    waitForStoppingThreads();
 
     mIsActive = true;
-    mThreadBlocker.reset();
-    mThread = std::thread([this, enableCorrVecOutputs, enableFullTracking]() {
+    mGnss->setGnssMeasurementEnabled(true);
+    mThreads.emplace_back(std::thread([this, enableCorrVecOutputs, enableFullTracking]() {
+        waitForStoppingThreads();
+        mThreadBlocker.reset();
+
         int intervalMs;
         do {
             if (!mIsActive) {
@@ -127,22 +131,30 @@
                 auto measurement =
                         Utils::getMockMeasurement(enableCorrVecOutputs, enableFullTracking);
                 this->reportMeasurement(measurement);
-                if (!mLocationEnabled) {
+                if (!mLocationEnabled || mLocationIntervalMs > mIntervalMs) {
                     mGnss->reportSvStatus();
                 }
             }
             intervalMs =
                     (mLocationEnabled) ? std::min(mLocationIntervalMs, mIntervalMs) : mIntervalMs;
         } while (mIsActive && mThreadBlocker.wait_for(std::chrono::milliseconds(intervalMs)));
-    });
+    }));
 }
 
 void GnssMeasurementInterface::stop() {
     ALOGD("stop");
     mIsActive = false;
+    mGnss->setGnssMeasurementEnabled(false);
     mThreadBlocker.notify();
-    if (mThread.joinable()) {
-        mFutures.push_back(std::async(std::launch::async, [this] { mThread.join(); }));
+    for (auto iter = mThreads.begin(); iter != mThreads.end(); ++iter) {
+        if (iter->joinable()) {
+            mFutures.push_back(std::async(std::launch::async, [this, iter] {
+                iter->join();
+                mThreads.erase(iter);
+            }));
+        } else {
+            mThreads.erase(iter);
+        }
     }
 }
 
diff --git a/gnss/aidl/default/GnssMeasurementInterface.h b/gnss/aidl/default/GnssMeasurementInterface.h
index 926a4e7..ea07c9a 100644
--- a/gnss/aidl/default/GnssMeasurementInterface.h
+++ b/gnss/aidl/default/GnssMeasurementInterface.h
@@ -52,7 +52,7 @@
     std::atomic<long> mLocationIntervalMs;
     std::atomic<bool> mIsActive;
     std::atomic<bool> mLocationEnabled;
-    std::thread mThread;
+    std::vector<std::thread> mThreads;
     std::vector<std::future<void>> mFutures;
     ::android::hardware::gnss::common::ThreadBlocker mThreadBlocker;
 
diff --git a/gnss/aidl/default/GnssNavigationMessageInterface.cpp b/gnss/aidl/default/GnssNavigationMessageInterface.cpp
index 75b9624..c262dc6 100644
--- a/gnss/aidl/default/GnssNavigationMessageInterface.cpp
+++ b/gnss/aidl/default/GnssNavigationMessageInterface.cpp
@@ -29,7 +29,9 @@
 
 std::shared_ptr<IGnssNavigationMessageCallback> GnssNavigationMessageInterface::sCallback = nullptr;
 
-GnssNavigationMessageInterface::GnssNavigationMessageInterface() : mMinIntervalMillis(1000) {}
+GnssNavigationMessageInterface::GnssNavigationMessageInterface() : mMinIntervalMillis(1000) {
+    mThreads.reserve(2);
+}
 
 GnssNavigationMessageInterface::~GnssNavigationMessageInterface() {
     waitForStoppingThreads();
@@ -61,11 +63,11 @@
         ALOGD("restarting since nav msg has started");
         stop();
     }
-    // Wait for stopping previous thread.
-    waitForStoppingThreads();
 
     mIsActive = true;
-    mThread = std::thread([this]() {
+    mThreads.emplace_back(std::thread([this]() {
+        waitForStoppingThreads();
+        mThreadBlocker.reset();
         do {
             if (!mIsActive) {
                 break;
@@ -81,15 +83,22 @@
             this->reportMessage(message);
         } while (mIsActive &&
                  mThreadBlocker.wait_for(std::chrono::milliseconds(mMinIntervalMillis)));
-    });
+    }));
 }
 
 void GnssNavigationMessageInterface::stop() {
     ALOGD("stop");
     mIsActive = false;
     mThreadBlocker.notify();
-    if (mThread.joinable()) {
-        mFutures.push_back(std::async(std::launch::async, [this] { mThread.join(); }));
+    for (auto iter = mThreads.begin(); iter != mThreads.end(); ++iter) {
+        if (iter->joinable()) {
+            mFutures.push_back(std::async(std::launch::async, [this, iter] {
+                iter->join();
+                mThreads.erase(iter);
+            }));
+        } else {
+            mThreads.erase(iter);
+        }
     }
 }
 
diff --git a/gnss/aidl/default/GnssNavigationMessageInterface.h b/gnss/aidl/default/GnssNavigationMessageInterface.h
index b335348..e9a7536 100644
--- a/gnss/aidl/default/GnssNavigationMessageInterface.h
+++ b/gnss/aidl/default/GnssNavigationMessageInterface.h
@@ -40,7 +40,7 @@
 
     std::atomic<long> mMinIntervalMillis;
     std::atomic<bool> mIsActive;
-    std::thread mThread;
+    std::vector<std::thread> mThreads;
     std::vector<std::future<void>> mFutures;
     ::android::hardware::gnss::common::ThreadBlocker mThreadBlocker;
 
diff --git a/gnss/aidl/default/apex_file_contexts b/gnss/aidl/default/apex_file_contexts
new file mode 100644
index 0000000..83b01ed
--- /dev/null
+++ b/gnss/aidl/default/apex_file_contexts
@@ -0,0 +1,3 @@
+(/.*)?                                                          u:object_r:vendor_file:s0
+/etc(/.*)?                                                      u:object_r:vendor_configs_file:s0
+/bin/hw/android\.hardware\.gnss-service\.example                u:object_r:hal_gnss_default_exec:s0
diff --git a/gnss/aidl/default/apex_manifest.json b/gnss/aidl/default/apex_manifest.json
new file mode 100644
index 0000000..9b2db23
--- /dev/null
+++ b/gnss/aidl/default/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+    "name": "com.android.hardware.gnss",
+    "version": 1
+}
diff --git a/gnss/aidl/default/gnss-default.rc b/gnss/aidl/default/gnss-default.rc
index fe179c3..d47b3a5 100644
--- a/gnss/aidl/default/gnss-default.rc
+++ b/gnss/aidl/default/gnss-default.rc
@@ -1,4 +1,4 @@
-service vendor.gnss-default /vendor/bin/hw/android.hardware.gnss-service.example
+service vendor.gnss-default /apex/com.android.hardware.gnss/bin/hw/android.hardware.gnss-service.example
     class hal
     user nobody
     group nobody
diff --git a/gnss/aidl/vts/gnss_hal_test_cases.cpp b/gnss/aidl/vts/gnss_hal_test_cases.cpp
index aa8bdfd..9381a0a 100644
--- a/gnss/aidl/vts/gnss_hal_test_cases.cpp
+++ b/gnss/aidl/vts/gnss_hal_test_cases.cpp
@@ -1430,13 +1430,13 @@
         startMeasurementWithInterval(intervals[i], iGnssMeasurement, callback);
 
         std::vector<int> measurementDeltas;
-        std::vector<int> svInfoListTimestampsDeltas;
+        std::vector<int> svInfoListDeltas;
 
         collectMeasurementIntervals(callback, numEvents[i], /* timeoutSeconds= */ 10,
                                     measurementDeltas);
         if (aidl_gnss_hal_->getInterfaceVersion() >= 3) {
-            collectSvInfoListTimestamps(numEvents[i], /* timeoutSeconds= */ 10,
-                                        svInfoListTimestampsDeltas);
+            collectSvInfoListTimestamps(numEvents[i], /* timeoutSeconds= */ 10, svInfoListDeltas);
+            EXPECT_TRUE(aidl_gnss_cb_->sv_info_list_cbq_.size() > 0);
         }
         status = iGnssMeasurement->close();
         ASSERT_TRUE(status.isOk());
@@ -1444,8 +1444,7 @@
         assertMeanAndStdev(intervals[i], measurementDeltas);
 
         if (aidl_gnss_hal_->getInterfaceVersion() >= 3) {
-            assertMeanAndStdev(intervals[i], svInfoListTimestampsDeltas);
-            EXPECT_TRUE(aidl_gnss_cb_->sv_info_list_cbq_.size() > 0);
+            assertMeanAndStdev(intervals[i], svInfoListDeltas);
         }
     }
 }
@@ -1477,13 +1476,25 @@
         auto callback = sp<GnssMeasurementCallbackAidl>::make();
         startMeasurementWithInterval(intervalMs, iGnssMeasurement, callback);
 
-        std::vector<int> deltas;
-        collectMeasurementIntervals(callback, /*numEvents=*/10, /*timeoutSeconds=*/10, deltas);
+        std::vector<int> measurementDeltas;
+        std::vector<int> svInfoListDeltas;
+
+        collectMeasurementIntervals(callback, /*numEvents=*/10, /*timeoutSeconds=*/10,
+                                    measurementDeltas);
+        if (aidl_gnss_hal_->getInterfaceVersion() >= 3) {
+            collectSvInfoListTimestamps(/*numEvents=*/10, /* timeoutSeconds= */ 10,
+                                        svInfoListDeltas);
+            EXPECT_TRUE(aidl_gnss_cb_->sv_info_list_cbq_.size() > 0);
+        }
 
         status = iGnssMeasurement->close();
         ASSERT_TRUE(status.isOk());
 
-        assertMeanAndStdev(locationIntervalMs, deltas);
+        assertMeanAndStdev(locationIntervalMs, measurementDeltas);
+        if (aidl_gnss_hal_->getInterfaceVersion() >= 3) {
+            // Verify the SvStatus interval is 1s (not 2s)
+            assertMeanAndStdev(locationIntervalMs, svInfoListDeltas);
+        }
     }
     StopAndClearLocations();
 }
@@ -1516,16 +1527,37 @@
 
         // Start location and verify the measurements are received at 1Hz
         StartAndCheckFirstLocation(locationIntervalMs, /* lowPowerMode= */ false);
-        std::vector<int> deltas;
-        collectMeasurementIntervals(callback, /*numEvents=*/10, kFirstMeasTimeoutSec, deltas);
-        assertMeanAndStdev(locationIntervalMs, deltas);
+        std::vector<int> measurementDeltas;
+        std::vector<int> svInfoListDeltas;
+        collectMeasurementIntervals(callback, /*numEvents=*/10, kFirstMeasTimeoutSec,
+                                    measurementDeltas);
+        assertMeanAndStdev(locationIntervalMs, measurementDeltas);
+        if (aidl_gnss_hal_->getInterfaceVersion() >= 3) {
+            collectSvInfoListTimestamps(/*numEvents=*/10, /* timeoutSeconds= */ 10,
+                                        svInfoListDeltas);
+            EXPECT_TRUE(aidl_gnss_cb_->sv_info_list_cbq_.size() > 0);
+            // Verify the SvStatus intervals are at 1s interval
+            assertMeanAndStdev(locationIntervalMs, svInfoListDeltas);
+        }
 
         // Stop location request and verify the measurements are received at 2s intervals
         StopAndClearLocations();
-        callback->gnss_data_cbq_.reset();
-        deltas.clear();
-        collectMeasurementIntervals(callback, /*numEvents=*/5, kFirstMeasTimeoutSec, deltas);
-        assertMeanAndStdev(intervalMs, deltas);
+        measurementDeltas.clear();
+        collectMeasurementIntervals(callback, /*numEvents=*/5, kFirstMeasTimeoutSec,
+                                    measurementDeltas);
+        assertMeanAndStdev(intervalMs, measurementDeltas);
+
+        if (aidl_gnss_hal_->getInterfaceVersion() >= 3) {
+            svInfoListDeltas.clear();
+            collectSvInfoListTimestamps(/*numEvents=*/5, /* timeoutSeconds= */ 10,
+                                        svInfoListDeltas);
+            EXPECT_TRUE(aidl_gnss_cb_->sv_info_list_cbq_.size() > 0);
+            // Verify the SvStatus intervals are at 2s interval
+            for (const int& delta : svInfoListDeltas) {
+                ALOGD("svInfoListDelta: %d", delta);
+            }
+            assertMeanAndStdev(intervalMs, svInfoListDeltas);
+        }
 
         status = iGnssMeasurement->close();
         ASSERT_TRUE(status.isOk());
@@ -1587,8 +1619,7 @@
  * TestGnssMeasurementIsFullTracking
  * 1. Start measurement with enableFullTracking=true. Verify the received measurements have
  *    isFullTracking=true.
- * 2. Start measurement with enableFullTracking = false. Verify the received measurements have
- *    isFullTracking=false.
+ * 2. Start measurement with enableFullTracking = false.
  * 3. Do step 1 again.
  */
 TEST_P(GnssHalTest, TestGnssMeasurementIsFullTracking) {
@@ -1675,4 +1706,59 @@
     ASSERT_TRUE(accumulatedDeltaRangeFound);
     status = iGnssMeasurement->close();
     ASSERT_TRUE(status.isOk());
-}
\ No newline at end of file
+}
+
+/*
+ * TestSvStatusIntervals:
+ * 1. start measurement and location with various intervals
+ * 2. verify the SvStatus are received at expected interval
+ */
+TEST_P(GnssHalTest, TestSvStatusIntervals) {
+    if (aidl_gnss_hal_->getInterfaceVersion() <= 2) {
+        return;
+    }
+    ALOGD("TestSvStatusIntervals");
+    sp<IGnssMeasurementInterface> iGnssMeasurement;
+    auto status = aidl_gnss_hal_->getExtensionGnssMeasurement(&iGnssMeasurement);
+    ASSERT_TRUE(status.isOk());
+    ASSERT_TRUE(iGnssMeasurement != nullptr);
+
+    std::vector<int> locationIntervals{1000, 2000, INT_MAX};
+    std::vector<int> measurementIntervals{1000, 2000, INT_MAX};
+
+    for (auto& locationIntervalMs : locationIntervals) {
+        for (auto& measurementIntervalMs : measurementIntervals) {
+            if (locationIntervalMs == INT_MAX && measurementIntervalMs == INT_MAX) {
+                continue;
+            }
+            auto measurementCallback = sp<GnssMeasurementCallbackAidl>::make();
+            // Start measurement
+            if (measurementIntervalMs < INT_MAX) {
+                startMeasurementWithInterval(measurementIntervalMs, iGnssMeasurement,
+                                             measurementCallback);
+            }
+            // Start location
+            if (locationIntervalMs < INT_MAX) {
+                StartAndCheckFirstLocation(locationIntervalMs, /* lowPowerMode= */ false);
+            }
+            ALOGD("location@%d(ms), measurement@%d(ms)", locationIntervalMs, measurementIntervalMs);
+            std::vector<int> svInfoListDeltas;
+            collectSvInfoListTimestamps(/*numEvents=*/5, /* timeoutSeconds= */ 10,
+                                        svInfoListDeltas);
+            EXPECT_TRUE(aidl_gnss_cb_->sv_info_list_cbq_.size() > 0);
+
+            int svStatusInterval = std::min(locationIntervalMs, measurementIntervalMs);
+            assertMeanAndStdev(svStatusInterval, svInfoListDeltas);
+
+            if (locationIntervalMs < INT_MAX) {
+                // Stop location request
+                StopAndClearLocations();
+            }
+            if (measurementIntervalMs < INT_MAX) {
+                // Stop measurement request
+                status = iGnssMeasurement->close();
+                ASSERT_TRUE(status.isOk());
+            }
+        }
+    }
+}
diff --git a/graphics/Android.bp b/graphics/Android.bp
index 8a69f09..cae5292 100644
--- a/graphics/Android.bp
+++ b/graphics/Android.bp
@@ -39,14 +39,14 @@
 cc_defaults {
     name: "android.hardware.graphics.common-ndk_static",
     static_libs: [
-        "android.hardware.graphics.common-V4-ndk",
+        "android.hardware.graphics.common-V5-ndk",
     ],
 }
 
 cc_defaults {
     name: "android.hardware.graphics.common-ndk_shared",
     shared_libs: [
-        "android.hardware.graphics.common-V4-ndk",
+        "android.hardware.graphics.common-V5-ndk",
     ],
 }
 
diff --git a/graphics/allocator/aidl/Android.bp b/graphics/allocator/aidl/Android.bp
index a3a2c55..03628b0 100644
--- a/graphics/allocator/aidl/Android.bp
+++ b/graphics/allocator/aidl/Android.bp
@@ -18,7 +18,7 @@
     srcs: ["android/hardware/graphics/allocator/*.aidl"],
     imports: [
         "android.hardware.common-V2",
-        "android.hardware.graphics.common-V4",
+        "android.hardware.graphics.common-V5",
     ],
     stability: "vintf",
     backend: {
@@ -49,7 +49,7 @@
             version: "2",
             imports: [
                 "android.hardware.common-V2",
-                "android.hardware.graphics.common-V4",
+                "android.hardware.graphics.common-V5",
             ],
         },
 
diff --git a/graphics/common/aidl/Android.bp b/graphics/common/aidl/Android.bp
index 142c7c1..f177a41 100644
--- a/graphics/common/aidl/Android.bp
+++ b/graphics/common/aidl/Android.bp
@@ -15,7 +15,7 @@
         enabled: true,
         support_system_process: true,
     },
-    vndk_use_version: "4",
+    vndk_use_version: "5",
     srcs: [
         "android/hardware/graphics/common/*.aidl",
     ],
diff --git a/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/Dataspace.aidl b/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/Dataspace.aidl
index d9ff5aa..6ed5bb2 100644
--- a/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/Dataspace.aidl
+++ b/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/Dataspace.aidl
@@ -81,9 +81,11 @@
   DISPLAY_P3_LINEAR = (((10 << 16) | (1 << 22)) | (1 << 27)) /* 139067392 */,
   DISPLAY_P3 = (((10 << 16) | (2 << 22)) | (1 << 27)) /* 143261696 */,
   ADOBE_RGB = (((11 << 16) | (4 << 22)) | (1 << 27)) /* 151715840 */,
+  ADOBE_RGB_LINEAR = (((11 << 16) | (1 << 22)) | (1 << 27)) /* 139132928 */,
   BT2020_LINEAR = (((6 << 16) | (1 << 22)) | (1 << 27)) /* 138805248 */,
   BT2020 = (((6 << 16) | (3 << 22)) | (1 << 27)) /* 147193856 */,
   BT2020_PQ = (((6 << 16) | (7 << 22)) | (1 << 27)) /* 163971072 */,
+  BT2020_LINEAR_EXTENDED = (((6 << 16) | (1 << 22)) | (3 << 27)) /* 407240704 */,
   DEPTH = 0x1000,
   SENSOR = 0x1001,
   BT2020_ITU = (((6 << 16) | (3 << 22)) | (2 << 27)) /* 281411584 */,
diff --git a/graphics/common/aidl/android/hardware/graphics/common/Dataspace.aidl b/graphics/common/aidl/android/hardware/graphics/common/Dataspace.aidl
index 4b6613e..79737eb 100644
--- a/graphics/common/aidl/android/hardware/graphics/common/Dataspace.aidl
+++ b/graphics/common/aidl/android/hardware/graphics/common/Dataspace.aidl
@@ -559,6 +559,13 @@
     ADOBE_RGB = 11 << 16 | 4 << 22 | 1 << 27, // STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2 | RANGE_FULL
 
     /**
+     * Adobe RGB LINEAR
+     *
+     * Use full range, linear transfer and Adobe RGB primaries
+     */
+    ADOBE_RGB_LINEAR = 11 << 16 | 1 << 22 | 1 << 27, // STANDARD_ADOBE_RGB | TRANSFER_LINEAR | RANGE_FULL
+
+    /**
      * ITU-R Recommendation 2020 (BT.2020)
      *
      * Ultra High-definition television
@@ -586,6 +593,15 @@
     BT2020_PQ = 6 << 16 | 7 << 22 | 1 << 27, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL
 
     /**
+     * ITU-R Recommendation 2020 (BT.2020)
+     *
+     * Ultra High-definition television
+     *
+     * Use extended range, linear transfer and BT2020 standard
+     */
+    BT2020_LINEAR_EXTENDED = 6 << 16 | 1 << 22 | 3 << 27, // STANDARD_BT2020 | TRANSFER_LINEAR | RANGE_EXTENDED
+
+    /**
      * Data spaces for non-color formats
      */
 
diff --git a/graphics/composer/aidl/Android.bp b/graphics/composer/aidl/Android.bp
index c105ad4..5699895 100644
--- a/graphics/composer/aidl/Android.bp
+++ b/graphics/composer/aidl/Android.bp
@@ -38,7 +38,7 @@
     ],
     stability: "vintf",
     imports: [
-        "android.hardware.graphics.common-V4",
+        "android.hardware.graphics.common-V5",
         "android.hardware.common-V2",
     ],
     backend: {
@@ -62,14 +62,14 @@
         {
             version: "1",
             imports: [
-                "android.hardware.graphics.common-V4",
+                "android.hardware.graphics.common-V5",
                 "android.hardware.common-V2",
             ],
         },
         {
             version: "2",
             imports: [
-                "android.hardware.graphics.common-V4",
+                "android.hardware.graphics.common-V5",
                 "android.hardware.common-V2",
             ],
         },
diff --git a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayCapability.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayCapability.aidl
index 6eba887..0e2d72b 100644
--- a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayCapability.aidl
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayCapability.aidl
@@ -42,4 +42,5 @@
   AUTO_LOW_LATENCY_MODE = 5,
   SUSPEND = 6,
   DISPLAY_IDLE_TIMER = 7,
+  MULTI_THREADED_PRESENT = 8,
 }
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCapability.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCapability.aidl
index f4b2984..7154d74 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCapability.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCapability.aidl
@@ -80,4 +80,20 @@
      * IComposerCallback.onVsyncIdle.
      */
     DISPLAY_IDLE_TIMER = 7,
+    /**
+     * Indicates that both the composer HAL implementation and the given display
+     * support calling executeCommands concurrently from separate threads.
+     * executeCommands for a particular display will never run concurrently to
+     * any other executeCommands for the same display. In addition, the
+     * CommandResultPayload must only reference displays included in the
+     * DisplayCommands passed to executeCommands. Displays referenced from
+     * separate threads must have minimal interference with one another. If a
+     * HWC-managed display has this capability, SurfaceFlinger can run
+     * executeCommands for this display concurrently with other displays with the
+     * same capability.
+     * @see IComposerClient.executeCommands
+     * @see DisplayCommand.presentDisplay
+     * @see DisplayCommand.validateDisplay
+     */
+    MULTI_THREADED_PRESENT = 8,
 }
diff --git a/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientWriter.h b/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientWriter.h
index 2e902e5..92ed6d3 100644
--- a/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientWriter.h
+++ b/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientWriter.h
@@ -98,17 +98,21 @@
     }
 
     void validateDisplay(int64_t display,
-                         std::optional<ClockMonotonicTimestamp> expectedPresentTime) {
+                         std::optional<ClockMonotonicTimestamp> expectedPresentTime,
+                         int32_t frameIntervalNs) {
         auto& command = getDisplayCommand(display);
         command.expectedPresentTime = expectedPresentTime;
         command.validateDisplay = true;
+        command.frameIntervalNs = frameIntervalNs;
     }
 
     void presentOrvalidateDisplay(int64_t display,
-                                  std::optional<ClockMonotonicTimestamp> expectedPresentTime) {
+                                  std::optional<ClockMonotonicTimestamp> expectedPresentTime,
+                                  int32_t frameIntervalNs) {
         auto& command = getDisplayCommand(display);
         command.expectedPresentTime = expectedPresentTime;
         command.presentOrValidateDisplay = true;
+        command.frameIntervalNs = frameIntervalNs;
     }
 
     void acceptDisplayChanges(int64_t display) {
diff --git a/graphics/composer/aidl/vts/Android.bp b/graphics/composer/aidl/vts/Android.bp
index e60e1a7..d71999d 100644
--- a/graphics/composer/aidl/vts/Android.bp
+++ b/graphics/composer/aidl/vts/Android.bp
@@ -54,7 +54,6 @@
         "libgui",
         "libhidlbase",
         "libprocessgroup",
-        "libtinyxml2",
         "libvndksupport",
     ],
     header_libs: [
diff --git a/graphics/composer/aidl/vts/ReadbackVts.cpp b/graphics/composer/aidl/vts/ReadbackVts.cpp
index abb58e2..8605628 100644
--- a/graphics/composer/aidl/vts/ReadbackVts.cpp
+++ b/graphics/composer/aidl/vts/ReadbackVts.cpp
@@ -35,6 +35,7 @@
     writer.setLayerPlaneAlpha(mDisplay, mLayer, mAlpha);
     writer.setLayerBlendMode(mDisplay, mLayer, mBlendMode);
     writer.setLayerBrightness(mDisplay, mLayer, mBrightness);
+    writer.setLayerDataspace(mDisplay, mLayer, mDataspace);
 }
 
 std::string ReadbackHelper::getColorModeString(ColorMode mode) {
@@ -99,6 +100,7 @@
 
     layerSettings.geometry.positionTransform = scale * translation;
     layerSettings.whitePointNits = mWhitePointNits;
+    layerSettings.sourceDataspace = static_cast<::android::ui::Dataspace>(mDataspace);
 
     return layerSettings;
 }
@@ -189,6 +191,23 @@
     }
 }
 
+void ReadbackHelper::compareColorBuffers(void* expectedBuffer, void* actualBuffer,
+                                         const uint32_t stride, const uint32_t width,
+                                         const uint32_t height, common::PixelFormat pixelFormat) {
+    const int32_t bytesPerPixel = ReadbackHelper::GetBytesPerPixel(pixelFormat);
+    ASSERT_NE(-1, bytesPerPixel);
+    for (int row = 0; row < height; row++) {
+        for (int col = 0; col < width; col++) {
+            int offset = (row * static_cast<int32_t>(stride) + col) * bytesPerPixel;
+            uint8_t* expectedColor = (uint8_t*)expectedBuffer + offset;
+            uint8_t* actualColor = (uint8_t*)actualBuffer + offset;
+            ASSERT_EQ(expectedColor[0], actualColor[0]);
+            ASSERT_EQ(expectedColor[1], actualColor[1]);
+            ASSERT_EQ(expectedColor[2], actualColor[2]);
+        }
+    }
+}
+
 ReadbackBuffer::ReadbackBuffer(int64_t display, const std::shared_ptr<VtsComposerClient>& client,
                                int32_t width, int32_t height, common::PixelFormat pixelFormat,
                                common::Dataspace dataspace)
@@ -248,6 +267,15 @@
     EXPECT_EQ(::android::OK, status);
 }
 
+::android::sp<::android::GraphicBuffer> ReadbackBuffer::getBuffer() {
+    const auto& [fenceStatus, bufferFence] = mComposerClient->getReadbackBufferFence(mDisplay);
+    EXPECT_TRUE(fenceStatus.isOk());
+    if (bufferFence.get() != -1) {
+        sync_wait(bufferFence.get(), -1);
+    }
+    return mGraphicBuffer;
+}
+
 void TestColorLayer::write(ComposerClientWriter& writer) {
     TestLayer::write(writer);
     writer.setLayerCompositionType(mDisplay, mLayer, Composition::SOLID_COLOR);
@@ -344,10 +372,6 @@
             "TestBufferLayer");
 }
 
-void TestBufferLayer::setDataspace(common::Dataspace dataspace, ComposerClientWriter& writer) {
-    writer.setLayerDataspace(mDisplay, mLayer, dataspace);
-}
-
 void TestBufferLayer::setToClientComposition(ComposerClientWriter& writer) {
     writer.setLayerCompositionType(mDisplay, mLayer, Composition::CLIENT);
 }
diff --git a/graphics/composer/aidl/vts/ReadbackVts.h b/graphics/composer/aidl/vts/ReadbackVts.h
index d5602c1..ee20573 100644
--- a/graphics/composer/aidl/vts/ReadbackVts.h
+++ b/graphics/composer/aidl/vts/ReadbackVts.h
@@ -42,6 +42,9 @@
 static const Color GREEN = {0.0f, 1.0f, 0.0f, 1.0f};
 static const Color BLUE = {0.0f, 0.0f, 1.0f, 1.0f};
 static const Color WHITE = {1.0f, 1.0f, 1.0f, 1.0f};
+static const Color LIGHT_RED = {0.5f, 0.0f, 0.0f, 1.0f};
+static const Color LIGHT_GREEN = {0.0f, 0.5f, 0.0f, 1.0f};
+static const Color LIGHT_BLUE = {0.0f, 0.0f, 0.5f, 1.0f};
 
 class TestRenderEngine;
 
@@ -71,6 +74,8 @@
         mSurfaceDamage = std::move(surfaceDamage);
     }
 
+    void setDataspace(Dataspace dataspace) { mDataspace = dataspace; }
+
     void setTransform(Transform transform) { mTransform = transform; }
     void setAlpha(float alpha) { mAlpha = alpha; }
     void setBlendMode(BlendMode blendMode) { mBlendMode = blendMode; }
@@ -98,6 +103,7 @@
     float mAlpha = 1.0;
     BlendMode mBlendMode = BlendMode::NONE;
     uint32_t mZOrder = 0;
+    Dataspace mDataspace = Dataspace::UNKNOWN;
 };
 
 class TestColorLayer : public TestLayer {
@@ -130,8 +136,6 @@
 
     void setBuffer(std::vector<Color> colors);
 
-    void setDataspace(Dataspace dataspace, ComposerClientWriter& writer);
-
     void setToClientComposition(ComposerClientWriter& writer);
 
     uint32_t getWidth() const { return mWidth; }
@@ -185,6 +189,9 @@
     static void compareColorBuffers(const std::vector<Color>& expectedColors, void* bufferData,
                                     const uint32_t stride, const uint32_t width,
                                     const uint32_t height, PixelFormat pixelFormat);
+    static void compareColorBuffers(void* expectedBuffer, void* actualBuffer, const uint32_t stride,
+                                    const uint32_t width, const uint32_t height,
+                                    PixelFormat pixelFormat);
 };
 
 class ReadbackBuffer {
@@ -196,6 +203,8 @@
 
     void checkReadbackBuffer(const std::vector<Color>& expectedColors);
 
+    ::android::sp<::android::GraphicBuffer> getBuffer();
+
   protected:
     uint32_t mWidth;
     uint32_t mHeight;
diff --git a/graphics/composer/aidl/vts/RenderEngineVts.cpp b/graphics/composer/aidl/vts/RenderEngineVts.cpp
index d1f89d0..4e7f773 100644
--- a/graphics/composer/aidl/vts/RenderEngineVts.cpp
+++ b/graphics/composer/aidl/vts/RenderEngineVts.cpp
@@ -89,4 +89,32 @@
     ASSERT_EQ(::android::OK, mGraphicBuffer->unlock());
 }
 
+void TestRenderEngine::checkColorBuffer(const ::android::sp<::android::GraphicBuffer>& buffer) {
+    ASSERT_EQ(mGraphicBuffer->getWidth(), buffer->getWidth());
+    ASSERT_EQ(mGraphicBuffer->getHeight(), buffer->getHeight());
+    void* renderedBufferData;
+    int32_t bytesPerPixel = -1;
+    int32_t bytesPerStride = -1;
+    ASSERT_EQ(0, mGraphicBuffer->lock(static_cast<uint32_t>(mGraphicBuffer->getUsage()),
+                                      &renderedBufferData, &bytesPerPixel, &bytesPerStride));
+    const uint32_t renderedStride = (bytesPerPixel > 0 && bytesPerStride > 0)
+                                            ? static_cast<uint32_t>(bytesPerStride / bytesPerPixel)
+                                            : mGraphicBuffer->getStride();
+
+    void* bufferData;
+    ASSERT_EQ(0, buffer->lock(static_cast<uint32_t>(buffer->getUsage()), &bufferData,
+                              &bytesPerPixel, &bytesPerStride));
+    const uint32_t bufferStride = (bytesPerPixel > 0 && bytesPerStride > 0)
+                                          ? static_cast<uint32_t>(bytesPerStride / bytesPerPixel)
+                                          : buffer->getStride();
+
+    ASSERT_EQ(renderedStride, bufferStride);
+
+    ReadbackHelper::compareColorBuffers(renderedBufferData, bufferData, bufferStride,
+                                        mGraphicBuffer->getWidth(), mGraphicBuffer->getHeight(),
+                                        mFormat);
+    ASSERT_EQ(::android::OK, buffer->unlock());
+    ASSERT_EQ(::android::OK, mGraphicBuffer->unlock());
+}
+
 }  // namespace aidl::android::hardware::graphics::composer3::vts
diff --git a/graphics/composer/aidl/vts/RenderEngineVts.h b/graphics/composer/aidl/vts/RenderEngineVts.h
index 612f0ea..bbe508f 100644
--- a/graphics/composer/aidl/vts/RenderEngineVts.h
+++ b/graphics/composer/aidl/vts/RenderEngineVts.h
@@ -45,6 +45,7 @@
     };
     void drawLayers();
     void checkColorBuffer(const std::vector<Color>& expectedColors);
+    void checkColorBuffer(const ::android::sp<::android::GraphicBuffer>& buffer);
 
     ::android::renderengine::RenderEngine& getInternalRenderEngine() { return *mRenderEngine; }
 
diff --git a/graphics/composer/aidl/vts/VtsComposerClient.h b/graphics/composer/aidl/vts/VtsComposerClient.h
index 20dc733..b45c71f 100644
--- a/graphics/composer/aidl/vts/VtsComposerClient.h
+++ b/graphics/composer/aidl/vts/VtsComposerClient.h
@@ -197,6 +197,7 @@
     std::vector<RefreshRateChangedDebugData> takeListOfRefreshRateChangedDebugData();
 
     static constexpr int32_t kMaxFrameIntervalNs = 50000000;  // 20fps
+    static constexpr int32_t kNoFrameIntervalNs = 0;
 
   private:
     void addDisplayConfigs(VtsDisplay*, const std::vector<DisplayConfiguration>&);
diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
index a83b833..58eca6e 100644
--- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
+++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
@@ -31,12 +31,6 @@
 #include "RenderEngineVts.h"
 #include "VtsComposerClient.h"
 
-// tinyxml2 does implicit conversions >:(
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#include <tinyxml2.h>
-#pragma clang diagnostic pop
-
 namespace aidl::android::hardware::graphics::composer3::vts {
 namespace {
 
@@ -76,9 +70,9 @@
                                                             ContextPriority::HIGH)
                                 .build())));
 
-        ::android::renderengine::DisplaySettings clientCompositionDisplay;
-        clientCompositionDisplay.physicalDisplay = Rect(getDisplayWidth(), getDisplayHeight());
-        clientCompositionDisplay.clip = clientCompositionDisplay.physicalDisplay;
+        mClientCompositionDisplaySettings.physicalDisplay =
+                Rect(getDisplayWidth(), getDisplayHeight());
+        mClientCompositionDisplaySettings.clip = mClientCompositionDisplaySettings.physicalDisplay;
 
         mTestRenderEngine->initGraphicBuffer(
                 static_cast<uint32_t>(getDisplayWidth()), static_cast<uint32_t>(getDisplayHeight()),
@@ -87,7 +81,7 @@
                         static_cast<uint64_t>(common::BufferUsage::CPU_READ_OFTEN) |
                         static_cast<uint64_t>(common::BufferUsage::CPU_WRITE_OFTEN) |
                         static_cast<uint64_t>(common::BufferUsage::GPU_RENDER_TARGET)));
-        mTestRenderEngine->setDisplaySettings(clientCompositionDisplay);
+        mTestRenderEngine->setDisplaySettings(mClientCompositionDisplaySettings);
     }
 
     void TearDown() override {
@@ -128,63 +122,6 @@
         return {false, graphicBuffer};
     }
 
-    // Gets the per-display XML config
-    std::unique_ptr<tinyxml2::XMLDocument> getDisplayConfigXml(int64_t display) {
-
-        if (auto document = getDisplayConfigXmlByStableId(getStableDisplayId(display));
-                document != nullptr) {
-            return document;
-        }
-
-        // Fallback to looking up a per-port config if no config exists for the full ID
-        if (auto document = getDisplayConfigXmlByPort(getPort(display)); document != nullptr) {
-            return document;
-        }
-
-        return nullptr;
-    }
-
-    // Gets the max display brightness for this display.
-    // If the display config xml does not exist, then assume that the display is not well-configured
-    // enough to provide a display brightness, so return nullopt.
-    std::optional<float> getMaxDisplayBrightnessNits(int64_t display) {
-        const auto document = getDisplayConfigXml(display);
-        if (!document) {
-            // Assume the device doesn't support display brightness
-            return std::nullopt;
-        }
-
-        const auto root = document->RootElement();
-        if (!root) {
-            // If there's somehow no root element, then this isn't a valid config
-            return std::nullopt;
-        }
-
-        const auto screenBrightnessMap = root->FirstChildElement("screenBrightnessMap");
-        if (!screenBrightnessMap) {
-            // A valid display config must have a screen brightness map
-            return std::nullopt;
-        }
-
-        auto point = screenBrightnessMap->FirstChildElement("point");
-        float maxNits = -1.f;
-        while (point != nullptr) {
-            const auto nits = point->FirstChildElement("nits");
-            if (nits) {
-                maxNits = std::max(maxNits, nits->FloatText(-1.f));
-            }
-            point = point->NextSiblingElement("point");
-        }
-
-        if (maxNits < 0.f) {
-            // If we got here, then there were no point elements containing a nit value, so this
-            // config isn't valid
-            return std::nullopt;
-        }
-
-        return maxNits;
-    }
-
     void writeLayers(const std::vector<std::shared_ptr<TestLayer>>& layers) {
         for (const auto& layer : layers) {
             layer->write(*mWriter);
@@ -226,6 +163,7 @@
     std::unique_ptr<TestRenderEngine> mTestRenderEngine;
     common::PixelFormat mPixelFormat;
     common::Dataspace mDataspace;
+    ::android::renderengine::DisplaySettings mClientCompositionDisplaySettings;
 
     static constexpr uint32_t kClientTargetSlotCount = 64;
 
@@ -242,53 +180,6 @@
             }
         }
     }
-
-    uint8_t getPort(int64_t display) {
-        const auto& [status, identification] =
-                mComposerClient->getDisplayIdentificationData(display);
-        EXPECT_TRUE(status.isOk());
-        return static_cast<uint8_t>(identification.port);
-    }
-
-    uint64_t getStableDisplayId(int64_t display) {
-        const auto& [status, identification] =
-                mComposerClient->getDisplayIdentificationData(display);
-        EXPECT_TRUE(status.isOk());
-
-        if (const auto info = ::android::parseDisplayIdentificationData(
-                    static_cast<uint8_t>(identification.port), identification.data)) {
-            return info->id.value;
-        }
-
-        return ::android::PhysicalDisplayId::fromPort(static_cast<uint8_t>(identification.port))
-                .value;
-    }
-
-    std::unique_ptr<tinyxml2::XMLDocument> loadXml(const std::string& path) {
-        auto document = std::make_unique<tinyxml2::XMLDocument>();
-        const tinyxml2::XMLError error = document->LoadFile(path.c_str());
-        if (error != tinyxml2::XML_SUCCESS) {
-            ALOGD("%s: Failed to load config file: %s", __func__, path.c_str());
-            return nullptr;
-        }
-
-        ALOGD("%s: Successfully loaded config file: %s", __func__, path.c_str());
-        return document;
-    }
-
-    std::unique_ptr<tinyxml2::XMLDocument> getDisplayConfigXmlByPort(uint8_t port) {
-        std::stringstream pathBuilder;
-        pathBuilder << "/vendor/etc/displayconfig/display_port_" << static_cast<uint32_t>(port)
-                    << ".xml";
-        return loadXml(pathBuilder.str());
-    }
-
-    std::unique_ptr<tinyxml2::XMLDocument> getDisplayConfigXmlByStableId(uint64_t stableId) {
-        std::stringstream pathBuilder;
-        pathBuilder << "/vendor/etc/displayconfig/display_id_" << stableId
-                    << ".xml";
-       return loadXml(pathBuilder.str());
-    }
 };
 
 class GraphicsCompositionTest : public GraphicsCompositionTestBase,
@@ -329,7 +220,8 @@
 
         writeLayers(layers);
         ASSERT_TRUE(mReader.takeErrors().empty());
-        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                 VtsComposerClient::kNoFrameIntervalNs);
         execute();
         // if hwc cannot handle and asks for composition change,
         // just succeed the test
@@ -381,14 +273,15 @@
                 getDisplayHeight(), common::PixelFormat::RGBA_8888);
         layer->setDisplayFrame({0, 0, getDisplayWidth(), getDisplayHeight()});
         layer->setZOrder(10);
-        layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
+        layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode));
         ASSERT_NO_FATAL_FAILURE(layer->setBuffer(expectedColors));
 
         std::vector<std::shared_ptr<TestLayer>> layers = {layer};
 
         writeLayers(layers);
         ASSERT_TRUE(mReader.takeErrors().empty());
-        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                 VtsComposerClient::kNoFrameIntervalNs);
         execute();
 
         if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
@@ -447,7 +340,8 @@
                                       getDisplayHeight(), mPixelFormat, mDataspace);
         ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
 
-        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                 VtsComposerClient::kNoFrameIntervalNs);
         execute();
 
         if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
@@ -565,7 +459,7 @@
                                                        getDisplayHeight(), PixelFormat::RGBA_FP16);
         layer->setDisplayFrame({0, 0, getDisplayWidth(), getDisplayHeight()});
         layer->setZOrder(10);
-        layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
+        layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode));
 
         std::vector<std::shared_ptr<TestLayer>> layers = {layer};
 
@@ -574,7 +468,8 @@
         ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
         writeLayers(layers);
         ASSERT_TRUE(mReader.takeErrors().empty());
-        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                 VtsComposerClient::kNoFrameIntervalNs);
         execute();
 
         auto changedCompositionTypes = mReader.takeChangedCompositionTypes(getPrimaryDisplayId());
@@ -607,7 +502,8 @@
             mWriter->setClientTarget(getPrimaryDisplayId(), /*slot*/ 0, buffer, clientFence,
                                      clientDataspace, std::vector<common::Rect>(1, damage));
             layer->setToClientComposition(*mWriter);
-            mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+            mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                     VtsComposerClient::kNoFrameIntervalNs);
             execute();
             changedCompositionTypes = mReader.takeChangedCompositionTypes(getPrimaryDisplayId());
             ASSERT_TRUE(changedCompositionTypes.empty());
@@ -663,7 +559,7 @@
         deviceLayer->setDisplayFrame({0, 0, static_cast<int32_t>(deviceLayer->getWidth()),
                                       static_cast<int32_t>(deviceLayer->getHeight())});
         deviceLayer->setZOrder(10);
-        deviceLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
+        deviceLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode));
         ASSERT_NO_FATAL_FAILURE(deviceLayer->setBuffer(deviceColors));
         deviceLayer->write(*mWriter);
 
@@ -684,7 +580,8 @@
         clientLayer->setDisplayFrame(clientFrame);
         clientLayer->setZOrder(0);
         clientLayer->write(*mWriter);
-        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                 VtsComposerClient::kNoFrameIntervalNs);
         execute();
 
         auto changedCompositionTypes = mReader.takeChangedCompositionTypes(getPrimaryDisplayId());
@@ -713,7 +610,8 @@
         mWriter->setClientTarget(getPrimaryDisplayId(), /*slot*/ 0, buffer, clientFence,
                                  clientDataspace, std::vector<common::Rect>(1, clientFrame));
         clientLayer->setToClientComposition(*mWriter);
-        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                 VtsComposerClient::kNoFrameIntervalNs);
         execute();
         changedCompositionTypes = mReader.takeChangedCompositionTypes(getPrimaryDisplayId());
         ASSERT_TRUE(changedCompositionTypes.empty());
@@ -750,7 +648,7 @@
                                                        getDisplayHeight(), PixelFormat::RGBA_8888);
         layer->setDisplayFrame({0, 0, getDisplayWidth(), getDisplayHeight()});
         layer->setZOrder(10);
-        layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
+        layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode));
         ASSERT_NO_FATAL_FAILURE(layer->setBuffer(expectedColors));
 
         std::vector<std::shared_ptr<TestLayer>> layers = {layer};
@@ -761,7 +659,8 @@
 
         writeLayers(layers);
         ASSERT_TRUE(mReader.takeErrors().empty());
-        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                 VtsComposerClient::kNoFrameIntervalNs);
         execute();
         if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
             GTEST_SUCCEED();
@@ -789,7 +688,8 @@
 
         writeLayers(layers);
         ASSERT_TRUE(mReader.takeErrors().empty());
-        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                 VtsComposerClient::kNoFrameIntervalNs);
         execute();
         ASSERT_TRUE(mReader.takeErrors().empty());
         ASSERT_TRUE(mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty());
@@ -830,7 +730,8 @@
 
         writeLayers(layers);
         ASSERT_TRUE(mReader.takeErrors().empty());
-        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                 VtsComposerClient::kNoFrameIntervalNs);
         execute();
         if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
             GTEST_SUCCEED();
@@ -878,7 +779,7 @@
                                                        getDisplayHeight(), PixelFormat::RGBA_8888);
         layer->setDisplayFrame({0, 0, getDisplayWidth(), getDisplayHeight()});
         layer->setZOrder(10);
-        layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
+        layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode));
         layer->setSourceCrop({0, static_cast<float>(getDisplayHeight() / 2),
                               static_cast<float>(getDisplayWidth()),
                               static_cast<float>(getDisplayHeight())});
@@ -894,7 +795,8 @@
         ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
         writeLayers(layers);
         ASSERT_TRUE(mReader.takeErrors().empty());
-        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                 VtsComposerClient::kNoFrameIntervalNs);
         execute();
         if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
             GTEST_SUCCEED();
@@ -952,7 +854,8 @@
 
         writeLayers(layers);
         ASSERT_TRUE(mReader.takeErrors().empty());
-        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                 VtsComposerClient::kNoFrameIntervalNs);
         execute();
         if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
             GTEST_SUCCEED();
@@ -974,7 +877,8 @@
 
         writeLayers(layers);
         ASSERT_TRUE(mReader.takeErrors().empty());
-        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                 VtsComposerClient::kNoFrameIntervalNs);
         execute();
         ASSERT_TRUE(mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty());
         ASSERT_TRUE(mReader.takeErrors().empty());
@@ -990,32 +894,6 @@
 }
 
 TEST_P(GraphicsCompositionTest, SetLayerBrightnessDims) {
-    const auto& [status, capabilities] =
-            mComposerClient->getDisplayCapabilities(getPrimaryDisplayId());
-    ASSERT_TRUE(status.isOk());
-
-    const bool brightnessSupport = std::find(capabilities.begin(), capabilities.end(),
-                                             DisplayCapability::BRIGHTNESS) != capabilities.end();
-
-    if (!brightnessSupport) {
-        GTEST_SUCCEED() << "Cannot verify dimming behavior without brightness support";
-        return;
-    }
-
-    const std::optional<float> maxBrightnessNitsOptional =
-            getMaxDisplayBrightnessNits(getPrimaryDisplayId());
-
-    ASSERT_TRUE(maxBrightnessNitsOptional.has_value());
-
-    const float maxBrightnessNits = *maxBrightnessNitsOptional;
-
-    // Preconditions to successfully run are knowing the max brightness and successfully applying
-    // the max brightness
-    ASSERT_GT(maxBrightnessNits, 0.f);
-    mWriter->setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 1.f, maxBrightnessNits);
-    execute();
-    ASSERT_TRUE(mReader.takeErrors().empty());
-
     for (ColorMode mode : mTestColorModes) {
         EXPECT_TRUE(mComposerClient
                             ->setColorMode(getPrimaryDisplayId(), mode, RenderIntent::COLORIMETRIC)
@@ -1032,11 +910,14 @@
         const common::Rect redRect = {0, 0, getDisplayWidth(), getDisplayHeight() / 2};
         const common::Rect dimmerRedRect = {0, getDisplayHeight() / 2, getDisplayWidth(),
                                             getDisplayHeight()};
+
+        static constexpr float kMaxBrightnessNits = 300.f;
+
         const auto redLayer =
                 std::make_shared<TestColorLayer>(mComposerClient, getPrimaryDisplayId());
         redLayer->setColor(RED);
         redLayer->setDisplayFrame(redRect);
-        redLayer->setWhitePointNits(maxBrightnessNits);
+        redLayer->setWhitePointNits(kMaxBrightnessNits);
         redLayer->setBrightness(1.f);
 
         const auto dimmerRedLayer =
@@ -1046,7 +927,7 @@
         // Intentionally use a small dimming ratio as some implementations may be more likely to
         // kick into GPU composition to apply dithering when the dimming ratio is high.
         static constexpr float kDimmingRatio = 0.9f;
-        dimmerRedLayer->setWhitePointNits(maxBrightnessNits * kDimmingRatio);
+        dimmerRedLayer->setWhitePointNits(kMaxBrightnessNits * kDimmingRatio);
         dimmerRedLayer->setBrightness(kDimmingRatio);
 
         const std::vector<std::shared_ptr<TestLayer>> layers = {redLayer, dimmerRedLayer};
@@ -1062,7 +943,8 @@
 
         writeLayers(layers);
         ASSERT_TRUE(mReader.takeErrors().empty());
-        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                 VtsComposerClient::kNoFrameIntervalNs);
         execute();
         if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
             GTEST_SUCCEED()
@@ -1120,7 +1002,7 @@
                                                        getDisplayHeight(), PixelFormat::RGBA_8888);
         layer->setDisplayFrame({0, 0, getDisplayWidth(), getDisplayHeight()});
         layer->setZOrder(10);
-        layer->setDataspace(Dataspace::UNKNOWN, *mWriter);
+        layer->setDataspace(Dataspace::UNKNOWN);
         ASSERT_NO_FATAL_FAILURE(layer->setBuffer(topLayerPixelColors));
 
         layer->setBlendMode(blendMode);
@@ -1197,7 +1079,8 @@
         ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
         writeLayers(mLayers);
         ASSERT_TRUE(mReader.takeErrors().empty());
-        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                 VtsComposerClient::kNoFrameIntervalNs);
         execute();
         if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
             GTEST_SUCCEED();
@@ -1242,7 +1125,8 @@
         ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
         writeLayers(mLayers);
         ASSERT_TRUE(mReader.takeErrors().empty());
-        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                 VtsComposerClient::kNoFrameIntervalNs);
         execute();
         if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
             GTEST_SUCCEED();
@@ -1282,7 +1166,8 @@
         ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
         writeLayers(mLayers);
         ASSERT_TRUE(mReader.takeErrors().empty());
-        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                 VtsComposerClient::kNoFrameIntervalNs);
         execute();
         if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
             GTEST_SUCCEED();
@@ -1355,7 +1240,7 @@
                                       getDisplayHeight(), mPixelFormat, mDataspace);
         ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
         mLayer->setTransform(Transform::FLIP_H);
-        mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
+        mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode));
 
         std::vector<Color> expectedColors(
                 static_cast<size_t>(getDisplayWidth() * getDisplayHeight()));
@@ -1366,7 +1251,8 @@
 
         writeLayers(mLayers);
         ASSERT_TRUE(mReader.takeErrors().empty());
-        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                 VtsComposerClient::kNoFrameIntervalNs);
         execute();
         if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
             GTEST_SUCCEED();
@@ -1401,7 +1287,7 @@
         ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
 
         mLayer->setTransform(Transform::FLIP_V);
-        mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
+        mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode));
 
         std::vector<Color> expectedColors(
                 static_cast<size_t>(getDisplayWidth() * getDisplayHeight()));
@@ -1412,7 +1298,8 @@
 
         writeLayers(mLayers);
         ASSERT_TRUE(mReader.takeErrors().empty());
-        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                 VtsComposerClient::kNoFrameIntervalNs);
         execute();
         if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
             GTEST_SUCCEED();
@@ -1446,7 +1333,7 @@
         ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
 
         mLayer->setTransform(Transform::ROT_180);
-        mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
+        mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode));
 
         std::vector<Color> expectedColors(
                 static_cast<size_t>(getDisplayWidth() * getDisplayHeight()));
@@ -1458,7 +1345,8 @@
 
         writeLayers(mLayers);
         ASSERT_TRUE(mReader.takeErrors().empty());
-        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                 VtsComposerClient::kNoFrameIntervalNs);
         execute();
         if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
             GTEST_SUCCEED();
@@ -1475,6 +1363,101 @@
     }
 }
 
+class GraphicsColorManagementCompositionTest
+    : public GraphicsCompositionTestBase,
+      public testing::WithParamInterface<std::tuple<std::string, Dataspace, Dataspace, Dataspace>> {
+  public:
+    void SetUp() override {
+        SetUpBase(std::get<0>(GetParam()));
+        // for some reason only sRGB reliably works
+        mTestColorModes.erase(
+                std::remove_if(mTestColorModes.begin(), mTestColorModes.end(),
+                               [](ColorMode mode) { return mode != ColorMode::SRGB; }),
+                mTestColorModes.end());
+        auto standard = std::get<1>(GetParam());
+        auto transfer = std::get<2>(GetParam());
+        auto range = std::get<3>(GetParam());
+
+        mLayerDataspace = static_cast<Dataspace>(static_cast<int32_t>(standard) |
+                                                 static_cast<int32_t>(transfer) |
+                                                 static_cast<int32_t>(range));
+        ALOGD("Invoking test for dataspace: {%s, %s, %s}", toString(standard).c_str(),
+              toString(transfer).c_str(), toString(range).c_str());
+    }
+
+    void makeLayer() {
+        mLayer = std::make_shared<TestBufferLayer>(
+                mComposerClient, *mTestRenderEngine, getPrimaryDisplayId(), getDisplayWidth(),
+                getDisplayHeight(), common::PixelFormat::RGBA_8888);
+        mLayer->setDisplayFrame({0, 0, getDisplayWidth(), getDisplayHeight()});
+        mLayer->setZOrder(10);
+        mLayer->setAlpha(1.f);
+        mLayer->setDataspace(mLayerDataspace);
+    }
+
+    void fillColor(Color color) {
+        std::vector<Color> baseColors(static_cast<size_t>(getDisplayWidth() * getDisplayHeight()));
+        ReadbackHelper::fillColorsArea(baseColors, getDisplayWidth(),
+                                       common::Rect{.left = 0,
+                                                    .top = 0,
+                                                    .right = getDisplayWidth(),
+                                                    .bottom = getDisplayHeight()},
+                                       color);
+        ASSERT_NO_FATAL_FAILURE(mLayer->setBuffer(baseColors));
+    }
+
+    Dataspace mLayerDataspace;
+    std::shared_ptr<TestBufferLayer> mLayer;
+};
+
+TEST_P(GraphicsColorManagementCompositionTest, ColorConversion) {
+    for (ColorMode mode : mTestColorModes) {
+        EXPECT_TRUE(mComposerClient
+                            ->setColorMode(getPrimaryDisplayId(), mode, RenderIntent::COLORIMETRIC)
+                            .isOk());
+
+        bool isSupported;
+        ASSERT_NO_FATAL_FAILURE(isSupported = getHasReadbackBuffer());
+        if (!isSupported) {
+            GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace";
+            return;
+        }
+
+        mClientCompositionDisplaySettings.outputDataspace =
+                static_cast<::android::ui::Dataspace>(mDataspace);
+        mTestRenderEngine->setDisplaySettings(mClientCompositionDisplaySettings);
+
+        makeLayer();
+        for (auto color : {LIGHT_RED, LIGHT_GREEN, LIGHT_BLUE}) {
+            ALOGD("Testing color: %f, %f, %f, %f with color mode: %d", color.r, color.g, color.b,
+                  color.a, mode);
+            ReadbackBuffer readbackBuffer(getPrimaryDisplayId(), mComposerClient, getDisplayWidth(),
+                                          getDisplayHeight(), mPixelFormat, mDataspace);
+            ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
+            fillColor(color);
+            writeLayers({mLayer});
+            EXPECT_TRUE(mComposerClient->setPowerMode(getPrimaryDisplayId(), PowerMode::ON).isOk());
+
+            ASSERT_TRUE(mReader.takeErrors().empty());
+            mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                     VtsComposerClient::kNoFrameIntervalNs);
+            execute();
+            if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
+                continue;
+            }
+            ASSERT_TRUE(mReader.takeErrors().empty());
+            mWriter->presentDisplay(getPrimaryDisplayId());
+            execute();
+            ASSERT_TRUE(mReader.takeErrors().empty());
+
+            mTestRenderEngine->setRenderLayers({mLayer});
+            ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->drawLayers());
+            ASSERT_NO_FATAL_FAILURE(
+                    mTestRenderEngine->checkColorBuffer(readbackBuffer.getBuffer()));
+        }
+    }
+}
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsCompositionTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, GraphicsCompositionTest,
@@ -1493,5 +1476,17 @@
         testing::ValuesIn(::android::getAidlHalInstanceNames(IComposer::descriptor)),
         ::android::PrintInstanceNameToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsColorManagementCompositionTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance, GraphicsColorManagementCompositionTest,
+                         testing::Combine(testing::ValuesIn(::android::getAidlHalInstanceNames(
+                                                  IComposer::descriptor)),
+                                          // Only check sRGB, but verify that extended range
+                                          // doesn't trigger any gamma shifts
+                                          testing::Values(Dataspace::STANDARD_BT709),
+                                          testing::Values(Dataspace::TRANSFER_SRGB),
+                                          // Don't test limited range until we send YUV overlays
+                                          testing::Values(Dataspace::RANGE_FULL,
+                                                          Dataspace::RANGE_EXTENDED)));
+
 }  // namespace
 }  // namespace aidl::android::hardware::graphics::composer3::vts
diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
index 1e6f34b..c135298 100644
--- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
+++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
@@ -34,6 +34,7 @@
 #include <ui/PixelFormat.h>
 #include <algorithm>
 #include <iterator>
+#include <mutex>
 #include <numeric>
 #include <string>
 #include <thread>
@@ -1381,21 +1382,17 @@
     void execute() {
         std::vector<CommandResultPayload> payloads;
         for (auto& [_, writer] : mWriters) {
-            auto commands = writer.takePendingCommands();
-            if (commands.empty()) {
-                continue;
-            }
-
-            auto [status, results] = mComposerClient->executeCommands(commands);
-            ASSERT_TRUE(status.isOk()) << "executeCommands failed " << status.getDescription();
-
-            payloads.reserve(payloads.size() + results.size());
-            payloads.insert(payloads.end(), std::make_move_iterator(results.begin()),
-                            std::make_move_iterator(results.end()));
+            executeInternal(writer, payloads);
         }
         mReader.parse(std::move(payloads));
     }
 
+    void execute(ComposerClientWriter& writer, ComposerClientReader& reader) {
+        std::vector<CommandResultPayload> payloads;
+        executeInternal(writer, payloads);
+        reader.parse(std::move(payloads));
+    }
+
     static inline auto toTimePoint(nsecs_t time) {
         return std::chrono::time_point<std::chrono::steady_clock>(std::chrono::nanoseconds(time));
     }
@@ -1482,7 +1479,8 @@
                                   /*acquireFence*/ -1);
             writer.setLayerDataspace(display.getDisplayId(), layer, common::Dataspace::UNKNOWN);
 
-            writer.validateDisplay(display.getDisplayId(), ComposerClientWriter::kNoTimestamp);
+            writer.validateDisplay(display.getDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                   VtsComposerClient::kNoFrameIntervalNs);
             execute();
             ASSERT_TRUE(mReader.takeErrors().empty());
 
@@ -1499,7 +1497,8 @@
                                   /*acquireFence*/ -1);
             writer.setLayerSurfaceDamage(display.getDisplayId(), layer,
                                          std::vector<Rect>(1, {0, 0, 10, 10}));
-            writer.validateDisplay(display.getDisplayId(), ComposerClientWriter::kNoTimestamp);
+            writer.validateDisplay(display.getDisplayId(), ComposerClientWriter::kNoTimestamp,
+                                   VtsComposerClient::kNoFrameIntervalNs);
             execute();
             ASSERT_TRUE(mReader.takeErrors().empty());
 
@@ -1513,7 +1512,8 @@
     sp<::android::Fence> presentAndGetFence(
             std::optional<ClockMonotonicTimestamp> expectedPresentTime) {
         auto& writer = getWriter(getPrimaryDisplayId());
-        writer.validateDisplay(getPrimaryDisplayId(), expectedPresentTime);
+        writer.validateDisplay(getPrimaryDisplayId(), expectedPresentTime,
+                               VtsComposerClient::kNoFrameIntervalNs);
         execute();
         EXPECT_TRUE(mReader.takeErrors().empty());
 
@@ -1720,6 +1720,7 @@
     // clang-format on
 
     ComposerClientWriter& getWriter(int64_t display) {
+        std::lock_guard guard{mWritersMutex};
         auto [it, _] = mWriters.try_emplace(display, display);
         return it->second;
     }
@@ -1727,7 +1728,27 @@
     ComposerClientReader mReader;
 
   private:
-    std::unordered_map<int64_t, ComposerClientWriter> mWriters;
+    void executeInternal(ComposerClientWriter& writer,
+                         std::vector<CommandResultPayload>& payloads) {
+        auto commands = writer.takePendingCommands();
+        if (commands.empty()) {
+            return;
+        }
+
+        auto [status, results] = mComposerClient->executeCommands(commands);
+        ASSERT_TRUE(status.isOk()) << "executeCommands failed " << status.getDescription();
+
+        payloads.reserve(payloads.size() + results.size());
+        payloads.insert(payloads.end(), std::make_move_iterator(results.begin()),
+                        std::make_move_iterator(results.end()));
+    }
+
+    // Guards access to the map itself. Callers must ensure not to attempt to
+    // - modify the same writer from multiple threads
+    // - insert a new writer into the map during concurrent access, which would invalidate
+    //   references from other threads
+    std::mutex mWritersMutex;
+    std::unordered_map<int64_t, ComposerClientWriter> mWriters GUARDED_BY(mWritersMutex);
 };
 
 TEST_P(GraphicsComposerAidlCommandTest, SetColorTransform) {
@@ -1834,20 +1855,23 @@
 
 TEST_P(GraphicsComposerAidlCommandTest, ValidDisplay) {
     auto& writer = getWriter(getPrimaryDisplayId());
-    writer.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+    writer.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                           VtsComposerClient::kNoFrameIntervalNs);
     execute();
 }
 
 TEST_P(GraphicsComposerAidlCommandTest, AcceptDisplayChanges) {
     auto& writer = getWriter(getPrimaryDisplayId());
-    writer.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+    writer.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                           VtsComposerClient::kNoFrameIntervalNs);
     writer.acceptDisplayChanges(getPrimaryDisplayId());
     execute();
 }
 
 TEST_P(GraphicsComposerAidlCommandTest, PresentDisplay) {
     auto& writer = getWriter(getPrimaryDisplayId());
-    writer.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+    writer.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                           VtsComposerClient::kNoFrameIntervalNs);
     writer.presentDisplay(getPrimaryDisplayId());
     execute();
 }
@@ -1860,10 +1884,6 @@
  * surface damage have been set
  */
 TEST_P(GraphicsComposerAidlCommandTest, PresentDisplayNoLayerStateChanges) {
-    if (!hasCapability(Capability::SKIP_VALIDATE)) {
-        GTEST_SUCCEED() << "Device does not have skip validate capability, skipping";
-        return;
-    }
     EXPECT_TRUE(mComposerClient->setPowerMode(getPrimaryDisplayId(), PowerMode::ON).isOk());
 
     const auto& [renderIntentsStatus, renderIntents] =
@@ -1890,7 +1910,8 @@
         writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, handle,
                               /*acquireFence*/ -1);
         writer.setLayerDataspace(getPrimaryDisplayId(), layer, Dataspace::UNKNOWN);
-        writer.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+        writer.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                               VtsComposerClient::kNoFrameIntervalNs);
         execute();
         if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
             GTEST_SUCCEED() << "Composition change requested, skipping test";
@@ -1932,7 +1953,8 @@
                    (float)getPrimaryDisplay().getDisplayHeight()};
     configureLayer(getPrimaryDisplay(), layer, Composition::CURSOR, displayFrame, cropRect);
     writer.setLayerDataspace(getPrimaryDisplayId(), layer, Dataspace::UNKNOWN);
-    writer.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+    writer.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                           VtsComposerClient::kNoFrameIntervalNs);
 
     execute();
 
@@ -1947,7 +1969,8 @@
     execute();
 
     writer.setLayerCursorPosition(getPrimaryDisplayId(), layer, /*x*/ 0, /*y*/ 0);
-    writer.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+    writer.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp,
+                           VtsComposerClient::kNoFrameIntervalNs);
     writer.presentDisplay(getPrimaryDisplayId());
     execute();
 }
@@ -2146,7 +2169,8 @@
         auto& writer = getWriter(display.getDisplayId());
         writer.setLayerBuffer(display.getDisplayId(), layer, /*slot*/ 0, decorBuffer->handle,
                               /*acquireFence*/ -1);
-        writer.validateDisplay(display.getDisplayId(), ComposerClientWriter::kNoTimestamp);
+        writer.validateDisplay(display.getDisplayId(), ComposerClientWriter::kNoTimestamp,
+                               VtsComposerClient::kNoFrameIntervalNs);
         execute();
         if (support) {
             ASSERT_TRUE(mReader.takeErrors().empty());
@@ -2571,8 +2595,11 @@
  * Capability::SKIP_VALIDATE has been deprecated and should not be enabled.
  */
 TEST_P(GraphicsComposerAidlCommandV2Test, SkipValidateDeprecatedTest) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
     ASSERT_FALSE(hasCapability(Capability::SKIP_VALIDATE))
             << "Found Capability::SKIP_VALIDATE capability.";
+#pragma clang diagnostic pop
 }
 
 TEST_P(GraphicsComposerAidlCommandV2Test, SetLayerBufferSlotsToClear) {
@@ -2785,6 +2812,107 @@
     }
 }
 
+TEST_P(GraphicsComposerAidlCommandTest, MultiThreadedPresent) {
+    std::vector<VtsDisplay*> displays;
+    for (auto& display : mDisplays) {
+        if (hasDisplayCapability(display.getDisplayId(),
+                                 DisplayCapability::MULTI_THREADED_PRESENT)) {
+            displays.push_back(&display);
+        }
+    }
+
+    const size_t numDisplays = displays.size();
+    if (numDisplays <= 1u) {
+        GTEST_SKIP();
+    }
+
+    // When multi-threaded, use a reader per display. As with mWriters, this mutex
+    // guards access to the map.
+    std::mutex readersMutex;
+    std::unordered_map<int64_t, ComposerClientReader> readers;
+    std::vector<std::thread> threads;
+    threads.reserve(numDisplays);
+
+    // Each display will have a layer to present. This maps from the display to
+    // the layer, so we can properly destroy each layer at the end.
+    std::unordered_map<int64_t, int64_t> layers;
+
+    for (auto* const display : displays) {
+        const int64_t displayId = display->getDisplayId();
+
+        // Ensure that all writers and readers have been added to their respective
+        // maps initially, so that the following loop never modifies the maps. The
+        // maps are accessed from different threads, and if the maps were modified,
+        // this would invalidate their iterators, and therefore references to the
+        // writers and readers.
+        auto& writer = getWriter(displayId);
+        {
+            std::lock_guard guard{readersMutex};
+            readers.try_emplace(displayId, displayId);
+        }
+
+        EXPECT_TRUE(mComposerClient->setPowerMode(displayId, PowerMode::ON).isOk());
+
+        const auto& [status, layer] = mComposerClient->createLayer(displayId, kBufferSlotCount);
+        const auto buffer = allocate(::android::PIXEL_FORMAT_RGBA_8888);
+        ASSERT_NE(nullptr, buffer);
+        ASSERT_EQ(::android::OK, buffer->initCheck());
+        ASSERT_NE(nullptr, buffer->handle);
+
+        configureLayer(*display, layer, Composition::DEVICE, display->getFrameRect(),
+                       display->getCrop());
+        writer.setLayerBuffer(displayId, layer, /*slot*/ 0, buffer->handle,
+                              /*acquireFence*/ -1);
+        writer.setLayerDataspace(displayId, layer, common::Dataspace::UNKNOWN);
+        layers.try_emplace(displayId, layer);
+    }
+
+    for (auto* const display : displays) {
+        const int64_t displayId = display->getDisplayId();
+        auto& writer = getWriter(displayId);
+        std::unique_lock lock{readersMutex};
+        auto& reader = readers.at(displayId);
+        lock.unlock();
+
+        writer.validateDisplay(displayId, ComposerClientWriter::kNoTimestamp,
+                               VtsComposerClient::kNoFrameIntervalNs);
+        execute(writer, reader);
+
+        threads.emplace_back([this, displayId, &readers, &readersMutex]() {
+            auto& writer = getWriter(displayId);
+            std::unique_lock lock{readersMutex};
+            ComposerClientReader& reader = readers.at(displayId);
+            lock.unlock();
+
+            writer.presentDisplay(displayId);
+            execute(writer, reader);
+            ASSERT_TRUE(reader.takeErrors().empty());
+
+            auto presentFence = reader.takePresentFence(displayId);
+            // take ownership
+            const int fenceOwner = presentFence.get();
+            *presentFence.getR() = -1;
+            EXPECT_NE(-1, fenceOwner);
+            const auto presentFence2 = sp<::android::Fence>::make(fenceOwner);
+            presentFence2->waitForever(LOG_TAG);
+        });
+    }
+
+    for (auto& thread : threads) {
+        thread.join();
+    }
+
+    for (auto& [displayId, layer] : layers) {
+        EXPECT_TRUE(mComposerClient->destroyLayer(displayId, layer).isOk());
+    }
+
+    std::lock_guard guard{readersMutex};
+    for (auto& [displayId, reader] : readers) {
+        ASSERT_TRUE(reader.takeErrors().empty());
+        ASSERT_TRUE(reader.takeChangedCompositionTypes(displayId).empty());
+    }
+}
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsComposerAidlCommandTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, GraphicsComposerAidlCommandTest,
diff --git a/graphics/mapper/4.0/utils/vts/Android.bp b/graphics/mapper/4.0/utils/vts/Android.bp
index 51e871b..7815d41 100644
--- a/graphics/mapper/4.0/utils/vts/Android.bp
+++ b/graphics/mapper/4.0/utils/vts/Android.bp
@@ -47,7 +47,7 @@
     ],
     export_static_lib_headers: [
         "android.hardware.graphics.allocator@4.0",
-        "android.hardware.graphics.common-V4-ndk",
+        "android.hardware.graphics.common-V5-ndk",
         "android.hardware.graphics.mapper@4.0",
     ],
     export_include_dirs: ["include"],
diff --git a/graphics/mapper/stable-c/implutils/include/android/hardware/graphics/mapper/utils/IMapperProvider.h b/graphics/mapper/stable-c/implutils/include/android/hardware/graphics/mapper/utils/IMapperProvider.h
index 957fdc9..c4d1a0d 100644
--- a/graphics/mapper/stable-c/implutils/include/android/hardware/graphics/mapper/utils/IMapperProvider.h
+++ b/graphics/mapper/stable-c/implutils/include/android/hardware/graphics/mapper/utils/IMapperProvider.h
@@ -106,15 +106,15 @@
     static_assert(std::is_constructible_v<IMPL>, "Implementation must have a no-args constructor");
 
     std::once_flag mLoadOnceFlag;
-    std::optional<IMPL> mImpl;
-    AIMapper mMapper = {};
+    IMPL* _Nullable mImpl;
+    AIMapper* _Nullable mMapper;
 
     static IMPL& impl() {
         return *reinterpret_cast<IMapperProvider<IMPL>*>(provider::sIMapperInstance)->mImpl;
     }
 
     void bindV5() {
-        mMapper.v5 = {
+        mMapper->v5 = {
                 .importBuffer = [](const native_handle_t* _Nonnull handle,
                                    buffer_handle_t _Nullable* _Nonnull outBufferHandle)
                         -> AIMapper_Error { return impl().importBuffer(handle, outBufferHandle); },
@@ -208,13 +208,14 @@
             LOG_ALWAYS_FATAL_IF(provider::sIMapperInstance != nullptr,
                                 "AIMapper implementation already loaded!");
             provider::sIMapperInstance = this;
-            mImpl.emplace();
-            mMapper.version = IMPL::version;
+            mImpl = new IMPL();
+            mMapper = new AIMapper();
+            mMapper->version = IMPL::version;
             if (IMPL::version >= AIMAPPER_VERSION_5) {
                 bindV5();
             }
         });
-        *outImplementation = &mMapper;
+        *outImplementation = mMapper;
         return AIMAPPER_ERROR_NONE;
     }
 };
diff --git a/health/1.0/default/convert.cpp b/health/1.0/default/convert.cpp
index 31b4679..e12e197 100644
--- a/health/1.0/default/convert.cpp
+++ b/health/1.0/default/convert.cpp
@@ -117,7 +117,7 @@
     info.batteryCycleCount      = p->batteryCycleCount;
     info.batteryFullCharge      = p->batteryFullCharge;
     info.batteryChargeCounter   = p->batteryChargeCounter;
-    info.batteryTechnology      = p->batteryTechnology;
+    info.batteryTechnology      = p->batteryTechnology.c_str();
 }
 
 void convertFromHealthInfo(const HealthInfo& info,
diff --git a/input/processor/aidl/default/Android.bp b/input/processor/aidl/default/Android.bp
index f485597..bdd27e2 100644
--- a/input/processor/aidl/default/Android.bp
+++ b/input/processor/aidl/default/Android.bp
@@ -28,27 +28,54 @@
     ],
 }
 
-filegroup {
+prebuilt_etc {
     name: "android.hardware.input.processor.xml",
-    srcs: ["android.hardware.input.processor.xml"],
+    src: "android.hardware.input.processor.xml",
+    sub_dir: "vintf",
+    installable: false,
+}
+
+prebuilt_etc {
+    name: "inputprocessor-default.rc",
+    src: "inputprocessor-default.rc",
+    installable: false,
 }
 
 cc_binary {
     name: "android.hardware.input.processor-service.example",
     relative_install_path: "hw",
-    init_rc: ["inputprocessor-default.rc"],
-    vintf_fragments: [":android.hardware.input.processor.xml"],
     vendor: true,
+    installable: false, // installed in APEX
+
+    stl: "c++_static",
     shared_libs: [
-        "libbase",
         "libbinder_ndk",
         "liblog",
-        "libutils",
-        "android.hardware.input.common-V1-ndk",
-        "android.hardware.input.processor-V1-ndk",
     ],
     static_libs: [
+        "android.hardware.input.common-V1-ndk",
+        "android.hardware.input.processor-V1-ndk",
+        "libbase",
         "libinputprocessorexampleimpl",
+        "libutils",
     ],
     srcs: ["main.cpp"],
 }
+
+apex {
+    name: "com.android.hardware.input.processor",
+    file_contexts: "apex_file_contexts",
+    manifest: "apex_manifest.json",
+    key: "com.android.hardware.key",
+    certificate: ":com.android.hardware.certificate",
+    updatable: false,
+    vendor: true,
+
+    binaries: [
+        "android.hardware.input.processor-service.example",
+    ],
+    prebuilts: [
+        "android.hardware.input.processor.xml",
+        "inputprocessor-default.rc",
+    ],
+}
diff --git a/input/processor/aidl/default/apex_file_contexts b/input/processor/aidl/default/apex_file_contexts
new file mode 100644
index 0000000..bd2945a
--- /dev/null
+++ b/input/processor/aidl/default/apex_file_contexts
@@ -0,0 +1,3 @@
+(/.*)?                                                          u:object_r:vendor_file:s0
+/etc(/.*)?                                                      u:object_r:vendor_configs_file:s0
+/bin/hw/android\.hardware\.input\.processor-service\.example    u:object_r:hal_input_processor_default_exec:s0
diff --git a/input/processor/aidl/default/apex_manifest.json b/input/processor/aidl/default/apex_manifest.json
new file mode 100644
index 0000000..2fe0a1d
--- /dev/null
+++ b/input/processor/aidl/default/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+    "name": "com.android.hardware.input.processor",
+    "version": 1
+}
\ No newline at end of file
diff --git a/input/processor/aidl/default/inputprocessor-default.rc b/input/processor/aidl/default/inputprocessor-default.rc
index bcc6c02..ade0f11 100644
--- a/input/processor/aidl/default/inputprocessor-default.rc
+++ b/input/processor/aidl/default/inputprocessor-default.rc
@@ -1,4 +1,4 @@
-service vendor.inputprocessor-default /vendor/bin/hw/android.hardware.input.processor-service.example
+service vendor.inputprocessor-default /apex/com.android.hardware.input.processor/bin/hw/android.hardware.input.processor-service.example
     class hal
     user nobody
     group nobody
\ No newline at end of file
diff --git a/ir/aidl/default/Android.bp b/ir/aidl/default/Android.bp
index a8096c2..4006554 100644
--- a/ir/aidl/default/Android.bp
+++ b/ir/aidl/default/Android.bp
@@ -36,8 +36,58 @@
         "liblog",
         "libutils",
         "android.hardware.ir-V1-ndk",
-        "libhardware"
+        "libhardware",
     ],
 
     srcs: ["main.cpp"],
 }
+
+prebuilt_etc {
+    name: "android.hardware.ir-service.example.rc",
+    src: ":gen-android.hardware.ir-service.example.rc",
+    installable: false,
+}
+
+genrule {
+    name: "gen-android.hardware.ir-service.example.rc",
+    srcs: ["android.hardware.ir-service.example.rc"],
+    out: ["android.hardware.ir-service.example.apex.rc"],
+    cmd: "sed -e 's@/vendor/bin/@/apex/com.android.hardware.ir/bin/@' $(in) > $(out)",
+}
+
+prebuilt_etc {
+    name: "android.hardware.ir-service.example.xml",
+    src: "android.hardware.ir-service.example.xml",
+    sub_dir: "vintf",
+    installable: false,
+}
+
+filegroup {
+    name: "com.android.hardware.ir_file_contexts",
+    srcs: ["apex_file_contexts"],
+}
+
+filegroup {
+    name: "com.android.hardware.ir_apex_manifest.json",
+    srcs: ["apex_manifest.json"],
+}
+
+apex_defaults {
+    name: "com.android.hardware.ir",
+    // Reference to the filegroup instead of direct path since
+    // paths in defaults don't work in a different directory.
+    file_contexts: ":com.android.hardware.ir_file_contexts",
+    manifest: ":com.android.hardware.ir_apex_manifest.json",
+
+    updatable: false,
+    vendor: true,
+
+    binaries: [
+        "android.hardware.ir-service.example",
+    ],
+    prebuilts: [
+        "android.hardware.ir-service.example.rc",
+        "android.hardware.ir-service.example.xml",
+        "android.hardware.consumerir.prebuilt.xml", // feature
+    ],
+}
diff --git a/ir/aidl/default/android.hardware.ir-service.example.rc b/ir/aidl/default/android.hardware.ir-service.example.rc
index d27f282..1a721da 100644
--- a/ir/aidl/default/android.hardware.ir-service.example.rc
+++ b/ir/aidl/default/android.hardware.ir-service.example.rc
@@ -1,4 +1,4 @@
-service vendor.ir-default /vendor/bin/hw/android.hardware.ir-service.example
+service vendor.ir-default /apex/com.android.hardware.ir/bin/hw/android.hardware.ir-service.example
     class hal
     user system
     group system
diff --git a/ir/aidl/default/apex_file_contexts b/ir/aidl/default/apex_file_contexts
new file mode 100644
index 0000000..3bbf131
--- /dev/null
+++ b/ir/aidl/default/apex_file_contexts
@@ -0,0 +1,3 @@
+(/.*)?                                          u:object_r:vendor_file:s0
+/etc(/.*)?                                      u:object_r:vendor_configs_file:s0
+/bin/hw/android\.hardware\.ir-service\.example  u:object_r:hal_ir_default_exec:s0
diff --git a/ir/aidl/default/apex_manifest.json b/ir/aidl/default/apex_manifest.json
new file mode 100644
index 0000000..d384375
--- /dev/null
+++ b/ir/aidl/default/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+    "name": "com.android.hardware.ir",
+    "version": 1
+}
\ No newline at end of file
diff --git a/keymaster/4.0/support/fuzzer/Android.bp b/keymaster/4.0/support/fuzzer/Android.bp
index 8bc681a..f61d10f 100644
--- a/keymaster/4.0/support/fuzzer/Android.bp
+++ b/keymaster/4.0/support/fuzzer/Android.bp
@@ -39,9 +39,17 @@
     ],
     fuzz_config: {
         cc: [
-            "android-media-fuzzing-reports@google.com",
+            "android-hardware-security@google.com",
         ],
-        componentid: 533764,
+        componentid: 1084733,
+        hotlists: [
+            "4593311",
+        ],
+        description: "The fuzzer targets the APIs of libkeymaster4support",
+        vector: "local_no_privileges_required",
+        service_privilege: "privileged",
+        users: "multi_user",
+        fuzzed_code_usage: "shipped",
     },
 }
 
diff --git a/keymaster/4.0/support/fuzzer/keymaster4_utils_fuzzer.cpp b/keymaster/4.0/support/fuzzer/keymaster4_utils_fuzzer.cpp
index bf074e8..55c2630 100644
--- a/keymaster/4.0/support/fuzzer/keymaster4_utils_fuzzer.cpp
+++ b/keymaster/4.0/support/fuzzer/keymaster4_utils_fuzzer.cpp
@@ -46,33 +46,42 @@
     support::getOsVersion();
     support::getOsPatchlevel();
 
-    VerificationToken token;
-    token.challenge = mFdp->ConsumeIntegral<uint64_t>();
-    token.timestamp = mFdp->ConsumeIntegral<uint64_t>();
-    token.securityLevel = mFdp->PickValueInArray(kSecurityLevel);
-    size_t vectorSize = mFdp->ConsumeIntegralInRange<size_t>(0, kMaxVectorSize);
-    token.mac.resize(vectorSize);
-    for (size_t n = 0; n < vectorSize; ++n) {
-        token.mac[n] = n;
+    while (mFdp->remaining_bytes() > 0) {
+        auto keymaster_function = mFdp->PickValueInArray<const std::function<void()>>({
+                [&]() {
+                    VerificationToken token;
+                    token.challenge = mFdp->ConsumeIntegral<uint64_t>();
+                    token.timestamp = mFdp->ConsumeIntegral<uint64_t>();
+                    token.securityLevel = mFdp->PickValueInArray(kSecurityLevel);
+                    size_t vectorSize = mFdp->ConsumeIntegralInRange<size_t>(0, kMaxVectorSize);
+                    token.mac.resize(vectorSize);
+                    for (size_t n = 0; n < vectorSize; ++n) {
+                        token.mac[n] = mFdp->ConsumeIntegral<uint8_t>();
+                    }
+                    std::optional<std::vector<uint8_t>> serialized =
+                            serializeVerificationToken(token);
+                    if (serialized.has_value()) {
+                        std::optional<VerificationToken> deserialized =
+                                deserializeVerificationToken(serialized.value());
+                    }
+                },
+                [&]() {
+                    std::vector<uint8_t> dataVector;
+                    size_t size = mFdp->ConsumeIntegralInRange<size_t>(0, sizeof(hw_auth_token_t));
+                    dataVector = mFdp->ConsumeBytes<uint8_t>(size);
+                    support::blob2hidlVec(dataVector.data(), dataVector.size());
+                    support::blob2hidlVec(dataVector);
+                    HardwareAuthToken authToken = support::hidlVec2AuthToken(dataVector);
+                    hidl_vec<uint8_t> volatile hidlVector = support::authToken2HidlVec(authToken);
+                },
+                [&]() {
+                    std::string str = mFdp->ConsumeRandomLengthString(kMaxCharacters);
+                    support::blob2hidlVec(str);
+                },
+        });
+        keymaster_function();
     }
-    std::optional<std::vector<uint8_t>> serialized = serializeVerificationToken(token);
-    if (serialized.has_value()) {
-        std::optional<VerificationToken> deserialized =
-                deserializeVerificationToken(serialized.value());
-    }
-
-    std::vector<uint8_t> dataVector;
-    size_t size = mFdp->ConsumeIntegralInRange<size_t>(0, sizeof(hw_auth_token_t));
-    dataVector = mFdp->ConsumeBytes<uint8_t>(size);
-    support::blob2hidlVec(dataVector.data(), dataVector.size());
-
-    support::blob2hidlVec(dataVector);
-
-    std::string str = mFdp->ConsumeRandomLengthString(kMaxCharacters);
-    support::blob2hidlVec(str);
-
-    HardwareAuthToken authToken = support::hidlVec2AuthToken(dataVector);
-    hidl_vec<uint8_t> volatile hidlVector = support::authToken2HidlVec(authToken);
+    return;
 }
 
 void KeyMaster4UtilsFuzzer::process(const uint8_t* data, size_t size) {
diff --git a/media/OWNERS b/media/OWNERS
index 71a53ef..01b440a 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,7 +1,6 @@
 # Bug component: 25690
 
 # Media team
-jgus@google.com
 lajos@google.com
 taklee@google.com
 wonsik@google.com
diff --git a/media/bufferpool/aidl/Android.bp b/media/bufferpool/aidl/Android.bp
index 8caf525..8e013e0 100644
--- a/media/bufferpool/aidl/Android.bp
+++ b/media/bufferpool/aidl/Android.bp
@@ -46,6 +46,9 @@
             ],
             min_sdk_version: "29",
         },
+        rust: {
+            enabled: true,
+        },
     },
     versions_with_info: [
         {
diff --git a/media/c2/aidl/Android.bp b/media/c2/aidl/Android.bp
index 3f2cadb..3c0915d 100644
--- a/media/c2/aidl/Android.bp
+++ b/media/c2/aidl/Android.bp
@@ -11,7 +11,7 @@
 
 aidl_interface {
     name: "android.hardware.media.c2",
-    min_sdk_version: "31",
+    min_sdk_version: "30",
     vendor_available: true,
     double_loadable: true,
     srcs: ["android/hardware/media/c2/*.aidl"],
@@ -41,5 +41,12 @@
                 "libnativewindow",
             ],
         },
+        rust: {
+            min_sdk_version: "31",
+            enabled: true,
+            additional_rustlibs: [
+                "libnativewindow_rs",
+            ],
+        },
     },
 }
diff --git a/media/c2/aidl/aidl_api/android.hardware.media.c2/current/android/hardware/media/c2/IComponent.aidl b/media/c2/aidl/aidl_api/android.hardware.media.c2/current/android/hardware/media/c2/IComponent.aidl
index c7d8a97..7d58340 100644
--- a/media/c2/aidl/aidl_api/android.hardware.media.c2/current/android/hardware/media/c2/IComponent.aidl
+++ b/media/c2/aidl/aidl_api/android.hardware.media.c2/current/android/hardware/media/c2/IComponent.aidl
@@ -49,8 +49,12 @@
     long blockPoolId;
     android.hardware.media.c2.IConfigurable configurable;
   }
+  parcelable C2AidlGbAllocator {
+    android.hardware.media.c2.IGraphicBufferAllocator igba;
+    ParcelFileDescriptor waitableFd;
+  }
   union BlockPoolAllocator {
     int allocatorId;
-    android.hardware.media.c2.IGraphicBufferAllocator igba;
+    android.hardware.media.c2.IComponent.C2AidlGbAllocator allocator;
   }
 }
diff --git a/media/c2/aidl/aidl_api/android.hardware.media.c2/current/android/hardware/media/c2/IGraphicBufferAllocator.aidl b/media/c2/aidl/aidl_api/android.hardware.media.c2/current/android/hardware/media/c2/IGraphicBufferAllocator.aidl
index da3d5ff..3e460dd 100644
--- a/media/c2/aidl/aidl_api/android.hardware.media.c2/current/android/hardware/media/c2/IGraphicBufferAllocator.aidl
+++ b/media/c2/aidl/aidl_api/android.hardware.media.c2/current/android/hardware/media/c2/IGraphicBufferAllocator.aidl
@@ -36,7 +36,7 @@
 interface IGraphicBufferAllocator {
   android.hardware.media.c2.IGraphicBufferAllocator.Allocation allocate(in android.hardware.media.c2.IGraphicBufferAllocator.Description desc);
   boolean deallocate(in long id);
-  android.hardware.media.c2.IGraphicBufferAllocator.WaitableFds getWaitableFds();
+  ParcelFileDescriptor getWaitableFd();
   parcelable Allocation {
     android.hardware.HardwareBuffer buffer;
     ParcelFileDescriptor fence;
@@ -47,8 +47,4 @@
     int format;
     long usage;
   }
-  parcelable WaitableFds {
-    ParcelFileDescriptor allocEvent;
-    ParcelFileDescriptor statusEvent;
-  }
 }
diff --git a/media/c2/aidl/android/hardware/media/c2/IComponent.aidl b/media/c2/aidl/android/hardware/media/c2/IComponent.aidl
index a7d94b1..fc923ab 100644
--- a/media/c2/aidl/android/hardware/media/c2/IComponent.aidl
+++ b/media/c2/aidl/android/hardware/media/c2/IComponent.aidl
@@ -22,6 +22,8 @@
 import android.hardware.media.c2.IConfigurable;
 import android.hardware.media.c2.IGraphicBufferAllocator;
 import android.hardware.media.c2.WorkBundle;
+import android.os.ParcelFileDescriptor;
+
 
 /**
  * Interface for an AIDL Codec2 component.
@@ -46,6 +48,18 @@
     }
 
     /**
+     * C2AIDL allocator interface along with a waitable fd.
+     *
+     * The interface is used from a specific type of C2BlockPool to allocate
+     * graphic blocks. the waitable fd is used to create a specific type of
+     * C2Fence which can be used for waiting until to allocate is not blocked.
+     */
+    parcelable C2AidlGbAllocator {
+        IGraphicBufferAllocator igba;
+        ParcelFileDescriptor waitableFd;
+    }
+
+    /**
      * Allocator for C2BlockPool.
      *
      * C2BlockPool will use a C2Allocator which is specified by an id.
@@ -53,7 +67,7 @@
      */
     union BlockPoolAllocator {
         int allocatorId;
-        IGraphicBufferAllocator igba;
+        C2AidlGbAllocator allocator;
     }
 
     /**
diff --git a/media/c2/aidl/android/hardware/media/c2/IGraphicBufferAllocator.aidl b/media/c2/aidl/android/hardware/media/c2/IGraphicBufferAllocator.aidl
index 1c97214..49c4ea4 100644
--- a/media/c2/aidl/android/hardware/media/c2/IGraphicBufferAllocator.aidl
+++ b/media/c2/aidl/android/hardware/media/c2/IGraphicBufferAllocator.aidl
@@ -75,31 +75,22 @@
     boolean deallocate(in long id);
 
     /**
-     * Fds for waitable object events.
-     *
-     * Fds are created by eventfd() with semaphore mode.
-     * For allocEvent, An integer counter regarding dequeuable buffer count is maintained
-     * by client using read()/write() to the fd. The fd can be checked whether it is
-     * readable via poll(). When in readable status, the specified counter is positive
-     * so allocate/dequeue can happen.
-     *
-     * For statusEvent, the client can notify further allocation is not feasible.
-     * e.g.) life-cycle of the underlying allocator is ended.
-     *
-     * C2Fence object should be implemented based on this Fds. Thus, C2Fence can return
-     * either by allocation being ready or allocation being infeasible by the client status
-     * change.
-     */
-    parcelable WaitableFds {
-        ParcelFileDescriptor allocEvent;
-        ParcelFileDescriptor statusEvent;
-    }
-
-    /**
-     * Gets waiable file descriptors.
+     * Gets a waitable file descriptor.
      *
      * Use this method once and cache it in order not to create unnecessary duplicated fds.
-     * The returned array will have two fds.
+     *
+     * Two file descriptors are created by pipe2(), and the reading end will be returned
+     * and shared by this method. Whenever a dequeuable buffer is ready a byte will be
+     * written to the writing end. Whenever a buffer is allocated(or dequeued) a byte will
+     * be read from the reading end.
+     *
+     * The returned file descriptor(the reading end) can be polled whether the read is ready
+     * via ::poll(). If no more allocate() can be fulfilled(by status change or etc.), the
+     * writing end will be closed. In the case, POLLHUP event can be retrieved via ::poll().
+     *
+     * C2Fence object should be implemented based on this Fd. Thus, C2Fence can return
+     * either by allocation being ready or allocation being infeasible by the client status
+     * change.
      *
      * If many waitable objects based on the same fd are competing, all watiable objects will be
      * notified. After being notified, they should invoke allocate(). At least one of them can
@@ -110,5 +101,5 @@
      * @return an fd array which will be wrapped to C2Fence and will be waited for
      *     until allocating is unblocked.
      */
-    WaitableFds getWaitableFds();
+    ParcelFileDescriptor getWaitableFd();
 }
diff --git a/neuralnetworks/aidl/Android.bp b/neuralnetworks/aidl/Android.bp
index be86879..145604c 100644
--- a/neuralnetworks/aidl/Android.bp
+++ b/neuralnetworks/aidl/Android.bp
@@ -17,7 +17,7 @@
     stability: "vintf",
     imports: [
         "android.hardware.common-V2",
-        "android.hardware.graphics.common-V4",
+        "android.hardware.graphics.common-V5",
     ],
     backend: {
         java: {
@@ -40,28 +40,28 @@
             version: "1",
             imports: [
                 "android.hardware.common-V2",
-                "android.hardware.graphics.common-V4",
+                "android.hardware.graphics.common-V5",
             ],
         },
         {
             version: "2",
             imports: [
                 "android.hardware.common-V2",
-                "android.hardware.graphics.common-V4",
+                "android.hardware.graphics.common-V5",
             ],
         },
         {
             version: "3",
             imports: [
                 "android.hardware.common-V2",
-                "android.hardware.graphics.common-V4",
+                "android.hardware.graphics.common-V5",
             ],
         },
         {
             version: "4",
             imports: [
                 "android.hardware.common-V2",
-                "android.hardware.graphics.common-V4",
+                "android.hardware.graphics.common-V5",
             ],
         },
 
diff --git a/nfc/1.0/Android.bp b/nfc/1.0/Android.bp
index 55c8639..ef12c7c 100644
--- a/nfc/1.0/Android.bp
+++ b/nfc/1.0/Android.bp
@@ -22,4 +22,8 @@
     ],
     gen_java: true,
     gen_java_constants: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.nfcservices",
+    ],
 }
diff --git a/nfc/1.1/Android.bp b/nfc/1.1/Android.bp
index a8463cf..65da2a0 100644
--- a/nfc/1.1/Android.bp
+++ b/nfc/1.1/Android.bp
@@ -22,4 +22,8 @@
         "android.hidl.base@1.0",
     ],
     gen_java: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.nfcservices",
+    ],
 }
diff --git a/nfc/1.2/Android.bp b/nfc/1.2/Android.bp
index 4831ab9..8ad93f9 100644
--- a/nfc/1.2/Android.bp
+++ b/nfc/1.2/Android.bp
@@ -22,4 +22,8 @@
         "android.hidl.base@1.0",
     ],
     gen_java: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.nfcservices",
+    ],
 }
diff --git a/nfc/aidl/Android.bp b/nfc/aidl/Android.bp
index 08ec5fe..dae9f29 100644
--- a/nfc/aidl/Android.bp
+++ b/nfc/aidl/Android.bp
@@ -34,6 +34,13 @@
             sdk_version: "module_current",
             enabled: false,
         },
+	ndk: {
+            enabled: true,
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.nfcservices",
+            ],
+        },
         rust: {
             enabled: true,
         },
diff --git a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Mode.aidl b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Mode.aidl
index efbb300..b8a0710 100644
--- a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Mode.aidl
+++ b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/Mode.aidl
@@ -51,4 +51,5 @@
   CAMERA_STREAMING_HIGH,
   GAME,
   GAME_LOADING,
+  DISPLAY_CHANGE,
 }
diff --git a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/WorkDuration.aidl b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/WorkDuration.aidl
index e86cd40..45013dd 100644
--- a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/WorkDuration.aidl
+++ b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/WorkDuration.aidl
@@ -36,4 +36,7 @@
 parcelable WorkDuration {
   long timeStampNanos;
   long durationNanos;
+  long workPeriodStartTimestampNanos;
+  long cpuDurationNanos;
+  long gpuDurationNanos;
 }
diff --git a/power/aidl/android/hardware/power/Mode.aidl b/power/aidl/android/hardware/power/Mode.aidl
index cc4b130..78f0363 100644
--- a/power/aidl/android/hardware/power/Mode.aidl
+++ b/power/aidl/android/hardware/power/Mode.aidl
@@ -134,11 +134,6 @@
     DISPLAY_INACTIVE,
 
     /**
-     * Below hints are currently not sent in Android framework but OEM might choose to
-     * implement for power/perf optimizations.
-     */
-
-    /**
      * This mode indicates that low latency audio is active.
      */
     AUDIO_STREAMING_LOW_LATENCY,
@@ -172,4 +167,10 @@
      * This mode indicates that the user is waiting for loading in a game.
      */
     GAME_LOADING,
+
+    /**
+     * This mode indicates that the display layout is changing due to rotation
+     * or switching between inner and outer panels.
+     */
+    DISPLAY_CHANGE,
 }
diff --git a/power/aidl/android/hardware/power/WorkDuration.aidl b/power/aidl/android/hardware/power/WorkDuration.aidl
index a06a058..fcd638b 100644
--- a/power/aidl/android/hardware/power/WorkDuration.aidl
+++ b/power/aidl/android/hardware/power/WorkDuration.aidl
@@ -23,8 +23,30 @@
      * sample was measured.
      */
     long timeStampNanos;
+
     /**
-     * Work duration in nanoseconds.
+     * Total work duration in nanoseconds.
      */
     long durationNanos;
+
+    /**
+     * Timestamp in nanoseconds based on CLOCK_MONOTONIC when the work starts.
+     * The work period start timestamp could be zero if the call is from
+     * the legacy SDK/NDK reportActualWorkDuration API.
+     */
+    long workPeriodStartTimestampNanos;
+
+    /**
+     * CPU work duration in nanoseconds.
+     * The CPU work duration could be the same as the total work duration if
+     * the call is from the legacy SDK/NDK reportActualWorkDuration API.
+     */
+    long cpuDurationNanos;
+
+    /**
+     * GPU work duration in nanoseconds.
+     * The GPU work duration could be zero if the call is from the legacy
+     * SDK/NDK reportActualWorkDuration API.
+     */
+    long gpuDurationNanos;
 }
diff --git a/power/aidl/default/Android.bp b/power/aidl/default/Android.bp
index c0bfbe8..e3af179 100644
--- a/power/aidl/default/Android.bp
+++ b/power/aidl/default/Android.bp
@@ -39,9 +39,11 @@
     ],
 }
 
-filegroup {
+prebuilt_etc {
     name: "android.hardware.power.xml",
-    srcs: ["power-default.xml"],
+    src: "power-default.xml",
+    sub_dir: "vintf",
+    installable: false,
 }
 
 filegroup {
diff --git a/power/aidl/default/apex/Android.bp b/power/aidl/default/apex/Android.bp
index eb04087..a21901f 100644
--- a/power/aidl/default/apex/Android.bp
+++ b/power/aidl/default/apex/Android.bp
@@ -16,17 +16,6 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-apex_key {
-    name: "com.android.hardware.power.key",
-    public_key: "com.android.hardware.power.avbpubkey",
-    private_key: "com.android.hardware.power.pem",
-}
-
-android_app_certificate {
-    name: "com.android.hardware.power.certificate",
-    certificate: "com.android.hardware.power",
-}
-
 genrule {
     name: "com.android.hardware.power.rc-srcs",
     srcs: [
@@ -34,7 +23,7 @@
         ":android.hardware.power.stats.rc",
     ],
     out: ["com.android.hardware.power.rc"],
-    cmd: "sed -E 's/\\/vendor/\\/apex\\/com.android.hardware.power/' $(in) > $(out)",
+    cmd: "sed -E 's@/vendor/bin/@/apex/com.android.hardware.power/bin/@' $(in) > $(out)",
 }
 
 prebuilt_etc {
@@ -46,13 +35,12 @@
 apex {
     name: "com.android.hardware.power",
     manifest: "apex_manifest.json",
-    key: "com.android.hardware.power.key",
-    certificate: ":com.android.hardware.power.certificate",
+    key: "com.android.hardware.key",
+    certificate: ":com.android.hardware.certificate",
     file_contexts: "file_contexts",
-    use_vndk_as_stable: true,
     updatable: false,
-    // Install the apex in /vendor/apex
-    soc_specific: true,
+    vendor: true,
+
     // Bundle the Power and PowerStats HALs into this one APEX.
     binaries: [
         "android.hardware.power-service.example",
@@ -60,10 +48,8 @@
     ],
     prebuilts: [
         "com.android.hardware.power.rc",
-    ],
-    vintf_fragments: [
-        ":android.hardware.power.xml",
-        ":android.hardware.power.stats.xml",
+        "android.hardware.power.xml",
+        "android.hardware.power.stats.xml",
     ],
     overrides: [
         // Shared lib installed by default but unused by the AIDL implementation.
diff --git a/power/aidl/default/apex/com.android.hardware.power.avbpubkey b/power/aidl/default/apex/com.android.hardware.power.avbpubkey
deleted file mode 100644
index 3b6411d..0000000
--- a/power/aidl/default/apex/com.android.hardware.power.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/power/aidl/default/apex/com.android.hardware.power.pem b/power/aidl/default/apex/com.android.hardware.power.pem
deleted file mode 100644
index d18ae98..0000000
--- a/power/aidl/default/apex/com.android.hardware.power.pem
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJKQIBAAKCAgEAsUdFjtLq05tWKdQd4aj8V7tmV4KXw41pKGT5Q1CPzrdHF3yJ
-B/8VWdMpjZ+eQO1q8SijPgfvWExeWQVMfxKmwTmj26xWXhIOgo5G02Zva7zOptig
-KGnl/RdFlOiIGC36XeWDhzdIOhlGv+er9Sykf6Ot84OvktTBUTZNJrXQsyYTBRUX
-6B+wloPdBVxVf1HgzjeHUyCy5dTz0xZSWWELoW24tHIvV5FtJVKSY8ZDfuXWLLfT
-he7E93TepjT027U/J/iW4ITJzw4Qq87ick1D/jZDUbTrkqUMFEgPdgCouZ9zt5xG
-pcHAZ/Fcz9DZfEdX9Xy0R5/XbfrJdvDPguJlwK1pZnr/Pe13xxmE+TEohMmaQDqX
-jQNX3UlcfOYUAjnFMGucHDM10KjTMbP8ytCys88aNLiv7FOgVGrQ/djZN8rkMyVP
-ccoksUBMQmjYaQQZ2yZuJMiLD3P6aYkgU5tMEMoMTrBzfcx05GfElal+ZqOFKAzj
-eUxoZTR27wJb684FRbeE45D+y4jpFfrTEXry+aI7GrfDsVDnUqmyObCUttRtaT04
-6kuUmC44wFEg1EBfcWZc1szI192GBjMuZjFcYvJ4vMdCuennqvLNPBDY1PtqzCOf
-D8vpOE3T9rjS23xxFmmSmorwKKQOGChKqO/SaY0axkXgt+FbSsvTBQtZTSsCAwEA
-AQKCAgEArEu3t+MYJcdwS8EDtcF2V5IkGmMrOvziOkdA14Kh8efBKXt49xOc3atU
-VHfQ6AuXh4DWf0BZB7lZbS2wNkSbW2q47ZSmcFEeVxcOkQGawtxDAHfD2ONrirqm
-ft4s/0sYbU/WsIEzKnxMfdEdGHFmA0PCmczfxFYQ+OxMuZW1m5ginirtDEZYa0EH
-e+FMmyypz+K6HDnIhYWd4Aduy718/0zTWlUr2/DUYpTJAD2+dcPNj7Kt2xq/xj2I
-84K+hBa4phF+GgIU3a8u1ryA61RbA+QbM3siBWlxvvh2RlrHoXjuj4JMS2dup9c2
-PCggaCAyxb2IvaAFUbePPJE5LVz6RFT4HnLEydd5Yt+CEAm+iZKfCzyUgFRtb5y0
-HHTME1eVAt/rf/yIXUYA7q8nQ/PtSzIol5KLX4FUjX1MVKNmIWMa+836kxbuYDFB
-K1M1IKc1k0t9Q9F3TRCMhP/6qH/vJfubCGQhSRUMq7JyjivK9GjYST8R07Dpgu9Z
-4i9TRI8d+UVERsg8niCXONVkmNa3U49u2duUvqV3KmKgQ/Hgyy3keDjz6x56ie5w
-e0EusHAsot60W1BvHrdwlmGZjW3JmZEyazUPh9nBUAaQve1rIOpn80kGXx4EAE2o
-HcrcInJx/zVBk1Wk3UQDwmhUNpa64q9+nd9VMaR9SQNK3ah4NDECggEBAOeput2F
-CgRrvzka69i7FbgY4VmpNMIICPIB6gxvwpir/g4/GgYknuBB6ep1ksf/IZfsMp5A
-JTH1KdXqqQm8nV9v+ETYQAO+VnmWKSBKHsNJqONxsKkQ+xIJcusmKBTYLfL88XQg
-YWH3VMXgqPP8DnJYCeVRIKj1WqfEFFHiaLJJB8FgKhtZBwBnibkVG1K0XCkTdUfY
-mME2GRKW/C7DMvuFOpcFVj7Obwn68R2k3zsOhWA5NQGZF5mqhg5KYLVDg3IbMJQQ
-D+DymQxnc2s2ar0q24isy1Y/FOXrA057j1vAN951+pk6F/PCJM/mtAiRjhP0Aru1
-P6bbR11p+wnpU7MCggEBAMPm8Jmwu3F0xsyFC+1sWPAzPiwaMa7/30wANNKKqHVO
-7lUv1WYFbFMyAOzYPp3Y5HxdxNa43reULGk0R20kSu6W6FkApSvAws0rLKRlS5UI
-oZqhLGHUH2M7q07m2RgQY2TJkU2Zq6AH1kjcbSr127ISXKanKpqonwSHy38BTcGt
-Dl2fVioPzK/vwmiNo2njhh95TV4kqlbUfl7xtDt56tbg8oFBwOsK7UGajXYOxTGB
-o1DtO5E+oiOmlclXuo3m4qpSSMv+wM91aRFhHZVIx0vmO8y5lrfU2kM/5DDhJBxV
-FM4TaA+c5tFOTuCLejHc7nM99wVx7O4QZ0wBwETUxKkCggEAH0tBT+1J1iEL+tXV
-KDjVjUHnJyqBUvis5Kw3hqiOO/t33UrO5CeMQrUEuURaqKOhURl6GQCHRcFdfmUt
-ooAVLjA89GfV9et/WPtc4NzCXRUVOGxCNgRyNhSKrpM/9NjjFCDxKQO6w/YaQITB
-rfvNo8qaw5x68ff64BDPweP4yqSs5IVuCrWzCW3zH8pnH3v3uyDCxgrPT8JUDrvQ
-oyyBNZLgwEfbR66xN0Lr0VpVQXALulzf+TBKDNsJMuL/P104Y3Ci1k15J6T94bwT
-zlbSgm1IrKTS7vqkgw6FKtPsILPNmEKNsKc1VxtRx7fdeA7Zh3595Adu6sZSVJ8d
-Z1BamwKCAQAnbu0vgqu4gtEhigaEnDKq5yW0qvElUMwZ+FCpsM+IDYNcEmzaRG0x
-sfcNtdmk3GvhvN5KepwaR/FInAVkqtGKhUXv5Hla/Uo5El/CF8HHFh2xio/sgU5w
-IyqwjzdT6LiZKRnejPhHFkzEDdrLswGuLpQH185zo02fE9aakiCcw8EIh3JItTV2
-lMSFVz11qx7sZvZz5N2E7PEjG3Q0JK5o4o7uBdZXebOYaQvgn8iB1p6RQ6+h5QGu
-O3IbPVWICtnFfxq4NWeKWw/zN6FE04mKdaXD5/e2uVnV/55nWGp0aYvuj2l6+xJb
-P3ARMwI910MIX4jBx9TxdsvUOOYC9PFBAoIBAQDWswLnaNth4pgutngVWbMenSpv
-eK1RA1ldw2NoTZrGlqPB+LvjEMSH/7ioby8YtOyJRIWs3si8HpVF12qneu8qi7b7
-QlUtqyJOTnGalvhrlq5zPhdW+kk2DXvtTylUnz3vSxxi2I7cLhQRryLC/1kAwy67
-wEr0+u59bOvaqe8L1zgtYJpLQZeskUMzdSMIRVDdFShEFrMJU7adUvGpA7OZ6Ogf
-ux2jWr2vv/eKq6fU6kDPi/66MQjPbZPf2Uq6+XedkNkAeELpN4o3hw0/l/rfiK/r
-YUMJBwtjQw/hehtvC4GlgsH1tMZWzCZULo0tcW4qbzyi9PBrWFPteb33OjBc
------END RSA PRIVATE KEY-----
diff --git a/power/aidl/default/apex/com.android.hardware.power.pk8 b/power/aidl/default/apex/com.android.hardware.power.pk8
deleted file mode 100644
index e45435d..0000000
--- a/power/aidl/default/apex/com.android.hardware.power.pk8
+++ /dev/null
Binary files differ
diff --git a/power/aidl/default/apex/com.android.hardware.power.x509.pem b/power/aidl/default/apex/com.android.hardware.power.x509.pem
deleted file mode 100644
index 9f0c5f0..0000000
--- a/power/aidl/default/apex/com.android.hardware.power.x509.pem
+++ /dev/null
@@ -1,34 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIF2TCCA8ECFDFsXbm5CdS/UtQZgTiF8Umr8LrLMA0GCSqGSIb3DQEBCwUAMIGn
-MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
-bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEi
-MCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTEjMCEGA1UEAwwaY29t
-LmFuZHJvaWQuaGFyZHdhcmUucG93ZXIwIBcNMjExMDIwMTcwNTA0WhgPNDc1OTA5
-MTYxNzA1MDRaMIGnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEW
-MBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UE
-CwwHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTEj
-MCEGA1UEAwwaY29tLmFuZHJvaWQuaGFyZHdhcmUucG93ZXIwggIiMA0GCSqGSIb3
-DQEBAQUAA4ICDwAwggIKAoICAQC/37fhOkOfgM2e+M7bMJ1baLFif8dKGwRa6mkJ
-9HWmuOgRcTKllzuEHtrJ0jzur3cDy6/0oZSfA/E1ck3DdRHMQadW26JSNSg6fCU9
-h1kDzkakZgyr3DsJnKGoSHCJ2V2kVbCnd6GuOaOU1ZZISw1I+BWJDc3t1mZPs80D
-ar7/hoIZnsWRoE/eWgJDcWWscRsquSi+q6hyqlCbRvwRznPaDGwmb4geHNugCXkz
-EtCswfc5jiT8DjMDkgVsGO/WcYj2GWT0K0H+Zf1CmEO9fAoXTLfVBjdumtGILgii
-d/TJe2tOBSWyZz6sVzfac2PvUH5Lm8TNUXuLV5IEdcpySge0vqYQwAyd2EgsTH1e
-mRNSk9NerpmfCFEySRRP3BWMGRhbST1d8M3v9Bq0QFhrxoAF12r6GXBUpp9XcOL5
-pBTcAkA9XI++mfz4pDzyGRGOy4WX+8XtsaVZ/14JklupSLr0Tt7oaNocUhoXB03g
-4B0jUTX0hNnVzCxzJypw6YJ60Zc8z+z8pEF34FWarHec1QbkFuyWxbaTPQ4d2NLH
-8zDxQpMILErWdAgKsRL0d8RFG5fBcleEoBM2kKHMAgnP+1qyDqBgt8zloWbmmblw
-JXMuoePFOgeVcgPrZ3EGJSx+s4+dQGQc6r/GwKLKSWpUvHxTIGug76IX9xmptB+I
-F3xb2QIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQANISf3Vi2eueOlzzfnEGGa+CXz
-nvlgUXKv8Gv0/Pbg5uC1BaHTAUgRu5rvrfP9p3Mdj86I/HbE/F4Vkuzqb8/JTWGA
-mG636zAsJRJr0fnkbPma9wVEPSK8MF1QqM6PmKXboixX82TqV1R1sRYG+9hh9W3u
-isDzYDb2ODE0X9M8/3hLS28zdCdtl4zCRK6KB86aGxvkVEj4qDA5l+AbVYevS/SU
-hz1+K/aM0Fi6MZovo5kd/Mof5l05e1TEgCoL1FtFX79r+PYGHJ8/LjtEMkFgwqvG
-CLx2sOV09EHZU27EbVvSs1JYMMXgeAvKaHsVZ51QlSzW4esg/E6z4pw654p8qyK/
-WLXIZ7BMILl1sHYmGqXitnu19puvNks2/+hyqVr0seM5GyQDuwBE8nx6xZzTRxdj
-4TZyN9LuMc9/cKJFvOPqD152bkA2frCLEzYCQreDWwxsWcUHzYrQT+v2SqzP6Ue2
-Xn06HDLx9wBL7Dz6no05SlNS0u1KdvKas6FKZHO+QaKKsBlDmXbMrBTcuUI6OXv2
-6NpVbeyDd0+A23hDiNSgI6zTY6jMidesNExB7rW/bCE4ltPyxFAB+sffyXounODc
-groB5CaS2bv+H1IXJzMMe4LkgQPl1C7G+I3KvJmnrYwmIhLIDuxP82arClIDzccS
-ExRR7ugEg91XCc87Zg==
------END CERTIFICATE-----
diff --git a/power/aidl/default/apex/file_contexts b/power/aidl/default/apex/file_contexts
index 3433851..3b7526a 100644
--- a/power/aidl/default/apex/file_contexts
+++ b/power/aidl/default/apex/file_contexts
@@ -1,3 +1,4 @@
 (/.*)?                                                      u:object_r:vendor_file:s0
+/etc(/.*)?                                                  u:object_r:vendor_configs_file:s0
 /bin/hw/android\.hardware\.power-service\.example           u:object_r:hal_power_default_exec:s0
 /bin/hw/android\.hardware\.power\.stats-service\.example    u:object_r:hal_power_stats_default_exec:s0
diff --git a/power/stats/aidl/default/Android.bp b/power/stats/aidl/default/Android.bp
index d3ab29b..cc0fbf6 100644
--- a/power/stats/aidl/default/Android.bp
+++ b/power/stats/aidl/default/Android.bp
@@ -38,9 +38,11 @@
     ],
 }
 
-filegroup {
+prebuilt_etc {
     name: "android.hardware.power.stats.xml",
-    srcs: ["power.stats-default.xml"],
+    src: "power.stats-default.xml",
+    sub_dir: "vintf",
+    installable: false,
 }
 
 filegroup {
diff --git a/radio/aidl/Android.bp b/radio/aidl/Android.bp
index 72aaa0f..1971832 100644
--- a/radio/aidl/Android.bp
+++ b/radio/aidl/Android.bp
@@ -318,19 +318,3 @@
         },
     ],
 }
-
-aidl_interface {
-    name: "android.hardware.radio.satellite",
-    vendor_available: true,
-    srcs: ["android/hardware/radio/satellite/*.aidl"],
-    stability: "vintf",
-    imports: ["android.hardware.radio-V3"],
-    backend: {
-        cpp: {
-            enabled: false,
-        },
-        java: {
-            sdk_version: "module_current",
-        },
-    },
-}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.data/current/android/hardware/radio/data/ApnTypes.aidl b/radio/aidl/aidl_api/android.hardware.radio.data/current/android/hardware/radio/data/ApnTypes.aidl
index d2d3137..782dbbf 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.data/current/android/hardware/radio/data/ApnTypes.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.data/current/android/hardware/radio/data/ApnTypes.aidl
@@ -51,4 +51,5 @@
   VSIM = (1 << 12) /* 4096 */,
   BIP = (1 << 13) /* 8192 */,
   ENTERPRISE = (1 << 14) /* 16384 */,
+  RCS = (1 << 15) /* 32768 */,
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.data/current/android/hardware/radio/data/DataProfileInfo.aidl b/radio/aidl/aidl_api/android.hardware.radio.data/current/android/hardware/radio/data/DataProfileInfo.aidl
index bd44275..7f3fdc7 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.data/current/android/hardware/radio/data/DataProfileInfo.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.data/current/android/hardware/radio/data/DataProfileInfo.aidl
@@ -55,6 +55,7 @@
   boolean persistent;
   boolean alwaysOn;
   android.hardware.radio.data.TrafficDescriptor trafficDescriptor;
+  int infrastructureBitmap = INFRASTRUCTURE_UNKNOWN /* 0 */;
   const int ID_DEFAULT = 0;
   const int ID_TETHERED = 1;
   const int ID_IMS = 2;
@@ -65,4 +66,7 @@
   const int TYPE_COMMON = 0;
   const int TYPE_3GPP = 1;
   const int TYPE_3GPP2 = 2;
+  const int INFRASTRUCTURE_UNKNOWN = 0;
+  const int INFRASTRUCTURE_CELLULAR = (1 << 0) /* 1 */;
+  const int INFRASTRUCTURE_SATELLITE = (1 << 1) /* 2 */;
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/IImsMediaSession.aidl b/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/IImsMediaSession.aidl
index 45c8002..2150fbe 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/IImsMediaSession.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/IImsMediaSession.aidl
@@ -42,4 +42,6 @@
   oneway void stopDtmf();
   oneway void sendHeaderExtension(in List<android.hardware.radio.ims.media.RtpHeaderExtension> extensions);
   oneway void setMediaQualityThreshold(in android.hardware.radio.ims.media.MediaQualityThreshold threshold);
+  oneway void requestRtpReceptionStats(in int intervalMs);
+  oneway void adjustDelay(in int delayMs);
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/IImsMediaSessionListener.aidl b/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/IImsMediaSessionListener.aidl
index 5ec9c18..87474ef 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/IImsMediaSessionListener.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/IImsMediaSessionListener.aidl
@@ -42,4 +42,5 @@
   oneway void triggerAnbrQuery(in android.hardware.radio.ims.media.RtpConfig config);
   oneway void onDtmfReceived(char dtmfDigit, int durationMs);
   oneway void onCallQualityChanged(in android.hardware.radio.ims.media.CallQuality callQuality);
+  oneway void notifyRtpReceptionStats(in android.hardware.radio.ims.media.RtpReceptionStats stats);
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/RtpReceptionStats.aidl
similarity index 85%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
copy to radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/RtpReceptionStats.aidl
index b9df1f5..82b798b 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.ims.media/current/android/hardware/radio/ims/media/RtpReceptionStats.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,12 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
-/* @hide */
-@Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+package android.hardware.radio.ims.media;
+@VintfStability
+parcelable RtpReceptionStats {
+  int rtpTimestamp;
+  int rtpSequenceNumber;
+  int timeDurationMs;
+  int jitterBufferMs;
+  int roundTripTimeMs;
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.ims/current/android/hardware/radio/ims/SuggestedAction.aidl b/radio/aidl/aidl_api/android.hardware.radio.ims/current/android/hardware/radio/ims/SuggestedAction.aidl
index 120e7e8..6dbf09d 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.ims/current/android/hardware/radio/ims/SuggestedAction.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.ims/current/android/hardware/radio/ims/SuggestedAction.aidl
@@ -38,4 +38,6 @@
   NONE,
   TRIGGER_PLMN_BLOCK,
   TRIGGER_PLMN_BLOCK_WITH_TIMEOUT,
+  TRIGGER_RAT_BLOCK,
+  TRIGGER_CLEAR_RAT_BLOCK,
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.modem/current/android/hardware/radio/modem/IRadioModemIndication.aidl b/radio/aidl/aidl_api/android.hardware.radio.modem/current/android/hardware/radio/modem/IRadioModemIndication.aidl
index 20066f8..3c06877 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.modem/current/android/hardware/radio/modem/IRadioModemIndication.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.modem/current/android/hardware/radio/modem/IRadioModemIndication.aidl
@@ -40,4 +40,5 @@
   oneway void radioCapabilityIndication(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.modem.RadioCapability rc);
   oneway void radioStateChanged(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.modem.RadioState radioState);
   oneway void rilConnected(in android.hardware.radio.RadioIndicationType type);
+  oneway void onImeiMappingChanged(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.modem.ImeiInfo imeiInfo);
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/CellularIdentifier.aidl
similarity index 90%
rename from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
rename to radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/CellularIdentifier.aidl
index b9df1f5..d38494f 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/CellularIdentifier.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,12 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
+package android.hardware.radio.network;
 /* @hide */
 @Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+enum CellularIdentifier {
+  UNKNOWN = 0,
+  IMSI = 1,
+  IMEI = 2,
+  SUCI = 3,
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/PointingInfo.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/CellularIdentifierDisclosure.aidl
similarity index 84%
rename from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/PointingInfo.aidl
rename to radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/CellularIdentifierDisclosure.aidl
index 1ada88a..cb542e8 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/PointingInfo.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/CellularIdentifierDisclosure.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,13 +31,12 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
+package android.hardware.radio.network;
 /* @hide */
 @JavaDerive(toString=true) @VintfStability
-parcelable PointingInfo {
-  float satelliteAzimuthDegrees;
-  float satelliteElevationDegrees;
-  float antennaAzimuthDegrees;
-  float antennaPitchDegrees;
-  float antennaRollDegrees;
+parcelable CellularIdentifierDisclosure {
+  String plmn;
+  android.hardware.radio.network.CellularIdentifier identifier;
+  android.hardware.radio.network.NasProtocolMessage protocolMessage;
+  boolean isEmergency;
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/ConnectionEvent.aidl
similarity index 80%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
copy to radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/ConnectionEvent.aidl
index b9df1f5..3161322 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/ConnectionEvent.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,22 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
+package android.hardware.radio.network;
 /* @hide */
 @Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+enum ConnectionEvent {
+  CS_SERVICE_GSM = 0,
+  SIGNALLING_GSM = 1,
+  PS_SERVICE_GPRS = 2,
+  SIGNALLING_GPRS = 3,
+  PS_SERVICE_3G = 4,
+  SIGNALLING_3G = 5,
+  NAS_SIGNALLING_LTE = 6,
+  AS_SIGNALLING_LTE = 7,
+  VOLTE_SIP = 8,
+  VOLTE_RTP = 9,
+  NAS_SIGNALLING_5G = 10,
+  AS_SIGNALLING_5G = 11,
+  VONR_SIP = 12,
+  VONR_RTP = 13,
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetwork.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetwork.aidl
index cbc1e30..8af617f 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetwork.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetwork.aidl
@@ -82,4 +82,8 @@
   oneway void isNullCipherAndIntegrityEnabled(in int serial);
   oneway void isN1ModeEnabled(in int serial);
   oneway void setN1ModeEnabled(in int serial, boolean enable);
+  oneway void isCellularIdentifierTransparencyEnabled(in int serial);
+  oneway void setCellularIdentifierTransparencyEnabled(in int serial, in boolean enabled);
+  oneway void setSecurityAlgorithmsUpdatedEnabled(in int serial, boolean enable);
+  oneway void isSecurityAlgorithmsUpdatedEnabled(in int serial);
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl
index 05a36ef..8eea14f 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl
@@ -50,4 +50,6 @@
   oneway void suppSvcNotify(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.network.SuppSvcNotification suppSvc);
   oneway void voiceRadioTechChanged(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.RadioTechnology rat);
   oneway void emergencyNetworkScanResult(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.network.EmergencyRegResult result);
+  oneway void cellularIdentifierDisclosed(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.network.CellularIdentifierDisclosure disclosure);
+  oneway void securityAlgorithmsUpdated(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.network.SecurityAlgorithmUpdate securityAlgorithmUpdate);
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkResponse.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkResponse.aidl
index c86bebc..e7f2918 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkResponse.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkResponse.aidl
@@ -81,4 +81,8 @@
   oneway void isNullCipherAndIntegrityEnabledResponse(in android.hardware.radio.RadioResponseInfo info, in boolean isEnabled);
   oneway void isN1ModeEnabledResponse(in android.hardware.radio.RadioResponseInfo info, boolean isEnabled);
   oneway void setN1ModeEnabledResponse(in android.hardware.radio.RadioResponseInfo info);
+  oneway void isCellularIdentifierTransparencyEnabledResponse(in android.hardware.radio.RadioResponseInfo info, boolean isEnabled);
+  oneway void setCellularIdentifierTransparencyEnabledResponse(in android.hardware.radio.RadioResponseInfo info);
+  oneway void setSecurityAlgorithmsUpdatedEnabledResponse(in android.hardware.radio.RadioResponseInfo info);
+  oneway void isSecurityAlgorithmsUpdatedEnabledResponse(in android.hardware.radio.RadioResponseInfo info, in boolean isEnabled);
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/NasProtocolMessage.aidl
similarity index 81%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
copy to radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/NasProtocolMessage.aidl
index b9df1f5..9f852cc 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/NasProtocolMessage.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,17 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
+package android.hardware.radio.network;
 /* @hide */
 @Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+enum NasProtocolMessage {
+  UNKNOWN = 0,
+  ATTACH_REQUEST = 1,
+  IDENTITY_RESPONSE = 2,
+  DETACH_REQUEST = 3,
+  TRACKING_AREA_UPDATE_REQUEST = 4,
+  LOCATION_UPDATE_REQUEST = 5,
+  AUTHENTICATION_AND_CIPHERING_RESPONSE = 6,
+  REGISTRATION_REQUEST = 7,
+  DEREGISTRATION_REQUEST = 8,
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/SecurityAlgorithm.aidl
similarity index 67%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
copy to radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/SecurityAlgorithm.aidl
index b9df1f5..97ce004 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/SecurityAlgorithm.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,50 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
+package android.hardware.radio.network;
 /* @hide */
 @Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+enum SecurityAlgorithm {
+  A50 = 0,
+  A51 = 1,
+  A52 = 2,
+  A53 = 3,
+  A54 = 4,
+  GEA0 = 14,
+  GEA1 = 15,
+  GEA2 = 16,
+  GEA3 = 17,
+  GEA4 = 18,
+  GEA5 = 19,
+  UEA0 = 29,
+  UEA1 = 30,
+  UEA2 = 31,
+  EEA0 = 41,
+  EEA1 = 42,
+  EEA2 = 43,
+  EEA3 = 44,
+  EEA4_ZUC = 45,
+  NEA0 = 55,
+  NEA1 = 56,
+  NEA2 = 57,
+  NEA3 = 58,
+  SIP_NULL = 68,
+  AES_GCM = 69,
+  AES_GMAC = 70,
+  AES_CBC = 71,
+  DES_EDE3_CBC = 72,
+  AES_EDE3_CBC = 73,
+  HMAC_SHA1_96 = 74,
+  HMAC_SHA1_96_null = 75,
+  HMAC_MD5_96 = 76,
+  HMAC_MD5_96_null = 77,
+  SRTP_AES_COUNTER = 87,
+  SRTP_AES_F8 = 88,
+  SRTP_HMAC_SHA1 = 89,
+  ENCR_AES_GCM_16 = 99,
+  ENCR_AES_CBC = 100,
+  AUTH_HMAC_SHA2_256_128 = 101,
+  UNKNOWN = 113,
+  OTHER = 114,
+  ORYX = 124,
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/PointingInfo.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/SecurityAlgorithmUpdate.aidl
similarity index 82%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/PointingInfo.aidl
copy to radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/SecurityAlgorithmUpdate.aidl
index 1ada88a..73ad180 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/PointingInfo.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/SecurityAlgorithmUpdate.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,13 +31,12 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
+package android.hardware.radio.network;
 /* @hide */
 @JavaDerive(toString=true) @VintfStability
-parcelable PointingInfo {
-  float satelliteAzimuthDegrees;
-  float satelliteElevationDegrees;
-  float antennaAzimuthDegrees;
-  float antennaPitchDegrees;
-  float antennaRollDegrees;
+parcelable SecurityAlgorithmUpdate {
+  android.hardware.radio.network.ConnectionEvent connectionEvent;
+  android.hardware.radio.network.SecurityAlgorithm encryption;
+  android.hardware.radio.network.SecurityAlgorithm integrity;
+  boolean isUnprotectedEmergency;
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IRadioSatellite.aidl b/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IRadioSatellite.aidl
deleted file mode 100644
index 0c7408a..0000000
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IRadioSatellite.aidl
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL file. Do not edit it manually. There are
-// two cases:
-// 1). this is a frozen version file - do not edit this in any case.
-// 2). this is a 'current' file. If you make a backwards compatible change to
-//     the interface (from the latest frozen version), the build system will
-//     prompt you to update this file with `m <name>-update-api`.
-//
-// You must not make a backward incompatible change to any AIDL file built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.hardware.radio.satellite;
-/* @hide */
-@VintfStability
-interface IRadioSatellite {
-  oneway void addAllowedSatelliteContacts(in int serial, in String[] contacts);
-  oneway void getCapabilities(in int serial);
-  oneway void getMaxCharactersPerTextMessage(in int serial);
-  oneway void getPendingMessages(in int serial);
-  oneway void getPowerState(in int serial);
-  oneway void getSatelliteMode(in int serial);
-  oneway void getTimeForNextSatelliteVisibility(in int serial);
-  oneway void provisionService(in int serial, in String imei, in String msisdn, in String imsi, in android.hardware.radio.satellite.SatelliteFeature[] features);
-  oneway void removeAllowedSatelliteContacts(in int serial, in String[] contacts);
-  oneway void responseAcknowledgement();
-  oneway void sendMessages(in int serial, in String[] messages, in String destination, in double latitude, in double longitude);
-  oneway void setIndicationFilter(in int serial, in int filterBitmask);
-  oneway void setPower(in int serial, in boolean on);
-  oneway void setResponseFunctions(in android.hardware.radio.satellite.IRadioSatelliteResponse satelliteResponse, in android.hardware.radio.satellite.IRadioSatelliteIndication satelliteIndication);
-  oneway void startSendingSatellitePointingInfo(in int serial);
-  oneway void stopSendingSatellitePointingInfo(in int serial);
-}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IRadioSatelliteIndication.aidl b/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IRadioSatelliteIndication.aidl
deleted file mode 100644
index 36ec5f3..0000000
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IRadioSatelliteIndication.aidl
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL file. Do not edit it manually. There are
-// two cases:
-// 1). this is a frozen version file - do not edit this in any case.
-// 2). this is a 'current' file. If you make a backwards compatible change to
-//     the interface (from the latest frozen version), the build system will
-//     prompt you to update this file with `m <name>-update-api`.
-//
-// You must not make a backward incompatible change to any AIDL file built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.hardware.radio.satellite;
-/* @hide */
-@VintfStability
-interface IRadioSatelliteIndication {
-  oneway void onMessagesTransferComplete(in android.hardware.radio.RadioIndicationType type, in boolean complete);
-  oneway void onNewMessages(in android.hardware.radio.RadioIndicationType type, in String[] messages);
-  oneway void onPendingMessageCount(in android.hardware.radio.RadioIndicationType type, in int count);
-  oneway void onProvisionStateChanged(in android.hardware.radio.RadioIndicationType type, boolean provisioned, in android.hardware.radio.satellite.SatelliteFeature[] features);
-  oneway void onSatelliteModeChanged(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.satellite.SatelliteMode mode);
-  oneway void onSatellitePointingInfoChanged(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.satellite.PointingInfo pointingInfo);
-  oneway void onSatelliteRadioTechnologyChanged(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.satellite.NTRadioTechnology technology);
-}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IRadioSatelliteResponse.aidl b/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IRadioSatelliteResponse.aidl
deleted file mode 100644
index bf9b9e6..0000000
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IRadioSatelliteResponse.aidl
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL file. Do not edit it manually. There are
-// two cases:
-// 1). this is a frozen version file - do not edit this in any case.
-// 2). this is a 'current' file. If you make a backwards compatible change to
-//     the interface (from the latest frozen version), the build system will
-//     prompt you to update this file with `m <name>-update-api`.
-//
-// You must not make a backward incompatible change to any AIDL file built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.hardware.radio.satellite;
-/* @hide */
-@VintfStability
-interface IRadioSatelliteResponse {
-  oneway void acknowledgeRequest(in int serial);
-  oneway void addAllowedSatelliteContactsResponse(in android.hardware.radio.RadioResponseInfo info);
-  oneway void getCapabilitiesResponse(in android.hardware.radio.RadioResponseInfo info, in android.hardware.radio.satellite.SatelliteCapabilities capabilities);
-  oneway void getMaxCharactersPerTextMessageResponse(in android.hardware.radio.RadioResponseInfo info, in int charLimit);
-  oneway void getPendingMessagesResponse(in android.hardware.radio.RadioResponseInfo info, in String[] messages);
-  oneway void getPowerStateResponse(in android.hardware.radio.RadioResponseInfo info, in boolean on);
-  oneway void getSatelliteModeResponse(in android.hardware.radio.RadioResponseInfo info, in android.hardware.radio.satellite.SatelliteMode mode, in android.hardware.radio.satellite.NTRadioTechnology technology);
-  oneway void getTimeForNextSatelliteVisibilityResponse(in android.hardware.radio.RadioResponseInfo info, in int timeInSeconds);
-  oneway void provisionServiceResponse(in android.hardware.radio.RadioResponseInfo info, in boolean provisioned);
-  oneway void removeAllowedSatelliteContactsResponse(in android.hardware.radio.RadioResponseInfo info);
-  oneway void sendMessagesResponse(in android.hardware.radio.RadioResponseInfo info);
-  oneway void setIndicationFilterResponse(in android.hardware.radio.RadioResponseInfo info);
-  oneway void setPowerResponse(in android.hardware.radio.RadioResponseInfo info);
-  oneway void startSendingSatellitePointingInfoResponse(in android.hardware.radio.RadioResponseInfo info);
-  oneway void stopSendingSatellitePointingInfoResponse(in android.hardware.radio.RadioResponseInfo info);
-}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/NTRadioTechnology.aidl b/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/NTRadioTechnology.aidl
deleted file mode 100644
index f05baaa..0000000
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/NTRadioTechnology.aidl
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL file. Do not edit it manually. There are
-// two cases:
-// 1). this is a frozen version file - do not edit this in any case.
-// 2). this is a 'current' file. If you make a backwards compatible change to
-//     the interface (from the latest frozen version), the build system will
-//     prompt you to update this file with `m <name>-update-api`.
-//
-// You must not make a backward incompatible change to any AIDL file built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.hardware.radio.satellite;
-/* @hide */
-@Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum NTRadioTechnology {
-  NB_IOT_NTN = 0,
-  NR_NTN = 1,
-  EMTC_NTN = 2,
-  PROPRIETARY = 3,
-}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/SatelliteFeature.aidl b/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/SatelliteFeature.aidl
deleted file mode 100644
index e79e572..0000000
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/SatelliteFeature.aidl
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL file. Do not edit it manually. There are
-// two cases:
-// 1). this is a frozen version file - do not edit this in any case.
-// 2). this is a 'current' file. If you make a backwards compatible change to
-//     the interface (from the latest frozen version), the build system will
-//     prompt you to update this file with `m <name>-update-api`.
-//
-// You must not make a backward incompatible change to any AIDL file built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.hardware.radio.satellite;
-/* @hide */
-@Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum SatelliteFeature {
-  SOS_SMS = 0,
-  EMERGENCY_SMS = 1,
-  SMS = 2,
-  LOCATION_SHARING = 3,
-}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/SatelliteMode.aidl b/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/SatelliteMode.aidl
deleted file mode 100644
index a1ce566..0000000
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/SatelliteMode.aidl
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL file. Do not edit it manually. There are
-// two cases:
-// 1). this is a frozen version file - do not edit this in any case.
-// 2). this is a 'current' file. If you make a backwards compatible change to
-//     the interface (from the latest frozen version), the build system will
-//     prompt you to update this file with `m <name>-update-api`.
-//
-// You must not make a backward incompatible change to any AIDL file built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.hardware.radio.satellite;
-/* @hide */
-@Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum SatelliteMode {
-  POWERED_OFF = 0,
-  OUT_OF_SERVICE_NOT_SEARCHING = 1,
-  OUT_OF_SERVICE_SEARCHING = 2,
-  ACQUIRED = 3,
-  MESSAGE_TRANSFERRING = 4,
-}
diff --git a/radio/aidl/aidl_api/android.hardware.radio/current/android/hardware/radio/RadioError.aidl b/radio/aidl/aidl_api/android.hardware.radio/current/android/hardware/radio/RadioError.aidl
index 781c9f6..02c5370 100644
--- a/radio/aidl/aidl_api/android.hardware.radio/current/android/hardware/radio/RadioError.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio/current/android/hardware/radio/RadioError.aidl
@@ -124,15 +124,4 @@
   BLOCKED_DUE_TO_CALL = 69,
   RF_HARDWARE_ISSUE = 70,
   NO_RF_CALIBRATION_INFO = 71,
-  ENCODING_NOT_SUPPORTED = 72,
-  FEATURE_NOT_SUPPORTED = 73,
-  INVALID_CONTACT = 74,
-  MODEM_INCOMPATIBLE = 75,
-  NETWORK_TIMEOUT = 76,
-  NO_SATELLITE_SIGNAL = 77,
-  NOT_SUFFICIENT_ACCOUNT_BALANCE = 78,
-  RADIO_TECHNOLOGY_NOT_SUPPORTED = 79,
-  SUBSCRIBER_NOT_AUTHORIZED = 80,
-  SWITCHED_FROM_SATELLITE_TO_TERRESTRIAL = 81,
-  UNIDENTIFIED_SUBSCRIBER = 82,
 }
diff --git a/radio/aidl/android/hardware/radio/RadioError.aidl b/radio/aidl/android/hardware/radio/RadioError.aidl
index e95d725..9c39bc4 100644
--- a/radio/aidl/android/hardware/radio/RadioError.aidl
+++ b/radio/aidl/android/hardware/radio/RadioError.aidl
@@ -302,51 +302,4 @@
      * Unlike RF_HARDWARE_ISSUE, this is a SW problem and no HW repair is needed.
      */
     NO_RF_CALIBRATION_INFO = 71,
-    /**
-     * The encoding scheme is not supported by either the network or the MS.
-     */
-    ENCODING_NOT_SUPPORTED = 72,
-    /**
-     * The requesting feature is not supported by the service provider/operator.
-     */
-    FEATURE_NOT_SUPPORTED = 73,
-    /**
-     * The contact to be added is either not existing or not valid.
-     */
-    INVALID_CONTACT = 74,
-    /**
-     * The modem of the MS is not compatible with the service provider/operator. This generic error
-     * should be used only when there are some mimatches in the capabilities between the MS and
-     * the operator and the error cannot be mapped properly to other specific network errors.
-     */
-    MODEM_INCOMPATIBLE = 75,
-    /**
-     * Modem timeout to receive ACK or response from network after sending a request to the network.
-     */
-    NETWORK_TIMEOUT = 76,
-    /**
-     * Modem fails to communicate with the satellite network since there is no satellite signal.
-     */
-    NO_SATELLITE_SIGNAL = 77,
-    /**
-     * The request cannot be performed since the subscriber's account balance is not sufficient.
-     */
-    NOT_SUFFICIENT_ACCOUNT_BALANCE = 78,
-    /**
-     * The radio technology is not supported by the service provider/operator.
-     */
-    RADIO_TECHNOLOGY_NOT_SUPPORTED = 79,
-    /**
-     * The subscription is not authorized to register with the service provider/operator.
-     */
-    SUBSCRIBER_NOT_AUTHORIZED = 80,
-    /**
-     * While processing a request from the Framework, the satellite modem detects terrestrial
-     * signal, aborts the request, and switches to the terrestrial network.
-     */
-    SWITCHED_FROM_SATELLITE_TO_TERRESTRIAL = 81,
-    /**
-     * The subscriber is not registered in the service provider.
-     */
-    UNIDENTIFIED_SUBSCRIBER = 82
 }
diff --git a/radio/aidl/android/hardware/radio/config/IRadioConfig.aidl b/radio/aidl/android/hardware/radio/config/IRadioConfig.aidl
index 1075ef7..9058d9d 100644
--- a/radio/aidl/android/hardware/radio/config/IRadioConfig.aidl
+++ b/radio/aidl/android/hardware/radio/config/IRadioConfig.aidl
@@ -40,6 +40,8 @@
      *
      * Response callback is
      * IRadioConfigResponse.getHalDeviceCapabilitiesResponse()
+     *
+     * This is available when android.hardware.telephony is defined.
      */
     void getHalDeviceCapabilities(in int serial);
 
@@ -54,6 +56,8 @@
      *
      * Response callback is IRadioConfigResponse.getNumOfLiveModemsResponse() which
      * will return <byte>.
+     *
+     * This is available when android.hardware.telephony is defined.
      */
     void getNumOfLiveModems(in int serial);
 
@@ -64,6 +68,8 @@
      *
      * Response callback is IRadioResponse.getPhoneCapabilityResponse() which
      * will return <PhoneCapability>.
+     *
+     * This is available when android.hardware.telephony is defined.
      */
     void getPhoneCapability(in int serial);
 
@@ -76,6 +82,8 @@
      * @param serial Serial number of request.
      *
      * Response callback is IRadioConfigResponse.getSimSlotsStatusResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void getSimSlotsStatus(in int serial);
 
@@ -93,6 +101,8 @@
      * @param modemsConfig byte object including the number of live modems
      *
      * Response callback is IRadioResponse.setNumOfLiveModemsResponse()
+     *
+     * This is available when android.hardware.telephony is defined.
      */
     void setNumOfLiveModems(in int serial, in byte numOfLiveModems);
 
@@ -107,6 +117,8 @@
      * from getPhoneCapability().
      *
      * Response callback is IRadioConfigResponse.setPreferredDataModemResponse()
+     *
+     * This is available when android.hardware.telephony.data is defined.
      */
     void setPreferredDataModem(in int serial, in byte modemId);
 
@@ -115,6 +127,8 @@
      *
      * @param radioConfigResponse Object containing radio config response functions
      * @param radioConfigIndication Object containing radio config indications
+     *
+     * This is available when android.hardware.telephony is defined.
      */
     void setResponseFunctions(in IRadioConfigResponse radioConfigResponse,
             in IRadioConfigIndication radioConfigIndication);
@@ -173,6 +187,8 @@
      *        getSimSlotsStatusResponse
      *
      * Response callback is IRadioConfigResponse.setSimSlotsMappingResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void setSimSlotsMapping(in int serial, in SlotPortMapping[] slotMap);
 }
diff --git a/radio/aidl/android/hardware/radio/config/IRadioConfigResponse.aidl b/radio/aidl/android/hardware/radio/config/IRadioConfigResponse.aidl
index cb3533e..df93e3c 100644
--- a/radio/aidl/android/hardware/radio/config/IRadioConfigResponse.aidl
+++ b/radio/aidl/android/hardware/radio/config/IRadioConfigResponse.aidl
@@ -40,6 +40,7 @@
      *          IRadioIndication.currentPhysicalChannelConfigs_1_6()
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -53,6 +54,7 @@
      *        are enabled and actively working as part of a working connectivity stack
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      */
@@ -65,6 +67,7 @@
      *        how many logical modems it has, how many data connections it supports.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -78,6 +81,8 @@
      *        equal to the number of physical slots on the device
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:NO_MEMORY
@@ -91,6 +96,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -101,6 +107,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.data is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -112,6 +119,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:NO_MEMORY
diff --git a/radio/aidl/android/hardware/radio/data/ApnTypes.aidl b/radio/aidl/android/hardware/radio/data/ApnTypes.aidl
index 366570a..f44c636 100644
--- a/radio/aidl/android/hardware/radio/data/ApnTypes.aidl
+++ b/radio/aidl/android/hardware/radio/data/ApnTypes.aidl
@@ -86,5 +86,9 @@
     /**
      * APN type for ENTERPRISE
      */
-    ENTERPRISE = 1 << 14
+    ENTERPRISE = 1 << 14,
+    /**
+     * APN type for RCS (Rich Communication Services)
+     */
+    RCS = 1 << 15
 }
diff --git a/radio/aidl/android/hardware/radio/data/DataProfileInfo.aidl b/radio/aidl/android/hardware/radio/data/DataProfileInfo.aidl
index 613387f..d01f8ff 100644
--- a/radio/aidl/android/hardware/radio/data/DataProfileInfo.aidl
+++ b/radio/aidl/android/hardware/radio/data/DataProfileInfo.aidl
@@ -40,6 +40,21 @@
     const int TYPE_3GPP2 = 2;
 
     /**
+     * Innfrastructure type unknown. This is only for initializing.
+     */
+    const int INFRASTRUCTURE_UNKNOWN = 0;
+
+    /**
+     * Indicating this APN can be used when the device is using terrestrial cellular networks.
+     */
+    const int INFRASTRUCTURE_CELLULAR = 1 << 0;
+
+    /**
+     * Indicating this APN can be used when the device is attached to satellite.
+     */
+    const int INFRASTRUCTURE_SATELLITE = 1 << 1;
+
+    /**
      * ID of the data profile.
      * Values are ID_
      */
@@ -131,4 +146,10 @@
      * apn; apn must be used as the end point if one is not specified through URSP rules.
      */
     TrafficDescriptor trafficDescriptor;
+    /**
+     * The infrastructure bitmap which the APN can be used on. For example, some APNs can only
+     * be used when the device is using cellular network, using satellite network, or can be used
+     * in either cases.
+     */
+    int infrastructureBitmap = INFRASTRUCTURE_UNKNOWN;
 }
diff --git a/radio/aidl/android/hardware/radio/data/IRadioData.aidl b/radio/aidl/android/hardware/radio/data/IRadioData.aidl
index 519d425..a73616a 100644
--- a/radio/aidl/android/hardware/radio/data/IRadioData.aidl
+++ b/radio/aidl/android/hardware/radio/data/IRadioData.aidl
@@ -48,6 +48,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioDataResponse.allocatePduSessionIdResponse()
+     *
+     * This is available when android.hardware.telephony.data is defined.
      */
     void allocatePduSessionId(in int serial);
 
@@ -60,6 +62,8 @@
      * @param id callId The identifier of the data call which is provided in SetupDataCallResult
      *
      * Response function is IRadioDataResponse.cancelHandoverResponse()
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void cancelHandover(in int serial, in int callId);
 
@@ -73,6 +77,8 @@
      * @param reason The request reason. Must be normal, handover, or shutdown.
      *
      * Response function is IRadioDataResponse.deactivateDataCallResponse()
+     *
+     * This is available when android.hardware.telephony.data is defined.
      */
     void deactivateDataCall(in int serial, in int cid, in DataRequestReason reason);
 
@@ -83,6 +89,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioDataResponse.getDataCallListResponse()
+     *
+     * This is available when android.hardware.telephony.data is defined.
      */
     void getDataCallList(in int serial);
 
@@ -96,6 +104,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioDataResponse.getSlicingConfigResponse()
+     *
+     * This is available when android.hardware.telephony.data is defined.
      */
     void getSlicingConfig(in int serial);
 
@@ -107,6 +117,8 @@
      * @param id Pdu session id to release.
      *
      * Response function is IRadioDataResponse.releasePduSessionIdResponse()
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void releasePduSessionId(in int serial, in int id);
 
@@ -114,6 +126,8 @@
      * When response type received from a radio indication or radio response is
      * RadioIndicationType:UNSOLICITED_ACK_EXP or RadioResponseType:SOLICITED_ACK_EXP respectively,
      * acknowledge the receipt of those messages by sending responseAcknowledgement().
+     *
+     * This is available when android.hardware.telephony.data is defined.
      */
     void responseAcknowledgement();
 
@@ -124,6 +138,8 @@
      * @param allow true to allow data calls, false to disallow data calls
      *
      * Response function is IRadioDataResponse.setDataAllowedResponse()
+     *
+     * This is available when android.hardware.telephony.data is defined.
      */
     void setDataAllowed(in int serial, in boolean allow);
 
@@ -134,6 +150,8 @@
      * @param profiles Array of DataProfileInfo to set.
      *
      * Response function is IRadioDataResponse.setDataProfileResponse()
+     *
+     * This is available when android.hardware.telephony.data is defined.
      */
     void setDataProfile(in int serial, in DataProfileInfo[] profiles);
 
@@ -155,6 +173,8 @@
      *        DataThrottlingAction:HOLD.
      *
      * Response function is IRadioDataResponse.setDataThrottlingResponse()
+     *
+     * This is available when android.hardware.telephony.data is defined.
      */
     void setDataThrottling(in int serial, in DataThrottlingAction dataThrottlingAction,
             in long completionDurationMillis);
@@ -167,6 +187,8 @@
      *        initial attach APN.
      *
      * Response function is IRadioDataResponse.setInitialAttachApnResponse()
+     *
+     * This is available when android.hardware.telephony.data is defined.
      */
     void setInitialAttachApn(in int serial, in @nullable DataProfileInfo dataProfileInfo);
 
@@ -175,6 +197,8 @@
      *
      * @param radioDataResponse Object containing response functions
      * @param radioDataIndication Object containing radio indications
+     *
+     * This is available when android.hardware.telephony.data is defined.
      */
     void setResponseFunctions(
             in IRadioDataResponse radioDataResponse, in IRadioDataIndication radioDataIndication);
@@ -229,6 +253,8 @@
      *        example, a zero-rating slice.
      *
      * Response function is IRadioDataResponse.setupDataCallResponse()
+     *
+     * This is available when android.hardware.telephony.data is defined.
      */
     void setupDataCall(in int serial, in AccessNetwork accessNetwork,
             in DataProfileInfo dataProfileInfo, in boolean roamingAllowed,
@@ -250,6 +276,8 @@
      * @param id callId The identifier of the data call which is provided in SetupDataCallResult
      *
      * Response function is IRadioDataResponse.startHandoverResponse()
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void startHandover(in int serial, in int callId);
 
@@ -260,6 +288,8 @@
      * @param keepalive A request structure containing all necessary info to describe a keepalive
      *
      * Response function is IRadioDataResponse.startKeepaliveResponse()
+     *
+     * This is available when android.hardware.telephony.data is defined.
      */
     void startKeepalive(in int serial, in KeepaliveRequest keepalive);
 
@@ -271,6 +301,8 @@
      *        IRadioDataResponse.startKeepaliveResponse
      *
      * Response function is IRadioDataResponse.stopKeepaliveResponse()
+     *
+     * This is available when android.hardware.telephony.data is defined.
      */
     void stopKeepalive(in int serial, in int sessionHandle);
 }
diff --git a/radio/aidl/android/hardware/radio/data/IRadioDataResponse.aidl b/radio/aidl/android/hardware/radio/data/IRadioDataResponse.aidl
index b714844..538b90a 100644
--- a/radio/aidl/android/hardware/radio/data/IRadioDataResponse.aidl
+++ b/radio/aidl/android/hardware/radio/data/IRadioDataResponse.aidl
@@ -41,6 +41,7 @@
      * @param id The allocated id. On an error, this is set to 0.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.data is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -53,6 +54,7 @@
      * @param dcResponse Attributes of data call
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.ims is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -65,6 +67,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.data is not defined
      *   RadioError:NONE indicates success. Any other error will remove the network from the list.
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_CALL_ID
@@ -83,6 +86,7 @@
      * @param dcResponse List of SetupDataCallResult
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.data is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -95,6 +99,7 @@
      * @param slicingConfig Current slicing configuration
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.data is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -106,6 +111,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.ims is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -117,6 +123,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.data is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:NO_MEMORY
@@ -135,6 +142,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.data is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SUBSCRIPTION_NOT_AVAILABLE
@@ -150,6 +158,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      *  Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.data is not defined
      *  RadioError:NONE
      *  RadioError:RADIO_NOT_AVAILABLE
      *  RadioError:MODEM_ERR
@@ -161,6 +170,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.data is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SUBSCRIPTION_NOT_AVAILABLE
@@ -180,6 +190,7 @@
      * @param dcResponse SetupDataCallResult
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.data is not defined
      *   RadioError:NONE must be returned on both success and failure of setup with the
      *              DataCallResponse.status containing the actual status
      *              For all other errors the DataCallResponse is ignored.
@@ -197,6 +208,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.ims is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -212,6 +224,7 @@
      *        request.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.data is not defined
      *   RadioError:NONE
      *   RadioError:NO_RESOURCES
      *   RadioError:INVALID_ARGUMENTS
@@ -222,6 +235,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.data is not defined
      *   RadioError:NONE
      *   RadioError:INVALID_ARGUMENTS
      */
diff --git a/radio/aidl/android/hardware/radio/ims/IRadioIms.aidl b/radio/aidl/android/hardware/radio/ims/IRadioIms.aidl
index 05d6035..90792f7 100644
--- a/radio/aidl/android/hardware/radio/ims/IRadioIms.aidl
+++ b/radio/aidl/android/hardware/radio/ims/IRadioIms.aidl
@@ -45,6 +45,8 @@
      * @param srvccCalls the list of calls
      *
      * Response function is IRadioImsResponse.setSrvccCallInfoResponse()
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void setSrvccCallInfo(int serial, in SrvccCall[] srvccCalls);
 
@@ -60,6 +62,8 @@
      * @param imsRegistration IMS registration information
      *
      * Response function is IRadioImsResponse.updateImsRegistrationInfoResponse()
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void updateImsRegistrationInfo(int serial, in ImsRegistration imsRegistration);
 
@@ -90,6 +94,8 @@
      *        mobile terminated use case eg. MO/MT call/SMS etc
      *
      * Response function is IRadioImsResponse.startImsTrafficResponse()
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void startImsTraffic(int serial, int token, ImsTrafficType imsTrafficType,
             AccessNetwork accessNetworkType, ImsCall.Direction trafficDirection);
@@ -103,6 +109,8 @@
      * @param token The token assigned by startImsTraffic()
      *
      * Response function is IRadioImsResponse.stopImsTrafficResponse()
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void stopImsTraffic(int serial, int token);
 
@@ -114,6 +122,8 @@
      * @param reason Specifies the reason that causes EPS fallback
      *
      * Response function is IRadioImsResponse.triggerEpsFallbackResponse()
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void triggerEpsFallback(int serial, in EpsFallbackReason reason);
 
@@ -122,6 +132,8 @@
      *
      * @param radioImsResponse Object containing response functions
      * @param radioImsIndication Object containing radio indications
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void setResponseFunctions(
             in IRadioImsResponse radioImsResponse, in IRadioImsIndication radioImsIndication);
@@ -137,6 +149,8 @@
      * @param bitsPerSecond The bit rate requested by the opponent UE
      *
      * Response function is IRadioImsResponse.sendAnbrQueryResponse()
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void sendAnbrQuery(
             int serial, ImsStreamType mediaType, ImsStreamDirection direction, int bitsPerSecond);
@@ -148,6 +162,8 @@
      * @param imsCalls The list of IMS calls
      *
      * Response function is IRadioImsResponse.updateImsCallStatusResponse()
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void updateImsCallStatus(int serial, in ImsCall[] imsCalls);
 }
diff --git a/radio/aidl/android/hardware/radio/ims/IRadioImsResponse.aidl b/radio/aidl/android/hardware/radio/ims/IRadioImsResponse.aidl
index b8a5a94..ca33d07 100644
--- a/radio/aidl/android/hardware/radio/ims/IRadioImsResponse.aidl
+++ b/radio/aidl/android/hardware/radio/ims/IRadioImsResponse.aidl
@@ -29,6 +29,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.ims is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_STATE
@@ -45,6 +46,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.ims is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_STATE
@@ -63,6 +65,7 @@
      *        it should be {@code null}.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.ims is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_STATE
@@ -80,6 +83,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.ims is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_STATE
@@ -96,6 +100,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.ims is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_STATE
@@ -112,6 +117,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.ims is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_STATE
@@ -128,6 +134,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.ims is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_STATE
diff --git a/radio/aidl/android/hardware/radio/ims/SuggestedAction.aidl b/radio/aidl/android/hardware/radio/ims/SuggestedAction.aidl
index 443042d..f0e28fc 100644
--- a/radio/aidl/android/hardware/radio/ims/SuggestedAction.aidl
+++ b/radio/aidl/android/hardware/radio/ims/SuggestedAction.aidl
@@ -35,4 +35,18 @@
      * management timer value as per the carrier requirements.
      */
     TRIGGER_PLMN_BLOCK_WITH_TIMEOUT,
+    /**
+     * Indicates that the IMS registration on current RAT failed multiple times.
+     * The radio shall block the current RAT and search for other available RATs in the
+     * background. If no other RAT is available that meets the carrier requirements, the
+     * radio may remain on the current RAT for internet service. The radio clears all
+     * RATs marked as unavailable if {@link IRadioIms#updateImsRegistrationInfo()} API
+     * with REGISTERED state is invoked.
+     */
+    TRIGGER_RAT_BLOCK,
+    /**
+     * Indicates that the radio clears all RATs marked as unavailable and tries to find
+     * an available RAT that meets the carrier requirements.
+     */
+    TRIGGER_CLEAR_RAT_BLOCK,
 }
diff --git a/radio/aidl/android/hardware/radio/ims/media/IImsMedia.aidl b/radio/aidl/android/hardware/radio/ims/media/IImsMedia.aidl
index 0af86c7..14fe68b 100644
--- a/radio/aidl/android/hardware/radio/ims/media/IImsMedia.aidl
+++ b/radio/aidl/android/hardware/radio/ims/media/IImsMedia.aidl
@@ -31,6 +31,8 @@
      * Set the listener functions for receiving notifications from the RTP stack.
      *
      * @param mediaListener Object containing listener methods
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void setListener(in IImsMediaListener mediaListener);
 
@@ -46,6 +48,8 @@
      * @param localEndPoint provides IP address, port and logical modem id for local RTP endpoint
      * @param config provides remote end point info and codec details. This could be null initially
      *        and the application may update this later using modifySession() API.
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void openSession(int sessionId, in LocalEndPoint localEndPoint, in RtpConfig config);
 
@@ -54,6 +58,8 @@
      * This shall also close the session specific binder connection opened as part of openSession().
      *
      * @param sessionId identifier for the rtp session that needs to be closed
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void closeSession(int sessionId);
 }
diff --git a/radio/aidl/android/hardware/radio/ims/media/IImsMediaSession.aidl b/radio/aidl/android/hardware/radio/ims/media/IImsMediaSession.aidl
index deb741b..0fe6740 100644
--- a/radio/aidl/android/hardware/radio/ims/media/IImsMediaSession.aidl
+++ b/radio/aidl/android/hardware/radio/ims/media/IImsMediaSession.aidl
@@ -32,6 +32,8 @@
      * Set the listener functions to receive IMS media session specific notifications.
      *
      * @param sessionListener Object containing notification methods
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void setListener(in IImsMediaSessionListener sessionListener);
 
@@ -40,6 +42,8 @@
      * the media stream by changing the value of the MediaDirection.
      *
      * @param config provides remote end point info and codec details
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void modifySession(in RtpConfig config);
 
@@ -48,6 +52,8 @@
      *
      * @param dtmfDigit single char having one of 12 values: 0-9, *, #
      * @param duration of the key press in milliseconds.
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void sendDtmf(char dtmfDigit, int duration);
 
@@ -57,12 +63,16 @@
      * stopDtmf() is not received yet, then that digit must be stopped first
      *
      * @param dtmfDigit single char having one of 12 values: 0-9, *, #
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void startDtmf(char dtmfDigit);
 
     /**
      * Stop sending the last DTMF digit started by startDtmf().
      * stopDtmf() without preceding startDtmf() must be ignored.
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void stopDtmf();
 
@@ -70,6 +80,8 @@
      * Send RTP header extension to the other party in the next RTP packet.
      *
      * @param extensions data to be transmitted via RTP header extension
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void sendHeaderExtension(in List<RtpHeaderExtension> extensions);
 
@@ -78,6 +90,30 @@
      * media quality notifications.
      *
      * @param threshold media quality thresholds for various quality parameters
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void setMediaQualityThreshold(in MediaQualityThreshold threshold);
+
+    /**
+     * Queries the current RTP reception statistics of the RTP stream. It will trigger the
+       IImsMediaSessionListener#notifyRtpReceptionStats(RtpReceptionStats).
+     *
+     * @param intervalMs The interval of the time in milliseconds of the RTP reception
+     * notification. When it is zero, the report is disabled.
+     *
+     * This is available when android.hardware.telephony.ims is defined.
+     */
+    void requestRtpReceptionStats(in int intervalMs);
+
+    /**
+     * Adjust the delay in the jitter buffer to synchronize the audio with the time of video
+     * frames
+     *
+     * @param delayMs The delay to apply to the jitter buffer. If it is positive, the jitter
+     * buffer increases the delay, if it is negative, the jitter buffer decreases the delay.
+     *
+     * This is available when android.hardware.telephony.ims is defined.
+     */
+    void adjustDelay(in int delayMs);
 }
diff --git a/radio/aidl/android/hardware/radio/ims/media/IImsMediaSessionListener.aidl b/radio/aidl/android/hardware/radio/ims/media/IImsMediaSessionListener.aidl
index 442c018..d3d0b26 100644
--- a/radio/aidl/android/hardware/radio/ims/media/IImsMediaSessionListener.aidl
+++ b/radio/aidl/android/hardware/radio/ims/media/IImsMediaSessionListener.aidl
@@ -21,6 +21,7 @@
 import android.hardware.radio.ims.media.RtpConfig;
 import android.hardware.radio.ims.media.RtpError;
 import android.hardware.radio.ims.media.RtpHeaderExtension;
+import android.hardware.radio.ims.media.RtpReceptionStats;
 
 /**
  * Interface declaring listener functions for unsolicited IMS media notifications per session.
@@ -37,6 +38,8 @@
      *   RtpError :INTERNAL_ERR
      *   RtpError :NO_MEMORY
      *   RtpError :NO_RESOURCES
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void onModifySessionResponse(in RtpConfig config, RtpError error);
 
@@ -49,6 +52,8 @@
      * packets from the most recently added config.
      *
      * @param config The remote config where the media is received
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void onFirstMediaPacketReceived(in RtpConfig config);
 
@@ -56,6 +61,8 @@
      * RTP header extension received from the other party
      *
      * @param extensions content of the received RTP header extension
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void onHeaderExtensionReceived(in List<RtpHeaderExtension> extensions);
 
@@ -64,6 +71,8 @@
      * {@link MediaQualityThreshold} set by {@link IImsMediaSession#setMediaQualityThreshold()}.
      *
      * @param quality The object of MediaQualityStatus with the rtp and the rtcp statistics.
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void notifyMediaQualityStatus(in MediaQualityStatus quality);
 
@@ -73,6 +82,8 @@
      * See 3GPP TS 26.114.
      *
      * @param config containing desired bitrate and direction
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void triggerAnbrQuery(in RtpConfig config);
 
@@ -81,6 +92,8 @@
      *
      * @param dtmfDigit single char having one of 12 values: 0-9, *, #
      * @param durationMs The duration to play the tone in milliseconds unit
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void onDtmfReceived(char dtmfDigit, int durationMs);
 
@@ -88,6 +101,18 @@
      * Notifies when a change to call quality has occurred
      *
      * @param CallQuality The call quality statistics of ongoing call since last report
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void onCallQualityChanged(in CallQuality callQuality);
+
+    /**
+     * Notifies the RTP reception statistics periodically after
+     * IImsMediaSession#requestRtpReceptionStats(intervalMs) is invoked.
+     *
+     * @param stats The RTP reception statistics
+     *
+     * This is available when android.hardware.telephony.ims is defined.
+     */
+    void notifyRtpReceptionStats(in RtpReceptionStats stats);
 }
diff --git a/radio/aidl/android/hardware/radio/ims/media/RtpReceptionStats.aidl b/radio/aidl/android/hardware/radio/ims/media/RtpReceptionStats.aidl
new file mode 100644
index 0000000..1239d13
--- /dev/null
+++ b/radio/aidl/android/hardware/radio/ims/media/RtpReceptionStats.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio.ims.media;
+
+@VintfStability
+parcelable RtpReceptionStats {
+    /** The timestamp of the latest RTP packet received */
+    int rtpTimestamp;
+    /** The sequence number of latest RTP packet received */
+    int rtpSequenceNumber;
+    /** The system clock time in millisecond of latest RTP packet received */
+    int timeDurationMs;
+    /** The jitter buffer size in millisecond when latest RTP packet received */
+    int jitterBufferMs;
+    /** The round trip time delay in millisecond when latest RTP packet received */
+    int roundTripTimeMs;
+}
diff --git a/radio/aidl/android/hardware/radio/messaging/IRadioMessaging.aidl b/radio/aidl/android/hardware/radio/messaging/IRadioMessaging.aidl
index 44818ff..945453c 100644
--- a/radio/aidl/android/hardware/radio/messaging/IRadioMessaging.aidl
+++ b/radio/aidl/android/hardware/radio/messaging/IRadioMessaging.aidl
@@ -50,6 +50,8 @@
      * @param ackPdu acknowledgement TPDU in hexadecimal format
      *
      * Response function is IRadioMessagingResponse.acknowledgeIncomingGsmSmsWithPduResponse()
+     *
+     * This is available when android.hardware.telephony.messaging is defined.
      */
     void acknowledgeIncomingGsmSmsWithPdu(in int serial, in boolean success, in String ackPdu);
 
@@ -61,6 +63,8 @@
      * @param smsAck Cdma Sms ack to be sent described by CdmaSmsAck
      *
      * Response function is IRadioMessagingResponse.acknowledgeLastIncomingCdmaSmsResponse()
+     *
+     * This is available when android.hardware.telephony.cdma is defined.
      */
     void acknowledgeLastIncomingCdmaSms(in int serial, in CdmaSmsAck smsAck);
 
@@ -75,6 +79,8 @@
      *        in TS 23.040, 9.2.3.22.
      *
      * Response function is IRadioMessagingResponse.acknowledgeLastIncomingGsmSmsResponse()
+     *
+     * This is available when android.hardware.telephony.messaging is defined.
      */
     void acknowledgeLastIncomingGsmSms(
             in int serial, in boolean success, in SmsAcknowledgeFailCause cause);
@@ -86,6 +92,8 @@
      * @param index record index of the message to delete
      *
      * Response function is IRadioMessagingResponse.deleteSmsOnRuimResponse()
+     *
+     * This is available when android.hardware.telephony.cdma is defined.
      */
     void deleteSmsOnRuim(in int serial, in int index);
 
@@ -96,6 +104,8 @@
      * @param index Record index of the message to delete.
      *
      * Response function is IRadioMessagingResponse.deleteSmsOnSimResponse()
+     *
+     * This is available when android.hardware.telephony.messaging is defined.
      */
     void deleteSmsOnSim(in int serial, in int index);
 
@@ -105,6 +115,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioMessagingResponse.getCdmaBroadcastConfigResponse()
+     *
+     * This is available when android.hardware.telephony.cdma is defined.
      */
     void getCdmaBroadcastConfig(in int serial);
 
@@ -114,6 +126,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioMessagingResponse.getGsmBroadcastConfigResponse()
+     *
+     * This is available when android.hardware.telephony.messaging is defined.
      */
     void getGsmBroadcastConfig(in int serial);
 
@@ -123,6 +137,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioMessagingResponse.getSmscAddressResponse()
+     *
+     * This is available when android.hardware.telephony.messaging is defined.
      */
     void getSmscAddress(in int serial);
 
@@ -134,6 +150,8 @@
      *        false if memory capacity is exceeded
      *
      * Response function is IRadioMessagingResponse.reportSmsMemoryStatusResponse()
+     *
+     * This is available when android.hardware.telephony.messaging is defined.
      */
     void reportSmsMemoryStatus(in int serial, in boolean available);
 
@@ -141,6 +159,8 @@
      * When response type received from a radio indication or radio response is
      * RadioIndicationType:UNSOLICITED_ACK_EXP or RadioResponseType:SOLICITED_ACK_EXP respectively,
      * acknowledge the receipt of those messages by sending responseAcknowledgement().
+     *
+     * This is available when android.hardware.telephony.messaging is defined.
      */
     void responseAcknowledgement();
 
@@ -151,6 +171,8 @@
      * @param sms CdmaSmsMessage to be sent
      *
      * Response function is IRadioMessagingResponse.sendCdmaSmsResponse()
+     *
+     * This is available when android.hardware.telephony.cdma is defined.
      */
     void sendCdmaSms(in int serial, in CdmaSmsMessage sms);
 
@@ -162,6 +184,8 @@
      * @param sms CdmaSmsMessage to be sent
      *
      * Response function is IRadioMessagingResponse.sendCdmaSmsExpectMoreResponse()
+     *
+     * This is available when android.hardware.telephony.cdma is defined.
      */
     void sendCdmaSmsExpectMore(in int serial, in CdmaSmsMessage sms);
 
@@ -174,6 +198,8 @@
      * @param message ImsSmsMessage to be sent
      *
      * Response function is IRadioMessagingResponse.sendImsSmsResponse()
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void sendImsSms(in int serial, in ImsSmsMessage message);
 
@@ -186,6 +212,8 @@
      * @param message GsmSmsMessage to be sent
      *
      * Response function is IRadioMessagingResponse.sendSmsResponse()
+     *
+     * This is available when android.hardware.telephony.messaging is defined.
      */
     void sendSms(in int serial, in GsmSmsMessage message);
 
@@ -200,6 +228,8 @@
      * @param message GsmSmsMessage to be sent
      *
      * Response function is IRadioMessagingResponse.sendSmsExpectMoreResponse()
+     *
+     * This is available when android.hardware.telephony.messaging is defined.
      */
     void sendSmsExpectMore(in int serial, in GsmSmsMessage message);
 
@@ -211,6 +241,8 @@
      *        true = activate, false = turn off
      *
      * Response function is IRadioMessagingResponse.setCdmaBroadcastActivationResponse()
+     *
+     * This is available when android.hardware.telephony.cdma is defined.
      */
     void setCdmaBroadcastActivation(in int serial, in boolean activate);
 
@@ -221,6 +253,8 @@
      * @param configInfo CDMA Broadcast SMS config to be set.
      *
      * Response function is IRadioMessagingResponse.setCdmaBroadcastConfigResponse()
+     *
+     * This is available when android.hardware.telephony.cdma is defined.
      */
     void setCdmaBroadcastConfig(in int serial, in CdmaBroadcastSmsConfigInfo[] configInfo);
 
@@ -232,6 +266,8 @@
      *        Cell Broadcast SMS. true = activate, false = turn off
      *
      * Response function is IRadioMessagingResponse.setGsmBroadcastActivationResponse()
+     *
+     * This is available when android.hardware.telephony.messaging is defined.
      */
     void setGsmBroadcastActivation(in int serial, in boolean activate);
 
@@ -242,6 +278,8 @@
      * @param configInfo Setting of GSM/WCDMA Cell broadcast config
      *
      * Response function is IRadioMessagingResponse.setGsmBroadcastConfigResponse()
+     *
+     * This is available when android.hardware.telephony.messaging is defined.
      */
     void setGsmBroadcastConfig(in int serial, in GsmBroadcastSmsConfigInfo[] configInfo);
 
@@ -250,6 +288,8 @@
      *
      * @param radioMessagingResponse Object containing response functions
      * @param radioMessagingIndication Object containing radio indications
+     *
+     * This is available when android.hardware.telephony.messaging is defined.
      */
     void setResponseFunctions(in IRadioMessagingResponse radioMessagingResponse,
             in IRadioMessagingIndication radioMessagingIndication);
@@ -261,6 +301,8 @@
      * @param smsc Short Message Service Center address to set
      *
      * Response function is IRadioMessagingResponse.setSmscAddressResponse()
+     *
+     * This is available when android.hardware.telephony.messaging is defined.
      */
     void setSmscAddress(in int serial, in String smsc);
 
@@ -271,6 +313,8 @@
      * @param cdmaSms CdmaSmsWriteArgs
      *
      * Response function is IRadioMessagingResponse.writeSmsToRuimResponse()
+     *
+     * This is available when android.hardware.telephony.cdma is defined.
      */
     void writeSmsToRuim(in int serial, in CdmaSmsWriteArgs cdmaSms);
 
@@ -281,6 +325,8 @@
      * @param smsWriteArgs SmsWriteArgs
      *
      * Response function is IRadioMessagingResponse.writeSmsToSimResponse()
+     *
+     * This is available when android.hardware.telephony.messaging is defined.
      */
     void writeSmsToSim(in int serial, in SmsWriteArgs smsWriteArgs);
 }
diff --git a/radio/aidl/android/hardware/radio/messaging/IRadioMessagingResponse.aidl b/radio/aidl/android/hardware/radio/messaging/IRadioMessagingResponse.aidl
index 84c34ca..f0d7999 100644
--- a/radio/aidl/android/hardware/radio/messaging/IRadioMessagingResponse.aidl
+++ b/radio/aidl/android/hardware/radio/messaging/IRadioMessagingResponse.aidl
@@ -31,6 +31,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.messaging is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -44,6 +46,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.cdma is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -66,6 +69,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.messaging is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -88,6 +93,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.cdma is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -109,6 +115,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.messaging is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SIM_FULL
@@ -131,6 +139,7 @@
      * @param configs Vector of CDMA Broadcast SMS configs.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.cdma is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -153,6 +162,8 @@
      * @param configs Vector of GSM/WCDMA Cell broadcast configs
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.messaging is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -175,6 +186,8 @@
      * @param smsc Short Message Service Center address on the device
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.messaging is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -196,6 +209,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.messaging is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -216,6 +231,7 @@
      * @param sms Response to sms sent as defined by SendSmsResult
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.cdma is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SMS_SEND_FAIL_RETRY
@@ -249,6 +265,7 @@
      * @param sms Sms result struct as defined by SendSmsResult
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.cdma is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -282,6 +299,7 @@
      * @param sms Response to sms sent as defined by SendSmsResult
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.ims is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SMS_SEND_FAIL_RETRY
@@ -309,6 +327,8 @@
      * @param sms Response to sms sent as defined by SendSmsResult
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.messaging is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SMS_SEND_FAIL_RETRY
@@ -341,6 +361,8 @@
      * @param sms Response to sms sent as defined by SendSmsResult
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.messaging is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SMS_SEND_FAIL_RETRY
@@ -371,6 +393,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.cdma is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -391,6 +414,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.cdma is not
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -410,6 +434,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.messaging is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -430,6 +456,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.messaging is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -449,6 +477,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.messaging is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -471,6 +501,7 @@
      * @param index record index where the cmda sms message is stored
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.cdma is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -497,6 +528,8 @@
      * @param index record index where the message is stored
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.messaging is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SIM_FULL
diff --git a/radio/aidl/android/hardware/radio/modem/IRadioModem.aidl b/radio/aidl/android/hardware/radio/modem/IRadioModem.aidl
index 0d51584..bfca5a9 100644
--- a/radio/aidl/android/hardware/radio/modem/IRadioModem.aidl
+++ b/radio/aidl/android/hardware/radio/modem/IRadioModem.aidl
@@ -48,6 +48,8 @@
      * @param on True to turn on the logical modem, otherwise turn it off.
      *
      * Response function is IRadioModemResponse.enableModemResponse()
+     *
+     * This is available when android.hardware.telephony is defined.
      */
     void enableModem(in int serial, in boolean on);
 
@@ -57,6 +59,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioModemResponse.getBasebandVersionResponse()
+     *
+     * This is available when android.hardware.telephony is defined.
      */
     void getBasebandVersion(in int serial);
 
@@ -68,6 +72,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioModemResponse.getDeviceIdentityResponse()
+     *
+     * This is available when android.hardware.telephony is defined.
      * @deprecated use getImei(int serial)
      */
     void getDeviceIdentity(in int serial);
@@ -78,6 +84,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioModemResponse.getHardwareConfigResponse()
+     *
+     * This is available when android.hardware.telephony is defined.
      */
     void getHardwareConfig(in int serial);
 
@@ -89,6 +97,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioModemResponse.getModemActivityInfoResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void getModemActivityInfo(in int serial);
 
@@ -99,6 +109,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioModemResponse.getModemStackStatusResponse()
+     *
+     * This is available when android.hardware.telephony is defined.
      */
     void getModemStackStatus(in int serial);
 
@@ -108,6 +120,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioModemResponse.getRadioCapabilityResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void getRadioCapability(in int serial);
 
@@ -146,6 +160,8 @@
      *
      * Response function is IRadioModemResponse.nvWriteCdmaPrlResponse()
      *
+     * This is available when android.hardware.telephony.cdma is defined.
+     *
      * @deprecated NV APIs are deprecated starting from Android U.
      */
     void nvWriteCdmaPrl(in int serial, in byte[] prl);
@@ -170,6 +186,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioModemResponse.requestShutdownResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void requestShutdown(in int serial);
 
@@ -177,6 +195,8 @@
      * When response type received from a radio indication or radio response is
      * RadioIndicationType:UNSOLICITED_ACK_EXP or RadioResponseType:SOLICITED_ACK_EXP respectively,
      * acknowledge the receipt of those messages by sending responseAcknowledgement().
+     *
+     * This is available when android.hardware.telephony is defined.
      */
     void responseAcknowledgement();
 
@@ -189,6 +209,8 @@
      * @param state The updated state. See the definition of state at DeviceStateType.
      *
      * Response function is IRadioModemResponse.sendDeviceStateResponse()
+     *
+     * This is available when android.hardware.telephony is defined.
      */
     void sendDeviceState(in int serial, in DeviceStateType deviceStateType, in boolean state);
 
@@ -201,6 +223,8 @@
      * @param rc RadioCapability structure to be set
      *
      * Response function is IRadioModemResponse.setRadioCapabilityResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void setRadioCapability(in int serial, in RadioCapability rc);
 
@@ -225,6 +249,8 @@
      *        on this modem or not. No effect if forEmergencyCall is false, or powerOn is false.
      *
      * Response function is IRadioConfigResponse.setRadioPowerResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void setRadioPower(in int serial, in boolean powerOn, in boolean forEmergencyCall,
             in boolean preferredForEmergencyCall);
@@ -234,6 +260,8 @@
      *
      * @param radioModemResponse Object containing response functions
      * @param radioModemIndication Object containing radio indications
+     *
+     * This is available when android.hardware.telephony is defined.
      */
     void setResponseFunctions(in IRadioModemResponse radioModemResponse,
             in IRadioModemIndication radioModemIndication);
@@ -244,6 +272,8 @@
      * @param serial : Serial number of request.
      *
      * Response function is IRadioModemResponse.getImeiResponse()
+     *
+     * This is available when android.hardware.telephony.gsm is defined.
      */
     void getImei(in int serial);
 }
diff --git a/radio/aidl/android/hardware/radio/modem/IRadioModemIndication.aidl b/radio/aidl/android/hardware/radio/modem/IRadioModemIndication.aidl
index 4b98277..ba3c510 100644
--- a/radio/aidl/android/hardware/radio/modem/IRadioModemIndication.aidl
+++ b/radio/aidl/android/hardware/radio/modem/IRadioModemIndication.aidl
@@ -20,6 +20,7 @@
 import android.hardware.radio.modem.HardwareConfig;
 import android.hardware.radio.modem.RadioCapability;
 import android.hardware.radio.modem.RadioState;
+import android.hardware.radio.modem.ImeiInfo;
 
 /**
  * Interface declaring unsolicited radio indications for modem APIs.
@@ -76,4 +77,12 @@
      * @param type Type of radio indication
      */
     void rilConnected(in RadioIndicationType type);
+
+    /**
+     * Indicates when there is a change in the IMEI mapping.
+     *
+     * @param type Type of radio indication
+     * @param imeiInfo IMEI information
+     */
+     void onImeiMappingChanged(in RadioIndicationType type, in ImeiInfo imeiInfo);
 }
diff --git a/radio/aidl/android/hardware/radio/modem/IRadioModemResponse.aidl b/radio/aidl/android/hardware/radio/modem/IRadioModemResponse.aidl
index b6946f3..6d2504c 100644
--- a/radio/aidl/android/hardware/radio/modem/IRadioModemResponse.aidl
+++ b/radio/aidl/android/hardware/radio/modem/IRadioModemResponse.aidl
@@ -41,6 +41,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:MODEM_ERR
@@ -55,6 +56,7 @@
      * @param version string containing version string for log reporting
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:EMPTY_RECORD
@@ -79,6 +81,7 @@
      * accessing the device.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:NO_MEMORY
@@ -100,6 +103,7 @@
      * @param config Array of HardwareConfig of the radio.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      */
@@ -110,6 +114,8 @@
      * @param activityInfo modem activity information
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:NO_MEMORY
@@ -126,6 +132,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:MODEM_ERR
@@ -137,6 +144,8 @@
      * @param rc Radio capability as defined by RadioCapability in types.hal
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:OPERATION_NOT_ALLOWED
@@ -175,6 +184,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.cdma is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *
@@ -197,6 +207,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:OPERATION_NOT_ALLOWED
@@ -212,6 +224,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:NO_MEMORY
@@ -229,6 +242,8 @@
      *        feedback return status
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE means a unsol radioCapability() will be sent within 30 seconds.
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:OPERATION_NOT_ALLOWED
@@ -247,6 +262,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:INTERNAL_ERR
      *   RadioError:INVALID_ARGUMENTS
@@ -265,6 +282,7 @@
      * @param imeiInfo IMEI information
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.gsm is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:MODEM_ERR
diff --git a/radio/aidl/android/hardware/radio/satellite/IndicationFilter.aidl b/radio/aidl/android/hardware/radio/network/CellularIdentifier.aidl
similarity index 76%
rename from radio/aidl/android/hardware/radio/satellite/IndicationFilter.aidl
rename to radio/aidl/android/hardware/radio/network/CellularIdentifier.aidl
index 16bd10d..6d43920 100644
--- a/radio/aidl/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/radio/aidl/android/hardware/radio/network/CellularIdentifier.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,13 +14,15 @@
  * limitations under the License.
  */
 
-package android.hardware.radio.satellite;
+package android.hardware.radio.network;
 
-/** @hide */
+/** @hide **/
 @VintfStability
 @Backing(type="int")
 @JavaDerive(toString=true)
-enum IndicationFilter {
-    NONE = 0,
-    SATELLITE_MODE = 1
+enum CellularIdentifier {
+    UNKNOWN = 0,
+    IMSI = 1,
+    IMEI = 2,
+    SUCI = 3,
 }
diff --git a/radio/aidl/android/hardware/radio/network/CellularIdentifierDisclosure.aidl b/radio/aidl/android/hardware/radio/network/CellularIdentifierDisclosure.aidl
new file mode 100644
index 0000000..52b4116
--- /dev/null
+++ b/radio/aidl/android/hardware/radio/network/CellularIdentifierDisclosure.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio.network;
+
+import android.hardware.radio.network.CellularIdentifier;
+import android.hardware.radio.network.NasProtocolMessage;
+
+/**
+ * A single occurrence of a cellular identifier being sent in the clear pre-authentication. See
+ * IRadioNetwork.setCellularIdentifierTransparencyEnabled for more details.
+ *
+ * @hide
+ */
+@JavaDerive(toString=true)
+@VintfStability
+parcelable CellularIdentifierDisclosure {
+    // The PLMN-ID to which the UE transmitted the cellular identifier
+    String plmn;
+    // The type of cellular identifier that was disclosed
+    CellularIdentifier identifier;
+    // The NAS protocol message within which the cellular identifier was transmitted.
+    NasProtocolMessage protocolMessage;
+    // Whether or not this cellular identifier disclosure is in service of an emergency call.
+    boolean isEmergency;
+}
diff --git a/radio/aidl/android/hardware/radio/network/ConnectionEvent.aidl b/radio/aidl/android/hardware/radio/network/ConnectionEvent.aidl
new file mode 100644
index 0000000..639ba89
--- /dev/null
+++ b/radio/aidl/android/hardware/radio/network/ConnectionEvent.aidl
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio.network;
+
+/**
+ * See IRadioNetwork.securityAlgorithmsUpdated for more details.
+ *
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+@JavaDerive(toString=true)
+enum ConnectionEvent {
+    // 2G GSM
+    CS_SERVICE_GSM = 0,
+    SIGNALLING_GSM = 1,
+
+    // 2G GPRS packet services
+    PS_SERVICE_GPRS = 2,
+    SIGNALLING_GPRS = 3,
+
+    // 3G packet services
+    PS_SERVICE_3G = 4,
+    SIGNALLING_3G = 5,
+
+    // 4G LTE packet services
+    NAS_SIGNALLING_LTE = 6,
+    AS_SIGNALLING_LTE = 7,
+
+    // VoLTE
+    VOLTE_SIP = 8,
+    VOLTE_RTP = 9,
+
+    // 5G packet services
+    NAS_SIGNALLING_5G = 10,
+    AS_SIGNALLING_5G = 11,
+
+    // VoNR
+    VONR_SIP = 12,
+    VONR_RTP = 13,
+}
diff --git a/radio/aidl/android/hardware/radio/network/IRadioNetwork.aidl b/radio/aidl/android/hardware/radio/network/IRadioNetwork.aidl
index e4c3856..32890ec 100644
--- a/radio/aidl/android/hardware/radio/network/IRadioNetwork.aidl
+++ b/radio/aidl/android/hardware/radio/network/IRadioNetwork.aidl
@@ -32,6 +32,7 @@
 
 /**
  * This interface is used by telephony and telecom to talk to cellular radio for network APIs.
+ * All functions apply to both terrestrial and extraterrestrial (satellite) based cellular networks.
  * All the functions have minimum one parameter:
  * serial: which corresponds to serial no. of request. Serial numbers must only be memorized for the
  * duration of a method call. If clients provide colliding serials (including passing the same
@@ -47,6 +48,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioNetworkResponse.getAllowedNetworkTypesBitmapResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void getAllowedNetworkTypesBitmap(in int serial);
 
@@ -56,6 +59,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioNetworkResponse.getAvailableBandModesResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void getAvailableBandModes(in int serial);
 
@@ -65,6 +70,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioNetworkResponse.getAvailableNetworksResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void getAvailableNetworks(in int serial);
 
@@ -74,6 +81,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioNetworkResponse.getBarringInfoResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void getBarringInfo(in int serial);
 
@@ -83,6 +92,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioNetworkResponse.getCdmaRoamingPreferenceResponse()
+     *
+     * This is available when android.hardware.telephony.cdma is defined.
      */
     void getCdmaRoamingPreference(in int serial);
 
@@ -95,6 +106,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioNetworkResponse.getCellInfoListResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void getCellInfoList(in int serial);
 
@@ -104,6 +117,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioNetworkResponse.getDataRegistrationStateResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void getDataRegistrationState(in int serial);
 
@@ -114,6 +129,8 @@
      *
      * Response function is IRadioNetworkResponse.getImsRegistrationStateResponse()
      *
+     * This is available when android.hardware.telephony.ims is defined.
+     *
      * @deprecated Deprecated starting from Android U.
      */
     void getImsRegistrationState(in int serial);
@@ -124,6 +141,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioNetworkResponse.getNetworkSelectionModeResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void getNetworkSelectionMode(in int serial);
 
@@ -133,6 +152,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioNetworkResponse.getOperatorResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void getOperator(in int serial);
 
@@ -142,6 +163,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioNetworkResponse.getSignalStrengthResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void getSignalStrength(in int serial);
 
@@ -151,6 +174,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioNetworkResponse.getSystemSelectionChannelsResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void getSystemSelectionChannels(in int serial);
 
@@ -161,6 +186,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioNetworkResponse.getVoiceRadioTechnologyResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void getVoiceRadioTechnology(in int serial);
 
@@ -170,6 +197,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioNetworkResponse.getVoiceRegistrationStateResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void getVoiceRegistrationState(in int serial);
 
@@ -179,6 +208,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioNetworkResponse.isNrDualConnectivityEnabledResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void isNrDualConnectivityEnabled(in int serial);
 
@@ -186,6 +217,8 @@
      * When response type received from a radio indication or radio response is
      * RadioIndicationType:UNSOLICITED_ACK_EXP or RadioResponseType:SOLICITED_ACK_EXP respectively,
      * acknowledge the receipt of those messages by sending responseAcknowledgement().
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void responseAcknowledgement();
 
@@ -198,6 +231,8 @@
      * @param networkTypeBitmap a 32-bit bearer bitmap of RadioAccessFamily
      *
      * Response function is IRadioNetworkResponse.setAllowedNetworkTypesBitmapResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void setAllowedNetworkTypesBitmap(in int serial, in int networkTypeBitmap);
 
@@ -208,6 +243,8 @@
      * @param mode RadioBandMode
      *
      * Response function is IRadioNetworkResponse.setBandModeResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void setBandMode(in int serial, in RadioBandMode mode);
 
@@ -220,6 +257,8 @@
      * @param newPassword new password
      *
      * Response function is IRadioNetworkResponse.setBarringPasswordResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void setBarringPassword(
             in int serial, in String facility, in String oldPassword, in String newPassword);
@@ -231,6 +270,8 @@
      * @param type CdmaRoamingType defined in types.hal
      *
      * Response function is IRadioNetworkResponse.setCdmaRoamingPreferenceResponse()
+     *
+     * This is available when android.hardware.telephony.cdma is defined.
      */
     void setCdmaRoamingPreference(in int serial, in CdmaRoamingType type);
 
@@ -243,6 +284,8 @@
      * @param rate minimum time in milliseconds to indicate time between unsolicited cellInfoList()
      *
      * Response function is IRadioNetworkResponse.setCellInfoListRateResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void setCellInfoListRate(in int serial, in int rate);
 
@@ -256,6 +299,8 @@
      *        indications are enabled. See IndicationFilter for the definition of each bit.
      *
      * Response function is IRadioNetworkResponse.setIndicationFilterResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void setIndicationFilter(in int serial, in int indicationFilter);
 
@@ -280,6 +325,8 @@
      * @param accessNetwork The type of network for which to apply these thresholds.
      *
      * Response function is IRadioNetworkResponse.setLinkCapacityReportingCriteriaResponse().
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void setLinkCapacityReportingCriteria(in int serial, in int hysteresisMs,
             in int hysteresisDlKbps, in int hysteresisUlKbps, in int[] thresholdsDownlinkKbps,
@@ -295,6 +342,8 @@
      * @param enable true=updates enabled (+CREG=2), false=updates disabled (+CREG=1)
      *
      * Response function is IRadioNetworkResponse.setLocationUpdatesResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void setLocationUpdates(in int serial, in boolean enable);
 
@@ -305,6 +354,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioNetworkResponse.setNetworkSelectionModeAutomaticResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void setNetworkSelectionModeAutomatic(in int serial);
 
@@ -321,6 +372,8 @@
      *        the next best RAN for network registration.
      *
      * Response function is IRadioNetworkResponse.setNetworkSelectionModeManualResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void setNetworkSelectionModeManual(
             in int serial, in String operatorNumeric, in AccessNetwork ran);
@@ -337,6 +390,8 @@
      *           {NrDualConnectivityState:DISABLE_IMMEDIATE}
      *
      * Response function is IRadioNetworkResponse.setNrDualConnectivityStateResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void setNrDualConnectivityState(
             in int serial, in NrDualConnectivityState nrDualConnectivityState);
@@ -346,6 +401,8 @@
      *
      * @param radioNetworkResponse Object containing response functions
      * @param radioNetworkIndication Object containing radio indications
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void setResponseFunctions(in IRadioNetworkResponse radioNetworkResponse,
             in IRadioNetworkIndication radioNetworkIndication);
@@ -364,6 +421,8 @@
      *        criteria. See SignalThresholdInfo for details.
      *
      * Response function is IRadioNetworkResponse.setSignalStrengthReportingCriteriaResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void setSignalStrengthReportingCriteria(
             in int serial, in SignalThresholdInfo[] signalThresholdInfos);
@@ -376,6 +435,8 @@
      * @param enable true = notifications enabled, false = notifications disabled.
      *
      * Response function is IRadioNetworkResponse.setSuppServiceNotificationsResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void setSuppServiceNotifications(in int serial, in boolean enable);
 
@@ -389,6 +450,8 @@
      * @param specifiers which bands to scan. Only used if specifyChannels is true.
      *
      * Response function is IRadioNetworkResponse.setSystemSelectionChannelsResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void setSystemSelectionChannels(
             in int serial, in boolean specifyChannels, in RadioAccessSpecifier[] specifiers);
@@ -400,6 +463,8 @@
      * @param request Defines the radio networks/bands/channels which need to be scanned.
      *
      * Response function is IRadioNetworkResponse.startNetworkScanResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void startNetworkScan(in int serial, in NetworkScanRequest request);
 
@@ -409,6 +474,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioNetworkResponse.stopNetworkScanResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void stopNetworkScan(in int serial);
 
@@ -419,6 +486,8 @@
      * @param netPin Network depersonlization code
      *
      * Response function is IRadioNetworkResponse.supplyNetworkDepersonalizationResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void supplyNetworkDepersonalization(in int serial, in String netPin);
 
@@ -431,6 +500,8 @@
      *
      * @param serial Serial number of request.
      * @param usageSetting the usage setting for the current SIM.
+     *
+     * This is available when android.hardware.telephony is defined.
      */
     oneway void setUsageSetting(in int serial, in UsageSetting usageSetting);
 
@@ -440,6 +511,8 @@
      * <p>Gets the usage setting in accordance with 3gpp 24.301 sec 4.3 and 3gpp 24.501 sec 4.3.
      *
      * @param serial Serial number of request.
+     *
+     * This is available when android.hardware.telephony is defined.
      */
     oneway void getUsageSetting(in int serial);
 
@@ -451,6 +524,8 @@
      * type of service to be scanned.
      *
      * Response function is IRadioEmergencyResponse.setEmergencyModeResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void setEmergencyMode(int serial, in EmergencyMode emcModeType);
 
@@ -462,6 +537,8 @@
      *                See {@link EmergencyNetworkScanTrigger}.
      *
      * Response function is IRadioEmergencyResponse.triggerEmergencyNetworkScanResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void triggerEmergencyNetworkScan(int serial, in EmergencyNetworkScanTrigger request);
 
@@ -474,6 +551,8 @@
      *        otherwise the modem shall resume from the last search.
      *
      * Response function is IRadioEmergencyResponse.cancelEmergencyNetworkScan()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void cancelEmergencyNetworkScan(int serial, boolean resetScan);
 
@@ -483,6 +562,8 @@
      * @param serial Serial number of the request.
      *
      * Response function is IRadioEmergencyResponse.exitEmergencyModeResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void exitEmergencyMode(in int serial);
 
@@ -513,6 +594,8 @@
      *                Otherwise, false.
      *
      * Response callback is IRadioResponse.setNullCipherAndIntegrityEnabledResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void setNullCipherAndIntegrityEnabled(in int serial, in boolean enabled);
 
@@ -528,6 +611,8 @@
      * @param serial Serial number of the request.
      *
      * Response callback is IRadioNetworkResponse.isNullCipherAndIntegrityEnabledResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void isNullCipherAndIntegrityEnabled(in int serial);
 
@@ -537,6 +622,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioNetworkResponse.isN1ModeEnabledResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void isN1ModeEnabled(in int serial);
 
@@ -554,6 +641,80 @@
      * @param enable {@code true} to enable N1 mode, {@code false} to disable N1 mode.
      *
      * Response function is IRadioNetworkResponse.setN1ModeEnabledResponse()
+     *
+     * This is available when android.hardware.telephony.radio.access is defined.
      */
     void setN1ModeEnabled(in int serial, boolean enable);
+
+    /**
+     * Get whether pre-auth cellular identifier in-the-clear transparency is enabled. If
+     * IRadioNetworkInterface.setCellularIdentifierTransparencyEnabled has been called, this should
+     * return the value of the `enabled` parameter of the last successful call. If it hasn't been
+     * called this should return the default value of true.
+     *
+     * @param serial Serial number of request
+     *
+     * Response callback is IRadioNetworkResponse.isCellularIdentifierTransparencyEnabledResponse
+     *
+     * This is available when android.hardware.telephony.access is defined.
+     */
+    void isCellularIdentifierTransparencyEnabled(in int serial);
+
+    /**
+     * Enable or disable transparency for in-the-clear cellular identifiers. If the value of enabled
+     * is true, the modem must call IRadioNetworkIndication.cellularIdentifierDisclosed when an
+     * IMSI, IMEI, or unciphered SUCI (in 5G SA) appears in one of the following UE-initiated NAS
+     * messages before a security context is established.
+     *
+     * Note: Cellular identifiers disclosed in uplink messages covered under a NAS Security Context
+     * as well as identifiers disclosed in downlink messages are out of scope.
+     *
+     * This feature applies to 2g, 3g, 4g, and 5g (SA and NSA) messages sent before a NAS security
+     * context is established. In scope message definitions and their associated spec references can
+     * be found in NasProtocolMessage.
+     *
+     * If the value of enabled is false, the modem must not call
+     * IRadioNetworkIndication.sentCellularIdentifierDisclosure again until a subsequent call
+     * re-enables this functionality. The modem may choose to stop tracking cellular identifiers in
+     * the clear during this time.
+     *
+     * Note: The default value of enabled shall be true.
+     *
+     * @param serial Serial number of request
+     * @param enabled Whether or not to enable sending indications for cellular identifiers in the
+     *         clear
+     *
+     * Response function is IRadioNetworkResponse.setCellularIdentifierTransparencyEnabledResponse
+     *
+     * This is available when android.hardware.telephony.access is defined.
+     */
+    void setCellularIdentifierTransparencyEnabled(in int serial, in boolean enabled);
+
+    /**
+     * Enables or disables security algorithm update reports via indication API
+     * {@link IRadioNetworkIndication.securityAlgorithmsUpdated()}.
+     *
+     * Note: The default value shall be enabled.
+     *
+     * @param serial Serial number of request.
+     * @param enable {@code true} to enable security algorithm update reports, {@code false} to
+     *         disable.
+     *
+     * Response function is IRadioNetworkResponse.setSecurityAlgorithmsUpdatedEnabledResponse()
+     *
+     * This is available when android.hardware.telephony.access is defined.
+     */
+    void setSecurityAlgorithmsUpdatedEnabled(in int serial, boolean enable);
+
+    /**
+     * Checks whether security algorithm update reports are enabled via indication API
+     * {@link IRadioNetworkIndication.securityAlgorithmsUpdated()}.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response function is IRadioNetworkResponse.isSecurityAlgorithmsUpdatedEnabledResponse()
+     *
+     * This is available when android.hardware.telephony.access is defined.
+     */
+    void isSecurityAlgorithmsUpdatedEnabled(in int serial);
 }
diff --git a/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl b/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl
index 2c6f4e7..9c2502d 100644
--- a/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl
+++ b/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl
@@ -21,11 +21,13 @@
 import android.hardware.radio.network.BarringInfo;
 import android.hardware.radio.network.CellIdentity;
 import android.hardware.radio.network.CellInfo;
+import android.hardware.radio.network.CellularIdentifierDisclosure;
 import android.hardware.radio.network.EmergencyRegResult;
 import android.hardware.radio.network.LinkCapacityEstimate;
 import android.hardware.radio.network.NetworkScanResult;
 import android.hardware.radio.network.PhoneRestrictedState;
 import android.hardware.radio.network.PhysicalChannelConfig;
+import android.hardware.radio.network.SecurityAlgorithmUpdate;
 import android.hardware.radio.network.SignalStrength;
 import android.hardware.radio.network.SuppSvcNotification;
 
@@ -200,4 +202,50 @@
      * @param result the result of the Emergency Network Scan
      */
     void emergencyNetworkScanResult(in RadioIndicationType type, in EmergencyRegResult result);
+
+    /**
+     * Report a cellular identifier disclosure event. See
+     * IRadioNetwork.setCellularIdnetifierTransparencyEnabled for more details.
+     *
+     * A non-exhaustive list of when this method should be called follows:
+     *
+     * - If a device attempts an IMSI attach to the network.
+     * - If a device includes an IMSI in the IDENTITY_RESPONSE message on the NAS and a security
+     * context has not yet been established.
+     * - If a device includes an IMSI in a DETACH_REQUEST message sent on the NAS and the message is
+     * sent before a security context has been established.
+     * - If a device includes an IMSI in a TRACKING_AREA_UPDATE message sent on the NAS and the
+     * message is sent before a security context has been established.
+     * - If a device uses a 2G network to send a LOCATION_UPDATE_REQUEST message on the NAS that
+     * includes an IMSI or IMEI.
+     * - If a device uses a 2G network to send a AUTHENTICATION_AND_CIPHERING_RESPONSE message on
+     * the NAS and the message includes an IMEISV.
+     *
+     * @param type Type of radio indication
+     * @param disclosure A CellularIdentifierDisclosure as specified by
+     *         IRadioNetwork.setCellularIdentifierTransparencyEnabled.
+     *
+     */
+    void cellularIdentifierDisclosed(
+            in RadioIndicationType type, in CellularIdentifierDisclosure disclosure);
+
+    /*
+     * Indicates that a new ciphering or integrity algorithm was used for a particular voice,
+     * signaling, or data connection attempt for a given PLMN and/or access network. Due to
+     * power concerns, once a connection type has been reported on, follow-up reports about that
+     * connection type are only generated if there is any change to the previously reported
+     * encryption or integrity. Thus the AP is only to be notified when there is new information.
+     * List is reset upon rebooting thus info about initial connections is always passed to the
+     * AP after a reboot. List is also reset if the SIM is changed or if there has been a change
+     * in the access network.
+     *
+     * Note: a change only in cell ID should not trigger an update, as the design is intended to
+     * be agnostic to dual connectivity ("secondary serving cells").
+     *
+     * @param type Type of radio indication
+     * @param securityAlgorithmUpdate SecurityAlgorithmUpdate encapsulates details of security
+     *         algorithm updates
+     */
+    void securityAlgorithmsUpdated(
+            in RadioIndicationType type, in SecurityAlgorithmUpdate securityAlgorithmUpdate);
 }
diff --git a/radio/aidl/android/hardware/radio/network/IRadioNetworkResponse.aidl b/radio/aidl/android/hardware/radio/network/IRadioNetworkResponse.aidl
index db37a0e..d9eea03 100644
--- a/radio/aidl/android/hardware/radio/network/IRadioNetworkResponse.aidl
+++ b/radio/aidl/android/hardware/radio/network/IRadioNetworkResponse.aidl
@@ -51,6 +51,8 @@
      * @param networkTypeBitmap a 32-bit bitmap of RadioAccessFamily.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:OPERATION_NOT_ALLOWED
@@ -67,6 +69,8 @@
      * @param bandModes List of RadioBandMode listing supported modes
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -84,6 +88,8 @@
      * @param networkInfos List of network operator information as OperatorInfos
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:OPERATION_NOT_ALLOWED
@@ -104,6 +110,8 @@
      * @param barringInfos a vector of barring info for all barring service types
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -117,6 +125,7 @@
      * @param type CdmaRoamingType
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.cdma is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -135,6 +144,8 @@
      * @param cellInfo List of current cell information known to radio
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -146,6 +157,8 @@
      * @param dataRegResponse Current data registration response as defined by RegStateResult
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -160,6 +173,7 @@
      * @param ratFamily RadioTechnologyFamily. This value is valid only if isRegistered is true.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.ims is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -178,6 +192,8 @@
      * @param selection false for automatic selection, true for manual selection
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:NO_MEMORY
@@ -197,6 +213,8 @@
      * @param numeric is 5 or 6 digit numeric code (MCC + MNC) or empty string if unregistered
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:NO_MEMORY
@@ -213,6 +231,8 @@
      * @param signalStrength Current signal strength
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -224,6 +244,8 @@
      * @param specifiers List of RadioAccessSpecifiers that are scanned.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -237,6 +259,8 @@
      * @param rat Current voice RAT
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -251,6 +275,8 @@
      * @param voiceRegResponse Current Voice registration response as defined by RegStateResult
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -264,6 +290,8 @@
      *        else false.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -274,6 +302,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:OPERATION_NOT_ALLOWED
@@ -289,6 +319,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:OPERATION_NOT_ALLOWED
@@ -306,6 +338,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SS_MODIFIED_TO_DIAL
@@ -326,6 +360,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.cdma is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:NO_MEMORY
@@ -344,6 +379,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:NO_MEMORY
@@ -359,6 +396,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:INVALID_ARGUMENTS
      *   RadioError:RADIO_NOT_AVAILABLE
@@ -371,6 +410,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:INVALID_ARGUMENTS
      *   RadioError:RADIO_NOT_AVAILABLE
@@ -382,6 +423,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:NO_MEMORY
@@ -399,6 +442,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:ILLEGAL_SIM_OR_ME
@@ -420,6 +465,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:ILLEGAL_SIM_OR_ME
@@ -442,6 +489,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -453,6 +502,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:INVALID_ARGUMENTS
      *   RadioError:RADIO_NOT_AVAILABLE
@@ -463,6 +514,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -481,6 +533,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -492,6 +546,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:DEVICE_IN_USE
@@ -505,6 +561,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:INTERNAL_ERR
      *   RadioError:MODEM_ERR
@@ -516,6 +574,8 @@
      * @param remainingRetries Number of retries remaining, must be equal to -1 if unknown.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:PASSWORD_INCORRECT (code is invalid)
@@ -535,6 +595,7 @@
      * @param info Response info struct containing response type, serial no. and error.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_STATE
@@ -549,6 +610,7 @@
      * @param usageSetting the usage setting for the current SIM.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_STATE
@@ -565,6 +627,8 @@
      * @param regState the current registration state of the modem.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:REQUEST_NOT_SUPPORTED
      *   RadioError:RADIO_NOT_AVAILABLE
@@ -580,6 +644,8 @@
      * @param info Response info struct containing response type, serial no. and error.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:REQUEST_NOT_SUPPORTED
      *   RadioError:RADIO_NOT_AVAILABLE
@@ -595,6 +661,8 @@
      * @param info Response info struct containing response type, serial no. and error.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:REQUEST_NOT_SUPPORTED
      *   RadioError:RADIO_NOT_AVAILABLE
@@ -609,6 +677,8 @@
      * @param info Response info struct containing response type, serial no. and error.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:REQUEST_NOT_SUPPORTED
      *   RadioError:RADIO_NOT_AVAILABLE
@@ -620,10 +690,11 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:MODEM_ERR
-     *   RadioError:REQUEST_NOT_SUPPORTED
      */
     void setNullCipherAndIntegrityEnabledResponse(in RadioResponseInfo info);
 
@@ -632,10 +703,11 @@
      * @param enabled the last known state of null ciphering and integrity algorithms
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:MODEM_ERR
-     *   RadioError:REQUEST_NOT_SUPPORTED
      */
     void isNullCipherAndIntegrityEnabledResponse(in RadioResponseInfo info, in boolean isEnabled);
 
@@ -647,6 +719,8 @@
      * @param isEnabled Indicates whether N1 mode is enabled or not.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -661,6 +735,8 @@
      * @param info Response info struct containing response type, serial no. and error.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -668,4 +744,66 @@
      *   RadioError:INVALID_STATE
      */
     void setN1ModeEnabledResponse(in RadioResponseInfo info);
+
+    /**
+     * Response of isCellularIdentifierTransparencyEnabled.
+     *
+     * @param info Response info struct containing response type, serial no. and error.
+     * @param isEnabled Indicates whether cellular identifier transparency is enabled or not.
+     *
+     * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     */
+    void isCellularIdentifierTransparencyEnabledResponse(
+            in RadioResponseInfo info, boolean isEnabled);
+
+    /**
+     * Response of setCellularIdentifierTransparencyEnabled.
+     *
+     * @param info Response info struct containing response type, serial no. and error.
+     *
+     * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:INVALID_STATE
+     */
+    void setCellularIdentifierTransparencyEnabledResponse(in RadioResponseInfo info);
+
+    /**
+     * Response of setSecurityAlgorithmsUpdatedEnabled.
+     *
+     * @param info Response info struct containing response type, serial no. and error.
+     *
+     * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:INVALID_STATE
+     */
+    void setSecurityAlgorithmsUpdatedEnabledResponse(in RadioResponseInfo info);
+
+    /**
+     * Response of isSecurityAlgorithmsUpdatedEnabled.
+     *
+     * @param info Response info struct containing response type, serial no. and error.
+     * @param isEnabled Indicates whether cellular ciphering transparency is enabled or not.
+     *
+     * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.radio.access is not
+     *                                    defined
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     */
+    void isSecurityAlgorithmsUpdatedEnabledResponse(
+            in RadioResponseInfo info, in boolean isEnabled);
 }
diff --git a/radio/aidl/android/hardware/radio/network/NasProtocolMessage.aidl b/radio/aidl/android/hardware/radio/network/NasProtocolMessage.aidl
new file mode 100644
index 0000000..e8d8047
--- /dev/null
+++ b/radio/aidl/android/hardware/radio/network/NasProtocolMessage.aidl
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio.network;
+
+/**
+ * Each enum value represents a message type on the NAS. The relevant cellular generation is noted.
+ * Sample spec references are provided, but generally only reference one network generation's spec.
+ *
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+@JavaDerive(toString=true)
+enum NasProtocolMessage {
+    UNKNOWN = 0,
+    // Sample Reference: 3GPP TS 24.301 8.2.4
+    // Applies to 2g, 3g, and 4g networks
+    ATTACH_REQUEST = 1,
+    // Sample Reference: 3GPP TS 24.301 8.2.19
+    // Applies to 2g, 3g, 4g, and 5g networks
+    IDENTITY_RESPONSE = 2,
+    // Sample Reference: 3GPP TS 24.301 8.2.11
+    // Applies to 2g, 3g, and 4g networks
+    DETACH_REQUEST = 3,
+    // Sample Reference: 3GPP TS 24.301 8.2.29
+    // Note: that per the spec, only temporary IDs should be sent
+    // in the TAU Request, but since the EPS Mobile Identity field
+    // supports IMSIs, this is included as an extra safety measure
+    // to combat implementation bugs.
+    // Applies to 4g and 5g networks
+    TRACKING_AREA_UPDATE_REQUEST = 4,
+    // Sample Reference: 3GPP TS 24.008 4.4.3
+    // Applies to 2g and 3g networks
+    LOCATION_UPDATE_REQUEST = 5,
+    // Reference: 3GPP TS 24.008 4.7.7.1
+    // Applies to 2g and 3g networks
+    AUTHENTICATION_AND_CIPHERING_RESPONSE = 6,
+    // Reference: 3GPP TS 24.501 8.2.6
+    // Applies to 5g networks
+    REGISTRATION_REQUEST = 7,
+    // Reference: 3GPP TS 24.501 8.2.12
+    // Applies to 5g networks
+    DEREGISTRATION_REQUEST = 8
+}
diff --git a/radio/aidl/android/hardware/radio/network/SecurityAlgorithm.aidl b/radio/aidl/android/hardware/radio/network/SecurityAlgorithm.aidl
new file mode 100644
index 0000000..6d2c018
--- /dev/null
+++ b/radio/aidl/android/hardware/radio/network/SecurityAlgorithm.aidl
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio.network;
+
+/**
+ * See IRadioNetwork.securityAlgorithmsUpdated for more details.
+ *
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+@JavaDerive(toString=true)
+enum SecurityAlgorithm {
+    // GSM CS services (3GPP TS 43.020)
+    A50 = 0,
+    A51 = 1,
+    A52 = 2,
+    A53 = 3,
+    A54 = 4,
+
+    // GPRS PS services (3GPP TS 43.020)
+    // These also refer to the respective integrity counterparts.
+    // E.g. GEA1 = GIA1
+    GEA0 = 14,
+    GEA1 = 15,
+    GEA2 = 16,
+    GEA3 = 17,
+    GEA4 = 18,
+    GEA5 = 19,
+
+    // 3G PS/CS services (3GPP TS 33.102)
+    UEA0 = 29,
+    UEA1 = 30,
+    UEA2 = 31,
+
+    // 4G PS services & 5G NSA (3GPP TS 33.401)
+    EEA0 = 41,
+    EEA1 = 42,
+    EEA2 = 43,
+    EEA3 = 44,
+    EEA4_ZUC = 45,
+
+    // 5G PS services (3GPP TS 33.401 for 5G NSA and 3GPP TS 33.501 for 5G SA)
+    NEA0 = 55,
+    NEA1 = 56,
+    NEA2 = 57,
+    NEA3 = 58,
+
+    // SIP layer security (See 3GPP TS 33.203)
+    SIP_NULL = 68,
+    AES_GCM = 69,
+    AES_GMAC = 70,
+    AES_CBC = 71,
+    DES_EDE3_CBC = 72,
+    AES_EDE3_CBC = 73,
+    HMAC_SHA1_96 = 74,
+    HMAC_SHA1_96_null = 75,
+    HMAC_MD5_96 = 76,
+    HMAC_MD5_96_null = 77,
+
+    // RTP (see 3GPP TS 33.328)
+    SRTP_AES_COUNTER = 87,
+    SRTP_AES_F8 = 88,
+    SRTP_HMAC_SHA1 = 89,
+
+    // ePDG (3GPP TS 33.402)
+    ENCR_AES_GCM_16 = 99,
+    ENCR_AES_CBC = 100,
+    AUTH_HMAC_SHA2_256_128 = 101,
+
+    /** Unknown */
+    UNKNOWN = 113,
+    OTHER = 114,
+
+    /** For proprietary algorithms */
+    ORYX = 124,
+}
diff --git a/radio/aidl/android/hardware/radio/network/SecurityAlgorithmUpdate.aidl b/radio/aidl/android/hardware/radio/network/SecurityAlgorithmUpdate.aidl
new file mode 100644
index 0000000..e945d3b
--- /dev/null
+++ b/radio/aidl/android/hardware/radio/network/SecurityAlgorithmUpdate.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio.network;
+
+import android.hardware.radio.network.ConnectionEvent;
+import android.hardware.radio.network.SecurityAlgorithm;
+
+/**
+ * A single occurrence capturing a notable change to previously reported
+ * cryptography algorithms for a given network and network event.
+ *
+ * @hide
+ */
+@JavaDerive(toString=true)
+@VintfStability
+parcelable SecurityAlgorithmUpdate {
+    /**
+     * Type of connection event which is being reported on
+     */
+    ConnectionEvent connectionEvent;
+    /**
+     * Encryption algorithm which was used
+     */
+    SecurityAlgorithm encryption;
+    /**
+     * Integrity algorithm which was used
+     */
+    SecurityAlgorithm integrity;
+    /**
+     * Whether or not this connection event is associated with an
+     * unauthenticated / unencrypted emergency session
+     */
+    boolean isUnprotectedEmergency;
+}
diff --git a/radio/aidl/android/hardware/radio/sap/ISap.aidl b/radio/aidl/android/hardware/radio/sap/ISap.aidl
index 552e602..04eee43 100644
--- a/radio/aidl/android/hardware/radio/sap/ISap.aidl
+++ b/radio/aidl/android/hardware/radio/sap/ISap.aidl
@@ -28,6 +28,8 @@
      * @param serial Id to match req-resp. Resp must include same serial.
      * @param type APDU command type
      * @param command CommandAPDU/CommandAPDU7816 parameter depending on type
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void apduReq(in int serial, in SapApduType type, in byte[] command);
 
@@ -36,6 +38,8 @@
      *
      * @param serial Id to match req-resp. Resp must include same serial.
      * @param maxMsgSizeBytes MaxMsgSize to be used for SIM Access Profile connection
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void connectReq(in int serial, in int maxMsgSizeBytes);
 
@@ -43,6 +47,8 @@
      * DISCONNECT_REQ from SAP 1.1 spec 5.1.3
      *
      * @param serial Id to match req-resp. Resp must include same serial.
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void disconnectReq(in int serial);
 
@@ -51,6 +57,8 @@
      *
      * @param serial Id to match req-resp. Resp must include same serial.
      * @param powerOn true for on, false for off
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void powerReq(in int serial, in boolean powerOn);
 
@@ -58,6 +66,8 @@
      * RESET_SIM_REQ from SAP 1.1 spec 5.1.14
      *
      * @param serial Id to match req-resp. Resp must include same serial.
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void resetSimReq(in int serial);
 
@@ -65,6 +75,8 @@
      * Set callback that has response and unsolicited indication functions
      *
      * @param sapCallback Object containing response and unosolicited indication callbacks
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void setCallback(in ISapCallback sapCallback);
 
@@ -73,6 +85,8 @@
      *
      * @param serial Id to match req-resp. Resp must include same serial.
      * @param transferProtocol Transport Protocol
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void setTransferProtocolReq(in int serial, in SapTransferProtocol transferProtocol);
 
@@ -80,6 +94,8 @@
      * TRANSFER_ATR_REQ from SAP 1.1 spec 5.1.8
      *
      * @param serial Id to match req-resp. Resp must include same serial.
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void transferAtrReq(in int serial);
 
@@ -87,6 +103,8 @@
      * TRANSFER_CARD_READER_STATUS_REQ from SAP 1.1 spec 5.1.17
      *
      * @param serial Id to match req-resp. Resp must include same serial.
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void transferCardReaderStatusReq(in int serial);
 }
diff --git a/radio/aidl/android/hardware/radio/sap/ISapCallback.aidl b/radio/aidl/android/hardware/radio/sap/ISapCallback.aidl
index 34111eb..37a94b8 100644
--- a/radio/aidl/android/hardware/radio/sap/ISapCallback.aidl
+++ b/radio/aidl/android/hardware/radio/sap/ISapCallback.aidl
@@ -29,6 +29,8 @@
      * @param serial Id to match req-resp. Value must match the one in req.
      * @param resultCode ResultCode to indicate if command was processed correctly
      *        Possible values:
+     *        SapResultCode:NOT_SUPPORTED  when android.hardware.telephony.subscription is not
+     *                                     defined
      *        SapResultCode:SUCCESS,
      *        SapResultCode:GENERIC_FAILURE,
      *        SapResultCode:CARD_NOT_ACCESSSIBLE,
@@ -77,6 +79,8 @@
      * @param serial Id to match req-resp. Value must match the one in req.
      * @param resultCode ResultCode to indicate if command was processed correctly
      *        Possible values:
+     *        SapResultCode:NOT_SUPPORTED  when android.hardware.telephony.subscription is not
+     *                                     defined
      *        SapResultCode:SUCCESS,
      *        SapResultCode:GENERIC_FAILURE,
      *        SapResultCode:CARD_NOT_ACCESSSIBLE, (possible only for power on req)
@@ -92,6 +96,8 @@
      * @param serial Id to match req-resp. Value must match the one in req.
      * @param resultCode ResultCode to indicate if command was processed correctly
      *        Possible values:
+     *        SapResultCode:NOT_SUPPORTED  when android.hardware.telephony.subscription is not
+     *                                     defined
      *        SapResultCode:SUCCESS,
      *        SapResultCode:GENERIC_FAILURE,
      *        SapResultCode:CARD_NOT_ACCESSSIBLE,
@@ -114,6 +120,8 @@
      * @param serial Id to match req-resp. Value must match the one in req.
      * @param resultCode ResultCode to indicate if command was processed correctly
      *        Possible values:
+     *        SapResultCode:NOT_SUPPORTED  when android.hardware.telephony.subscription is not
+     *                                     defined
      *        SapResultCode:SUCCESS,
      *        SapResultCode:GENERIC_FAILURE,
      *        SapResultCode:CARD_ALREADY_POWERED_OFF,
@@ -130,6 +138,8 @@
      * @param serial Id to match req-resp. Value must match the one in req.
      * @param resultCode ResultCode to indicate if command was processed correctly
      *        Possible values:
+     *        SapResultCode:NOT_SUPPORTED  when android.hardware.telephony.subscription is not
+     *                                     defined
      *        SapResultCode:SUCCESS,
      *        SapResultCode:GENERIC_FAILURE
      *        SapResultCode:DATA_NOT_AVAILABLE
@@ -145,6 +155,8 @@
      * @param serial Id to match req-resp. Value must match the one in req.
      * @param resultCode ResultCode to indicate if command was processed correctly
      *        Possible values:
+     *        SapResultCode:NOT_SUPPORTED  when android.hardware.telephony.subscription is not
+     *                                     defined
      *        SapResultCode:SUCCESS
      *        SapResultCode:NOT_SUPPORTED
      */
diff --git a/radio/aidl/android/hardware/radio/satellite/IRadioSatellite.aidl b/radio/aidl/android/hardware/radio/satellite/IRadioSatellite.aidl
deleted file mode 100644
index 749f49f..0000000
--- a/radio/aidl/android/hardware/radio/satellite/IRadioSatellite.aidl
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.radio.satellite;
-
-import android.hardware.radio.satellite.IRadioSatelliteIndication;
-import android.hardware.radio.satellite.IRadioSatelliteResponse;
-import android.hardware.radio.satellite.IndicationFilter;
-import android.hardware.radio.satellite.SatelliteFeature;
-
-/**
- * This interface is used by telephony to send commands to and query info from satellite modem.
- * All the functions have minimum one parameter:
- * serial: which corresponds to serial no. of request. Serial numbers must only be memorized for the
- * duration of a method call. If clients provide colliding serials (including passing the same
- * serial to different methods), multiple responses (one for each method call) must still be served.
- * @hide
- */
-@VintfStability
-oneway interface IRadioSatellite {
-    /**
-     * Add contacts that are allowed to be used for satellite communication. This is applicable for
-     * incoming messages as well.
-     *
-     * @param serial Serial number of request.
-     * @param contacts List of allowed contacts to be added.
-     */
-    void addAllowedSatelliteContacts(in int serial, in String[] contacts);
-
-    /**
-     * Get feature capabilities supported by satellite.
-     *
-     * @param serial Serial number of request.
-     */
-    void getCapabilities(in int serial);
-
-    /**
-     * Get max number of characters per text message.
-     *
-     * @param serial Serial number of request.
-     */
-    void getMaxCharactersPerTextMessage(in int serial);
-
-    /**
-     * Get pending messages.
-     *
-     * @param serial Serial number of request.
-     */
-    void getPendingMessages(in int serial);
-
-    /**
-     * Get satellite modem state.
-     *
-     * @param serial Serial number of request.
-     */
-    void getPowerState(in int serial);
-
-    /**
-     * Get current satellite registration mode, which is defined in {@link #SatelliteMode}.
-     *
-     * @param serial Serial number of request.
-     */
-    void getSatelliteMode(in int serial);
-
-    /**
-     * Get time for next visibility of satellite.
-     *
-     * @param serial Serial number of request.
-     */
-    void getTimeForNextSatelliteVisibility(in int serial);
-
-    /**
-     * Provision the subscription with a satellite provider. This is needed to register the
-     * subscription if the provider allows dynamic registration.
-     *
-     * @param serial Serial number of request.
-     * @param imei IMEI of the SIM associated with the satellite modem.
-     * @param msisdn MSISDN of the SIM associated with the satellite modem.
-     * @param imsi IMSI of the SIM associated with the satellite modem.
-     * @param features List of features to be provisioned.
-     */
-    void provisionService(in int serial, in String imei, in String msisdn, in String imsi,
-            in SatelliteFeature[] features);
-
-    /**
-     * Remove contacts that are allowed to be used for satellite communication. This is applicable
-     * for incoming messages as well.
-     *
-     * @param serial Serial number of request.
-     * @param contacts List of allowed contacts to be removed.
-     */
-    void removeAllowedSatelliteContacts(in int serial, in String[] contacts);
-
-    /**
-     * When response type received from a radio indication or radio response is
-     * RadioIndicationType:UNSOLICITED_ACK_EXP or RadioResponseType:SOLICITED_ACK_EXP respectively,
-     * acknowledge the receipt of those messages by sending responseAcknowledgement().
-     */
-    void responseAcknowledgement();
-
-    /**
-     * Send text messages.
-     *
-     * @param serial Serial number of request.
-     * @param messages List of messages in text format to be sent.
-     * @param destination The recipient of the message.
-     * @param latitude The current latitude of the device.
-     * @param longitude The current longitude of the device. The location (i.e., latitude and
-     *        longitude) of the device will be filled for emergency messages.
-     */
-    void sendMessages(in int serial, in String[] messages, in String destination,
-            in double latitude, in double longitude);
-
-    /**
-     * Set the filter for what type of indication framework want to receive from modem.
-     *
-     * @param serial Serial number of request.
-     * @param filterBitmask The filter bitmask identifying what type of indication Telephony
-     *                      framework wants to receive from modem. This bitmask is the 'or'
-     *                      combination of the enum values defined in {@link #IndicationFilter}.
-     */
-    void setIndicationFilter(in int serial, in int filterBitmask);
-
-    /**
-     * Turn satellite modem on/off.
-     *
-     * @param serial Serial number of request.
-     * @param on True for turning on.
-     *           False for turning off.
-     */
-    void setPower(in int serial, in boolean on);
-
-    /**
-     * Set response functions for Satellite requests and indications.
-     *
-     * @param satelliteResponse Object containing response functions
-     * @param satelliteIndication Object containing radio indications
-     */
-    void setResponseFunctions(in IRadioSatelliteResponse satelliteResponse,
-            in IRadioSatelliteIndication satelliteIndication);
-
-    /**
-     * User started pointing to the satellite. Modem should continue to update the pointing input
-     * as user device/satellite moves.
-     *
-     * @param serial Serial number of request.
-     */
-    void startSendingSatellitePointingInfo(in int serial);
-
-    /**
-     * Stop sending satellite pointing info to the framework.
-     *
-     * @param serial Serial number of request.
-     */
-    void stopSendingSatellitePointingInfo(in int serial);
-}
diff --git a/radio/aidl/android/hardware/radio/satellite/IRadioSatelliteIndication.aidl b/radio/aidl/android/hardware/radio/satellite/IRadioSatelliteIndication.aidl
deleted file mode 100644
index 2a2fb95..0000000
--- a/radio/aidl/android/hardware/radio/satellite/IRadioSatelliteIndication.aidl
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.radio.satellite;
-
-import android.hardware.radio.RadioIndicationType;
-import android.hardware.radio.satellite.NTRadioTechnology;
-import android.hardware.radio.satellite.PointingInfo;
-import android.hardware.radio.satellite.SatelliteFeature;
-import android.hardware.radio.satellite.SatelliteMode;
-
-/**
- * Interface declaring unsolicited radio indications for satellite APIs.
- * @hide
- */
-@VintfStability
-oneway interface IRadioSatelliteIndication {
-    /**
-     * Confirms that ongoing message transfer is complete.
-     *
-     * @param type Type of radio indication
-     * @param complete True mean the transfer is complete.
-     *                 False means the transfer is not complete.
-     */
-    void onMessagesTransferComplete(in RadioIndicationType type, in boolean complete);
-
-    /**
-     * Indicates new message received on device.
-     *
-     * @param type Type of radio indication
-     * @param messages List of new messages received.
-     */
-    void onNewMessages(in RadioIndicationType type, in String[] messages);
-
-    /**
-     * Indicates that satellite has pending messages for the device to be pulled.
-     *
-     * @param type Type of radio indication
-     * @param count Number of pending messages.
-     */
-    void onPendingMessageCount(in RadioIndicationType type, in int count);
-
-    /**
-     * Indicate that satellite provision state has changed.
-     *
-     * @param type Type of radio indication
-     * @param provisioned True means the service is provisioned.
-     *                    False means the service is not provisioned.
-     * @param features List of Feature whose provision state has changed.
-     */
-    void onProvisionStateChanged(
-            in RadioIndicationType type, boolean provisioned, in SatelliteFeature[] features);
-
-    /**
-     * Indicate that satellite mode has changed.
-     *
-     * @param type Type of radio indication
-     * @param mode The current mode of the satellite modem.
-     */
-    void onSatelliteModeChanged(in RadioIndicationType type, in SatelliteMode mode);
-
-    /**
-     * Indicate that satellite Pointing input has changed.
-     *
-     * @param type Type of radio indication
-     * @param pointingInfo The current pointing info.
-     */
-    void onSatellitePointingInfoChanged(in RadioIndicationType type, in PointingInfo pointingInfo);
-
-    /**
-     * Indicate that satellite radio technology has changed.
-     *
-     * @param type Type of radio indication
-     * @param technology The current technology of the satellite modem.
-     */
-    void onSatelliteRadioTechnologyChanged(
-            in RadioIndicationType type, in NTRadioTechnology technology);
-}
diff --git a/radio/aidl/android/hardware/radio/satellite/IRadioSatelliteResponse.aidl b/radio/aidl/android/hardware/radio/satellite/IRadioSatelliteResponse.aidl
deleted file mode 100644
index 2bef3d2..0000000
--- a/radio/aidl/android/hardware/radio/satellite/IRadioSatelliteResponse.aidl
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.radio.satellite;
-
-import android.hardware.radio.RadioResponseInfo;
-import android.hardware.radio.satellite.NTRadioTechnology;
-import android.hardware.radio.satellite.SatelliteCapabilities;
-import android.hardware.radio.satellite.SatelliteMode;
-
-/**
- * Interface declaring response functions to solicited radio requests for satellite APIs.
- * @hide
- */
-@VintfStability
-oneway interface IRadioSatelliteResponse {
-    /**
-     * Acknowledge the receipt of radio request sent to the vendor. This must be sent only for
-     * radio request which take long time to respond. For more details, refer
-     * https://source.android.com/devices/tech/connect/ril.html
-     *
-     * @param serial Serial no. of the request whose acknowledgement is sent.
-     */
-    void acknowledgeRequest(in int serial);
-
-    /**
-     * Response of the request addAllowedSatelliteContacts.
-     *
-     * @param info Response info struct containing serial no. and error
-     *
-     * Valid errors returned:
-     *   RadioError:NONE
-     *   RadioError:ABORTED
-     *   RadioError:ACCESS_BARRED
-     *   RadioError:CANCELLED
-     *   RadioError:INTERNAL_ERR
-     *   RadioError:INVALID_ARGUMENTS
-     *   RadioError:INVALID_CONTACT
-     *   RadioError:INVALID_MODEM_STATE
-     *   RadioError:INVALID_SIM_STATE
-     *   RadioError:INVALID_STATE
-     *   RadioError:MODEM_ERR
-     *   RadioError:NETWORK_ERR
-     *   RadioError:NETWORK_NOT_READY
-     *   RadioError:NETWORK_REJECT
-     *   RadioError:NETWORK_TIMEOUT
-     *   RadioError:NO_MEMORY
-     *   RadioError:NO_NETWORK_FOUND
-     *   RadioError:NO_RESOURCES
-     *   RadioError:NO_SATELLITE_SIGNAL
-     *   RadioError:NO_SUBSCRIPTION
-     *   RadioError:NOT_SUFFICIENT_ACCOUNT_BALANCE
-     *   RadioError:OPERATION_NOT_ALLOWED
-     *   RadioError:RADIO_NOT_AVAILABLE
-     *   RadioError:REQUEST_NOT_SUPPORTED
-     *   RadioError:REQUEST_RATE_LIMITED
-     *   RadioError:SIM_ABSENT
-     *   RadioError:SIM_BUSY
-     *   RadioError:SIM_ERR
-     *   RadioError:SIM_FULL
-     *   RadioError:SYSTEM_ERR
-     *   RadioError:UNIDENTIFIED_SUBSCRIBER
-     */
-    void addAllowedSatelliteContactsResponse(in RadioResponseInfo info);
-
-    /**
-     * Response of the request getCapabilities.
-     *
-     * @param info Response info struct containing serial no. and error
-     * @param capabilities List of capabilities that the satellite modem supports.
-     *
-     * Valid errors returned:
-     *   RadioError:NONE
-     *   RadioError:INTERNAL_ERR
-     *   RadioError:INVALID_ARGUMENTS
-     *   RadioError:INVALID_MODEM_STATE
-     *   RadioError:INVALID_SIM_STATE
-     *   RadioError:INVALID_STATE
-     *   RadioError:MODEM_ERR
-     *   RadioError:NO_MEMORY
-     *   RadioError:NO_RESOURCES
-     *   RadioError:RADIO_NOT_AVAILABLE
-     *   RadioError:REQUEST_NOT_SUPPORTED
-     *   RadioError:REQUEST_RATE_LIMITED
-     *   RadioError:SYSTEM_ERR
-     */
-    void getCapabilitiesResponse(in RadioResponseInfo info, in SatelliteCapabilities capabilities);
-
-    /**
-     * Response of the request getMaxCharactersPerTextMessage.
-     *
-     * @param info Response info struct containing serial no. and error
-     * @param charLimit Maximum number of characters in a text message that can be sent.
-     *
-     * Valid errors returned:
-     *   RadioError:NONE
-     *   RadioError:INTERNAL_ERR
-     *   RadioError:INVALID_ARGUMENTS
-     *   RadioError:INVALID_MODEM_STATE
-     *   RadioError:INVALID_SIM_STATE
-     *   RadioError:INVALID_STATE
-     *   RadioError:MODEM_ERR
-     *   RadioError:NO_MEMORY
-     *   RadioError:NO_RESOURCES
-     *   RadioError:RADIO_NOT_AVAILABLE
-     *   RadioError:REQUEST_NOT_SUPPORTED
-     *   RadioError:REQUEST_RATE_LIMITED
-     *   RadioError:SYSTEM_ERR
-     */
-    void getMaxCharactersPerTextMessageResponse(in RadioResponseInfo info, in int charLimit);
-
-    /**
-     * Response of the request getPendingMessages.
-     *
-     * @param info Response info struct containing serial no. and error
-     * @param messages List of pending messages received.
-     *
-     * Valid errors returned:
-     *   RadioError:NONE
-     *   RadioError:ABORTED
-     *   RadioError:ACCESS_BARRED
-     *   RadioError:BLOCKED_DUE_TO_CALL
-     *   RadioError:CANCELLED
-     *   RadioError:ENCODING_ERR
-     *   RadioError:ENCODING_NOT_SUPPORTED
-     *   RadioError:INTERNAL_ERR
-     *   RadioError:INVALID_ARGUMENTS
-     *   RadioError:INVALID_MODEM_STATE
-     *   RadioError:INVALID_SIM_STATE
-     *   RadioError:INVALID_SMS_FORMAT
-     *   RadioError:INVALID_STATE
-     *   RadioError:MODEM_ERR
-     *   RadioError:NETWORK_ERR
-     *   RadioError:NETWORK_NOT_READY
-     *   RadioError:NETWORK_REJECT
-     *   RadioError:NETWORK_TIMEOUT
-     *   RadioError:NO_MEMORY
-     *   RadioError:NO_NETWORK_FOUND
-     *   RadioError:NO_RESOURCES
-     *   RadioError:NO_SMS_TO_ACK
-     *   RadioError:NO_SATELLITE_SIGNAL
-     *   RadioError:NO_SUBSCRIPTION
-     *   RadioError:NOT_SUFFICIENT_ACCOUNT_BALANCE
-     *   RadioError:OPERATION_NOT_ALLOWED
-     *   RadioError:RADIO_NOT_AVAILABLE
-     *   RadioError:REQUEST_NOT_SUPPORTED
-     *   RadioError:REQUEST_RATE_LIMITED
-     *   RadioError:SIM_ABSENT
-     *   RadioError:SIM_BUSY
-     *   RadioError:SIM_ERR
-     *   RadioError:SIM_FULL
-     *   RadioError:SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED
-     *   RadioError:SYSTEM_ERR
-     *   RadioError:SWITCHED_FROM_SATELLITE_TO_TERRESTRIAL
-     */
-    void getPendingMessagesResponse(in RadioResponseInfo info, in String[] messages);
-
-    /**
-     * Response of the request getPowerSate.
-     *
-     * @param info Response info struct containing serial no. and error
-     * @param on True means the modem is ON.
-     *           False means the modem is OFF.
-     *
-     * Valid errors returned:
-     *   RadioError:NONE
-     *   RadioError:INTERNAL_ERR
-     *   RadioError:INVALID_ARGUMENTS
-     *   RadioError:INVALID_MODEM_STATE
-     *   RadioError:INVALID_SIM_STATE
-     *   RadioError:INVALID_STATE
-     *   RadioError:MODEM_ERR
-     *   RadioError:NO_MEMORY
-     *   RadioError:NO_RESOURCES
-     *   RadioError:RADIO_NOT_AVAILABLE
-     *   RadioError:REQUEST_NOT_SUPPORTED
-     *   RadioError:REQUEST_RATE_LIMITED
-     *   RadioError:SYSTEM_ERR
-     */
-    void getPowerStateResponse(in RadioResponseInfo info, in boolean on);
-
-    /**
-     * Response of the request getSatelliteMode.
-     *
-     * @param info Response info struct containing serial no. and error
-     * @param mode Current Mode of the satellite modem.
-     * @param technology The current technology of the satellite modem.
-     *
-     * Valid errors returned:
-     *   RadioError:NONE
-     *   RadioError:INTERNAL_ERR
-     *   RadioError:INVALID_ARGUMENTS
-     *   RadioError:INVALID_MODEM_STATE
-     *   RadioError:INVALID_SIM_STATE
-     *   RadioError:INVALID_STATE
-     *   RadioError:MODEM_ERR
-     *   RadioError:NO_MEMORY
-     *   RadioError:NO_RESOURCES
-     *   RadioError:RADIO_NOT_AVAILABLE
-     *   RadioError:REQUEST_NOT_SUPPORTED
-     *   RadioError:REQUEST_RATE_LIMITED
-     *   RadioError:SYSTEM_ERR
-     */
-    void getSatelliteModeResponse(
-            in RadioResponseInfo info, in SatelliteMode mode, in NTRadioTechnology technology);
-
-    /**
-     * Response of the request getTimeForNextSatelliteVisibility.
-     *
-     * @param info Response info struct containing serial no. and error
-     * @param timeInSeconds The duration in seconds after which the satellite will be visible.
-     *
-     * Valid errors returned:
-     *   RadioError:NONE
-     *   RadioError:INTERNAL_ERR
-     *   RadioError:INVALID_ARGUMENTS
-     *   RadioError:INVALID_MODEM_STATE
-     *   RadioError:INVALID_SIM_STATE
-     *   RadioError:INVALID_STATE
-     *   RadioError:MODEM_ERR
-     *   RadioError:NO_MEMORY
-     *   RadioError:NO_RESOURCES
-     *   RadioError:RADIO_NOT_AVAILABLE
-     *   RadioError:REQUEST_NOT_SUPPORTED
-     *   RadioError:REQUEST_RATE_LIMITED
-     *   RadioError:SYSTEM_ERR
-     */
-    void getTimeForNextSatelliteVisibilityResponse(in RadioResponseInfo info, in int timeInSeconds);
-
-    /**
-     * Response of the request provisionService.
-     *
-     * @param info Response info struct containing serial no. and error
-     * @param provisioned True means the service is provisioned.
-     *                    False means the service is not provisioned.
-     *
-     * Valid errors returned:
-     *   RadioError:NONE
-     *   RadioError:ABORTED
-     *   RadioError:ACCESS_BARRED
-     *   RadioError:CANCELLED
-     *   RadioError:FEATURE_NOT_SUPPORTED
-     *   RadioError:INTERNAL_ERR
-     *   RadioError:INVALID_ARGUMENTS
-     *   RadioError:INVALID_MODEM_STATE
-     *   RadioError:INVALID_SIM_STATE
-     *   RadioError:INVALID_STATE
-     *   RadioError:MODEM_ERR
-     *   RadioError:MODEM_INCOMPATIBLE
-     *   RadioError:NETWORK_ERR
-     *   RadioError:NETWORK_NOT_READY
-     *   RadioError:NETWORK_REJECT
-     *   RadioError:NETWORK_TIMEOUT
-     *   RadioError:NO_MEMORY
-     *   RadioError:NO_NETWORK_FOUND
-     *   RadioError:NO_RESOURCES
-     *   RadioError:NO_SATELLITE_SIGNAL
-     *   RadioError:NO_SUBSCRIPTION
-     *   RadioError:OPERATION_NOT_ALLOWED
-     *   RadioError:RADIO_NOT_AVAILABLE
-     *   RadioError:RADIO_TECHNOLOGY_NOT_SUPPORTED
-     *   RadioError:REQUEST_NOT_SUPPORTED
-     *   RadioError:REQUEST_RATE_LIMITED
-     *   RadioError:SIM_ABSENT
-     *   RadioError:SIM_BUSY
-     *   RadioError:SIM_ERR
-     *   RadioError:SIM_FULL
-     *   RadioError:SUBSCRIBER_NOT_AUTHORIZED
-     *   RadioError:SYSTEM_ERR
-     */
-    void provisionServiceResponse(in RadioResponseInfo info, in boolean provisioned);
-
-    /**
-     * Response of the request removeAllowedSatelliteContacts.
-     *
-     * @param info Response info struct containing serial no. and error
-     *
-     * Valid errors returned:
-     *   RadioError:NONE
-     *   RadioError:ABORTED
-     *   RadioError:ACCESS_BARRED
-     *   RadioError:CANCELLED
-     *   RadioError:INTERNAL_ERR
-     *   RadioError:INVALID_ARGUMENTS
-     *   RadioError:INVALID_CONTACT
-     *   RadioError:INVALID_MODEM_STATE
-     *   RadioError:INVALID_SIM_STATE
-     *   RadioError:INVALID_STATE
-     *   RadioError:MODEM_ERR
-     *   RadioError:NETWORK_ERR
-     *   RadioError:NETWORK_NOT_READY
-     *   RadioError:NETWORK_REJECT
-     *   RadioError:NETWORK_TIMEOUT
-     *   RadioError:NO_MEMORY
-     *   RadioError:NO_NETWORK_FOUND
-     *   RadioError:NO_RESOURCES
-     *   RadioError:NO_SATELLITE_SIGNAL
-     *   RadioError:NO_SUBSCRIPTION
-     *   RadioError:NOT_SUFFICIENT_ACCOUNT_BALANCE
-     *   RadioError:OPERATION_NOT_ALLOWED
-     *   RadioError:RADIO_NOT_AVAILABLE
-     *   RadioError:REQUEST_NOT_SUPPORTED
-     *   RadioError:REQUEST_RATE_LIMITED
-     *   RadioError:SIM_ABSENT
-     *   RadioError:SIM_BUSY
-     *   RadioError:SIM_ERR
-     *   RadioError:SIM_FULL
-     *   RadioError:SYSTEM_ERR
-     *   RadioError:UNIDENTIFIED_SUBSCRIBER
-     */
-    void removeAllowedSatelliteContactsResponse(in RadioResponseInfo info);
-
-    /**
-     * Response of the request sendMessages.
-     *
-     * @param info Response info struct containing serial no. and error
-     *
-     * Valid errors returned:
-     *   RadioError:NONE
-     *   RadioError:ABORTED
-     *   RadioError:ACCESS_BARRED
-     *   RadioError:BLOCKED_DUE_TO_CALL
-     *   RadioError:CANCELLED
-     *   RadioError:ENCODING_ERR
-     *   RadioError:ENCODING_NOT_SUPPORTED
-     *   RadioError:INTERNAL_ERR
-     *   RadioError:INVALID_ARGUMENTS
-     *   RadioError:INVALID_MODEM_STATE
-     *   RadioError:INVALID_SIM_STATE
-     *   RadioError:INVALID_SMS_FORMAT
-     *   RadioError:INVALID_STATE
-     *   RadioError:MODEM_ERR
-     *   RadioError:NETWORK_ERR
-     *   RadioError:NETWORK_NOT_READY
-     *   RadioError:NETWORK_REJECT
-     *   RadioError:NETWORK_TIMEOUT
-     *   RadioError:NO_MEMORY
-     *   RadioError:NO_NETWORK_FOUND
-     *   RadioError:NO_RESOURCES
-     *   RadioError:NO_SMS_TO_ACK
-     *   RadioError:NO_SATELLITE_SIGNAL
-     *   RadioError:NO_SUBSCRIPTION
-     *   RadioError:NOT_SUFFICIENT_ACCOUNT_BALANCE
-     *   RadioError:OPERATION_NOT_ALLOWED
-     *   RadioError:RADIO_NOT_AVAILABLE
-     *   RadioError:REQUEST_NOT_SUPPORTED
-     *   RadioError:REQUEST_RATE_LIMITED
-     *   RadioError:SIM_ABSENT
-     *   RadioError:SIM_BUSY
-     *   RadioError:SIM_ERR
-     *   RadioError:SIM_FULL
-     *   RadioError:SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED
-     *   RadioError:SMS_SEND_FAIL_RETRY
-     *   RadioError:SYSTEM_ERR
-     *   RadioError:SWITCHED_FROM_SATELLITE_TO_TERRESTRIAL
-     *   RadioError:UNIDENTIFIED_SUBSCRIBER
-     */
-    void sendMessagesResponse(in RadioResponseInfo info);
-
-    /**
-     * Response of the request setIndicationFilter.
-     *
-     * @param info Response info struct containing serial no. and error
-     *
-     * Valid errors returned:
-     *   RadioError:NONE
-     *   RadioError:INTERNAL_ERR
-     *   RadioError:INVALID_ARGUMENTS
-     *   RadioError:INVALID_MODEM_STATE
-     *   RadioError:INVALID_SIM_STATE
-     *   RadioError:INVALID_STATE
-     *   RadioError:MODEM_ERR
-     *   RadioError:NO_MEMORY
-     *   RadioError:NO_RESOURCES
-     *   RadioError:RADIO_NOT_AVAILABLE
-     *   RadioError:REQUEST_NOT_SUPPORTED
-     *   RadioError:REQUEST_RATE_LIMITED
-     *   RadioError:SYSTEM_ERR
-     */
-    void setIndicationFilterResponse(in RadioResponseInfo info);
-
-    /**
-     * Response of the request setPower.
-     *
-     * @param info Response info struct containing serial no. and error
-     *
-     * Valid errors returned:
-     *   RadioError:NONE
-     *   RadioError:INTERNAL_ERR
-     *   RadioError:INVALID_ARGUMENTS
-     *   RadioError:INVALID_MODEM_STATE
-     *   RadioError:INVALID_SIM_STATE
-     *   RadioError:INVALID_STATE
-     *   RadioError:MODEM_ERR
-     *   RadioError:NO_MEMORY
-     *   RadioError:NO_RESOURCES
-     *   RadioError:NO_RF_CALIBRATION_INFO
-     *   RadioError:RADIO_NOT_AVAILABLE
-     *   RadioError:REQUEST_NOT_SUPPORTED
-     *   RadioError:REQUEST_RATE_LIMITED
-     *   RadioError:RF_HARDWARE_ISSUE
-     *   RadioError:SYSTEM_ERR
-     */
-    void setPowerResponse(in RadioResponseInfo info);
-
-    /**
-     * Response of the request startSendingSatellitePointingInfo.
-     *
-     * @param info Response info struct containing serial no. and error
-     *
-     * Valid errors returned:
-     *   RadioError:NONE
-     *   RadioError:INTERNAL_ERR
-     *   RadioError:INVALID_ARGUMENTS
-     *   RadioError:INVALID_MODEM_STATE
-     *   RadioError:INVALID_SIM_STATE
-     *   RadioError:INVALID_STATE
-     *   RadioError:MODEM_ERR
-     *   RadioError:NO_MEMORY
-     *   RadioError:NO_RESOURCES
-     *   RadioError:RADIO_NOT_AVAILABLE
-     *   RadioError:REQUEST_NOT_SUPPORTED
-     *   RadioError:REQUEST_RATE_LIMITED
-     *   RadioError:SYSTEM_ERR
-     */
-    void startSendingSatellitePointingInfoResponse(in RadioResponseInfo info);
-
-    /**
-     * Response of the request stopSendingSatellitePointingInfo.
-     *
-     * @param info Response info struct containing serial no. and error
-     *
-     * Valid errors returned:
-     *   RadioError:NONE
-     *   RadioError:INTERNAL_ERR
-     *   RadioError:INVALID_ARGUMENTS
-     *   RadioError:INVALID_MODEM_STATE
-     *   RadioError:INVALID_SIM_STATE
-     *   RadioError:INVALID_STATE
-     *   RadioError:MODEM_ERR
-     *   RadioError:NO_MEMORY
-     *   RadioError:NO_RESOURCES
-     *   RadioError:RADIO_NOT_AVAILABLE
-     *   RadioError:REQUEST_NOT_SUPPORTED
-     *   RadioError:REQUEST_RATE_LIMITED
-     *   RadioError:SYSTEM_ERR
-     */
-    void stopSendingSatellitePointingInfoResponse(in RadioResponseInfo info);
-}
diff --git a/radio/aidl/android/hardware/radio/satellite/NTRadioTechnology.aidl b/radio/aidl/android/hardware/radio/satellite/NTRadioTechnology.aidl
deleted file mode 100644
index f161895..0000000
--- a/radio/aidl/android/hardware/radio/satellite/NTRadioTechnology.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.radio.satellite;
-
-/** @hide */
-@VintfStability
-@Backing(type="int")
-@JavaDerive(toString=true)
-enum NTRadioTechnology {
-    /* 3GPP NB-IoT (Narrowband Internet of Things) over Non-Terrestrial-Networks technology */
-    NB_IOT_NTN = 0,
-    /* 3GPP 5G NR over Non-Terrestrial-Networks technology */
-    NR_NTN = 1,
-    /* 3GPP eMTC (enhanced Machine-Type Communication) over Non-Terrestrial-Networks technology */
-    EMTC_NTN = 2,
-    /* Proprietary technology like Iridium or Bullitt */
-    PROPRIETARY = 3
-}
diff --git a/radio/aidl/android/hardware/radio/satellite/PointingInfo.aidl b/radio/aidl/android/hardware/radio/satellite/PointingInfo.aidl
deleted file mode 100644
index 9f12d7a..0000000
--- a/radio/aidl/android/hardware/radio/satellite/PointingInfo.aidl
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.radio.satellite;
-
-/** @hide */
-@VintfStability
-@JavaDerive(toString=true)
-parcelable PointingInfo {
-    /* Satellite azimuth in degrees */
-    float satelliteAzimuthDegrees;
-
-    /* Satellite elevation in degrees */
-    float satelliteElevationDegrees;
-
-    /* Antenna azimuth in degrees */
-    float antennaAzimuthDegrees;
-
-    /**
-     * Angle of rotation about the x axis. This value represents the angle between a plane
-     * parallel to the device's screen and a plane parallel to the ground.
-     */
-    float antennaPitchDegrees;
-
-    /**
-     * Angle of rotation about the y axis. This value represents the angle between a plane
-     * perpendicular to the device's screen and a plane parallel to the ground.
-     */
-    float antennaRollDegrees;
-}
diff --git a/radio/aidl/android/hardware/radio/satellite/SatelliteCapabilities.aidl b/radio/aidl/android/hardware/radio/satellite/SatelliteCapabilities.aidl
deleted file mode 100644
index 29eb635..0000000
--- a/radio/aidl/android/hardware/radio/satellite/SatelliteCapabilities.aidl
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.radio.satellite;
-
-import android.hardware.radio.satellite.NTRadioTechnology;
-import android.hardware.radio.satellite.SatelliteFeature;
-
-/** @hide */
-@VintfStability
-@JavaDerive(toString=true)
-parcelable SatelliteCapabilities {
-    /**
-     * List of technologies supported by the satellite modem.
-     */
-    NTRadioTechnology[] supportedRadioTechnologies;
-
-    /**
-     * Whether satellite mode is always on (this indicates the power impact of keeping it on is
-     * very minimal).
-     */
-    boolean isAlwaysOn;
-
-    /**
-     * Whether UE needs to point to a satellite to send and receive data.
-     */
-    boolean needsPointingToSatellite;
-
-    /**
-     * List of features supported by the satellite modem.
-     */
-    SatelliteFeature[] supportedFeatures;
-
-    /**
-     * Whether UE needs a separate SIM profile to communicate with satellite network.
-     */
-    boolean needsSeparateSimProfile;
-}
diff --git a/radio/aidl/android/hardware/radio/satellite/SatelliteFeature.aidl b/radio/aidl/android/hardware/radio/satellite/SatelliteFeature.aidl
deleted file mode 100644
index 6284ec8..0000000
--- a/radio/aidl/android/hardware/radio/satellite/SatelliteFeature.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.radio.satellite;
-
-/** @hide */
-@VintfStability
-@Backing(type="int")
-@JavaDerive(toString=true)
-enum SatelliteFeature {
-    /* Able to send and receive SMS messages to/from SOS numbers like call/service centers */
-    SOS_SMS = 0,
-    /* Able to send and receive SMS messages to/from emergency numbers like 911 */
-    EMERGENCY_SMS = 1,
-    /* Able to send and receive SMS messages to/from any allowed contacts */
-    SMS = 2,
-    /* Able to send device location to allowed contacts */
-    LOCATION_SHARING = 3
-}
diff --git a/radio/aidl/android/hardware/radio/satellite/SatelliteMode.aidl b/radio/aidl/android/hardware/radio/satellite/SatelliteMode.aidl
deleted file mode 100644
index b764d0e..0000000
--- a/radio/aidl/android/hardware/radio/satellite/SatelliteMode.aidl
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.radio.satellite;
-
-/** @hide */
-@VintfStability
-@Backing(type="int")
-@JavaDerive(toString=true)
-enum SatelliteMode {
-    /* Satellite modem is powered off */
-    POWERED_OFF = 0,
-    /* Satellite modem is in out of service state and not searching for satellite signal */
-    OUT_OF_SERVICE_NOT_SEARCHING = 1,
-    /* Satellite modem is in out of service state and searching for satellite signal */
-    OUT_OF_SERVICE_SEARCHING = 2,
-    /* Satellite modem has found satellite signal and gets connected to the satellite network */
-    ACQUIRED = 3,
-    /* Satellite modem is sending and/or receiving messages */
-    MESSAGE_TRANSFERRING = 4
-}
diff --git a/radio/aidl/android/hardware/radio/sim/IRadioSim.aidl b/radio/aidl/android/hardware/radio/sim/IRadioSim.aidl
index 1453664..7870a74 100644
--- a/radio/aidl/android/hardware/radio/sim/IRadioSim.aidl
+++ b/radio/aidl/android/hardware/radio/sim/IRadioSim.aidl
@@ -49,6 +49,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioSimResponse.areUiccApplicationsEnabledResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void areUiccApplicationsEnabled(in int serial);
 
@@ -61,6 +63,8 @@
      * @param aid AID value, See ETSI 102.221 8.1 and 101.220 4, empty string if no value.
      *
      * Response function is IRadioSimResponse.changeIccPin2ForAppResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void changeIccPin2ForApp(in int serial, in String oldPin2, in String newPin2, in String aid);
 
@@ -73,6 +77,8 @@
      * @param aid AID value, See ETSI 102.221 8.1 and 101.220 4, empty string if no value.
      *
      * Response function is IRadioSimResponse.changeIccPinForAppResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void changeIccPinForApp(in int serial, in String oldPin, in String newPin, in String aid);
 
@@ -91,6 +97,8 @@
      * @param enable true if to enable uiccApplications, false to disable.
      *
      * Response function is IRadioSimResponse.enableUiccApplicationsResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void enableUiccApplications(in int serial, in boolean enable);
 
@@ -100,6 +108,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioSimResponse.getAllowedCarriersResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void getAllowedCarriers(in int serial);
 
@@ -111,6 +121,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioSimResponse.getCdmaSubscriptionResponse()
+     *
+     * This is available when android.hardware.telephony.cdma is defined.
      */
     void getCdmaSubscription(in int serial);
 
@@ -120,6 +132,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioSimResponse.getCdmaSubscriptionSourceResponse()
+     *
+     * This is available when android.hardware.telephony.cdma is defined.
      */
     void getCdmaSubscriptionSource(in int serial);
 
@@ -135,6 +149,8 @@
      *        This is only applicable in the case of Fixed Dialing Numbers (FDN) requests.
      *
      * Response function is IRadioSimResponse.getFacilityLockForAppResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void getFacilityLockForApp(in int serial, in String facility, in String password,
             in int serviceClass, in String appId);
@@ -145,6 +161,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioSimResponse.getIccCardStatusResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void getIccCardStatus(in int serial);
 
@@ -155,6 +173,8 @@
      * @param aid AID value, See ETSI 102.221 8.1 and 101.220 4, empty string if no value.
      *
      * Response function is IRadioSimResponse.getImsiForAppResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void getImsiForApp(in int serial, in String aid);
 
@@ -164,6 +184,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioSimResponse.getSimPhonebookCapacityResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void getSimPhonebookCapacity(in int serial);
 
@@ -175,6 +197,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioSimResponse.getSimPhonebookRecordsResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void getSimPhonebookRecords(in int serial);
 
@@ -187,6 +211,8 @@
      *
      * Response function is IRadioSimResponse.iccCloseLogicalChannelResponse()
      *
+     * This is available when android.hardware.telephony.subscription is defined.
+     *
      * @deprecated use iccCloseLogicalChannelWithSessionInfo instead.
      */
     void iccCloseLogicalChannel(in int serial, in int channelId);
@@ -202,6 +228,8 @@
      * @param iccIo IccIo
      *
      * Response function is IRadioSimResponse.iccIoForAppResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void iccIoForApp(in int serial, in IccIo iccIo);
 
@@ -220,6 +248,8 @@
      * @param p2 P2 value, described in ISO 7816-4. Ignore if equal to RadioConst:P2_CONSTANT_NO_P2
      *
      * Response function is IRadioSimResponse.iccOpenLogicalChannelResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void iccOpenLogicalChannel(in int serial, in String aid, in int p2);
 
@@ -233,6 +263,8 @@
      * @param message SimApdu to be sent
      *
      * Response function is IRadioSimResponse.iccTransmitApduBasicChannelResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void iccTransmitApduBasicChannel(in int serial, in SimApdu message);
 
@@ -245,6 +277,8 @@
      * @param message SimApdu to be sent
      *
      * Response function is IRadioSimResponse.iccTransmitApduLogicalChannelResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void iccTransmitApduLogicalChannel(in int serial, in SimApdu message);
 
@@ -254,6 +288,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioSimResponse.reportStkServiceIsRunningResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void reportStkServiceIsRunning(in int serial);
 
@@ -267,6 +303,8 @@
      * @param aid AID value, See ETSI 102.221 8.1 and 101.220 4, empty string if no value
      *
      * Response function is IRadioSimResponse.requestIccSimAuthenticationResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void requestIccSimAuthentication(
             in int serial, in int authContext, in String authData, in String aid);
@@ -275,6 +313,8 @@
      * When response type received from a radio indication or radio response is
      * RadioIndicationType:UNSOLICITED_ACK_EXP or RadioResponseType:SOLICITED_ACK_EXP respectively,
      * acknowledge the receipt of those messages by sending responseAcknowledgement().
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void responseAcknowledgement();
 
@@ -286,6 +326,8 @@
      * @param contents SAT/USAT command in hexadecimal format string starting with command tag
      *
      * Response function is IRadioSimResponse.sendEnvelopeResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void sendEnvelope(in int serial, in String contents);
 
@@ -301,6 +343,8 @@
      * @param contents SAT/USAT command in hexadecimal format starting with command tag
      *
      * Response function is IRadioSimResponse.sendEnvelopeWithStatusResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void sendEnvelopeWithStatus(in int serial, in String contents);
 
@@ -312,6 +356,8 @@
      *        first byte of response data
      *
      * Response function is IRadioSimResponse.sendTerminalResponseResponseToSim()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void sendTerminalResponseToSim(in int serial, in String contents);
 
@@ -331,6 +377,8 @@
      * @param multiSimPolicy Policy to be used for devices with multiple SIMs.
      *
      * Response function is IRadioSimResponse.setAllowedCarriersResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void setAllowedCarriers(in int serial, in CarrierRestrictions carriers,
             in SimLockMultiSimPolicy multiSimPolicy);
@@ -344,6 +392,8 @@
      * @param imsiEncryptionInfo ImsiEncryptionInfo
      *
      * Response function is IRadioSimResponse.setCarrierInfoForImsiEncryptionResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void setCarrierInfoForImsiEncryption(in int serial, in ImsiEncryptionInfo imsiEncryptionInfo);
 
@@ -354,6 +404,8 @@
      * @param cdmaSub CdmaSubscriptionSource
      *
      * Response function is IRadioSimResponse.setCdmaSubscriptionSourceResponse()
+     *
+     * This is available when android.hardware.telephony.cdma is defined.
      */
     void setCdmaSubscriptionSource(in int serial, in CdmaSubscriptionSource cdmaSub);
 
@@ -370,6 +422,8 @@
      *        This is only applicable in the case of Fixed Dialing Numbers (FDN) requests.
      *
      * Response function is IRadioSimResponse.setFacilityLockForAppResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void setFacilityLockForApp(in int serial, in String facility, in boolean lockState,
             in String password, in int serviceClass, in String appId);
@@ -379,6 +433,8 @@
      *
      * @param radioSimResponse Object containing response functions
      * @param radioSimIndication Object containing radio indications
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void setResponseFunctions(
             in IRadioSimResponse radioSimResponse, in IRadioSimIndication radioSimIndication);
@@ -409,6 +465,8 @@
      *                POWER_UP_PASS_THROUGH if powering up the SIM card in pass through mode
      *
      * Response function is IRadioSimResponse.setSimCardPowerResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void setSimCardPower(in int serial, in CardPowerState powerUp);
 
@@ -419,6 +477,8 @@
      * @param uiccSub SelectUiccSub
      *
      * Response function is IRadioSimResponse.setUiccSubscriptionResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void setUiccSubscription(in int serial, in SelectUiccSub uiccSub);
 
@@ -431,6 +491,8 @@
      * @param aid AID value, See ETSI 102.221 8.1 and 101.220 4, empty string if no value.
      *
      * Response function is IRadioSimResponse.supplyIccPin2ForAppResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void supplyIccPin2ForApp(in int serial, in String pin2, in String aid);
 
@@ -442,6 +504,8 @@
      * @param aid AID value, See ETSI 102.221 8.1 and 101.220 4, empty string if no value.
      *
      * Response function is IRadioSimResponse.supplyIccPinForAppResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void supplyIccPinForApp(in int serial, in String pin, in String aid);
 
@@ -454,6 +518,8 @@
      * @param aid AID value, See ETSI 102.221 8.1 and 101.220 4, empty string if no value.
      *
      * Response function is IRadioSimResponse.supplyIccPuk2ForAppResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void supplyIccPuk2ForApp(in int serial, in String puk2, in String pin2, in String aid);
 
@@ -466,6 +532,8 @@
      * @param aid AID value, See ETSI 102.221 8.1 and 101.220 4, empty string if no value.
      *
      * Response function is IRadioSimResponse.supplyIccPukForAppResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void supplyIccPukForApp(in int serial, in String puk, in String pin, in String aid);
 
@@ -481,6 +549,8 @@
      * @param controlKey the unlock code for removing persoType personalization from this device
      *
      * Response function is IRadioSimResponse.supplySimDepersonalizationResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void supplySimDepersonalization(
             in int serial, in PersoSubstate persoType, in String controlKey);
@@ -496,6 +566,8 @@
      * @param recordInfo Details of the record to insert, delete or update.
      *
      * Response function is IRadioSimResponse.updateSimPhonebookRecordsResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void updateSimPhonebookRecords(in int serial, in PhonebookRecordInfo recordInfo);
 
@@ -511,6 +583,8 @@
      * @param sessionInfo Details of the opened logical channel info like sessionId and isEs10.
      *
      * Response function is IRadioSimResponse.iccCloseLogicalChannelWithSessionInfoResponse()
+     *
+     * This is available when android.hardware.telephony.subscription is defined.
      */
     void iccCloseLogicalChannelWithSessionInfo(in int serial, in SessionInfo sessionInfo);
 }
diff --git a/radio/aidl/android/hardware/radio/sim/IRadioSimResponse.aidl b/radio/aidl/android/hardware/radio/sim/IRadioSimResponse.aidl
index 48253d4..91b5729 100644
--- a/radio/aidl/android/hardware/radio/sim/IRadioSimResponse.aidl
+++ b/radio/aidl/android/hardware/radio/sim/IRadioSimResponse.aidl
@@ -45,6 +45,8 @@
      * @param enabled whether Uicc applications are enabled.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:SIM_ABSENT
      *   RadioError:RADIO_NOT_AVAILABLE
@@ -57,6 +59,8 @@
      * @param remainingRetries Number of retries remaining, must be equal to -1 if unknown.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE (radio resetting)
      *   RadioError:PASSWORD_INCORRECT (old PIN2 is invalid)
@@ -75,6 +79,8 @@
      * @param remainingRetries Number of retries remaining, must be equal to -1 if unknown.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE (radio resetting)
      *   RadioError:PASSWORD_INCORRECT
@@ -91,6 +97,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:SIM_ABSENT
      *   RadioError:RADIO_NOT_AVAILABLE
@@ -105,6 +113,8 @@
      * @param multiSimPolicy Policy used for devices with multiple SIM cards.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      */
@@ -122,6 +132,7 @@
      * @param prl PRL version if CDMA subscription is available
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.cdma is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SUBSCRIPTION_NOT_AVAILABLE
@@ -143,6 +154,7 @@
      * @param source CDMA subscription source
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.cdma is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SUBSCRIPTION_NOT_AVAILABLE
@@ -161,6 +173,8 @@
      *        specified barring facility is active. "0" means "disabled for all"
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SS_MODIFIED_TO_DIAL
@@ -182,6 +196,8 @@
      * @param cardStatus ICC card status as defined by CardStatus
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -194,6 +210,8 @@
      * @param imsi String containing the IMSI
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE (radio resetting)
      *   RadioError:INTERNAL_ERR
@@ -210,6 +228,8 @@
      * @param capacity Response capacity enum indicating response processing status
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -223,6 +243,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -236,6 +258,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -252,6 +276,8 @@
      * @param iccIo ICC IO operation response
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SIM_PIN2
@@ -272,6 +298,8 @@
      *        byte per integer
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:MISSING_RESOURCE
@@ -292,6 +320,8 @@
      * @param result IccIoResult
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -306,6 +336,8 @@
      * @param result IccIoResult
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -319,6 +351,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -333,6 +367,8 @@
      * @param result IccIoResult
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
@@ -351,6 +387,8 @@
      *        byte of response
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SIM_BUSY
@@ -370,6 +408,8 @@
      * @param iccIo IccIoResult corresponding to ICC IO response
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SIM_BUSY
@@ -386,6 +426,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -403,6 +445,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -413,6 +457,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SIM_ABSENT
@@ -425,6 +471,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.cdma is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SIM_ABSENT
@@ -441,6 +488,8 @@
      * @param retry 0 is the number of retries remaining, or -1 if unknown
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SS_MODIFIED_TO_DIAL
@@ -463,6 +512,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -474,6 +525,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SUBSCRIPTION_NOT_SUPPORTED
@@ -492,6 +545,8 @@
      * @param remainingRetries Number of retries remaining, must be equal to -1 if unknown.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE (radio resetting)
      *   RadioError:PASSWORD_INCORRECT
@@ -510,6 +565,8 @@
      * @param remainingRetries Number of retries remaining, must be equal to -1 if unknown.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE (radio resetting)
      *   RadioError:PASSWORD_INCORRECT
@@ -527,6 +584,8 @@
      * @param remainingRetries Number of retries remaining, must be equal to -1 if unknown.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE (radio resetting)
      *   RadioError:PASSWORD_INCORRECT (PUK is invalid)
@@ -544,6 +603,8 @@
      * @param remainingRetries Number of retries remaining, must be equal to -1 if unknown.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE (radio resetting)
      *   RadioError:PASSWORD_INCORRECT (PUK is invalid)
@@ -563,6 +624,8 @@
      *        to -1 if number of retries is infinite.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:PASSWORD_INCORRECT (code is invalid)
@@ -583,6 +646,8 @@
      *        the minimum value is 1
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -599,6 +664,8 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.subscription is not
+     *                                    defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INTERNAL_ERR
diff --git a/radio/aidl/android/hardware/radio/voice/IRadioVoice.aidl b/radio/aidl/android/hardware/radio/voice/IRadioVoice.aidl
index bf9b9cb..0c2b51d 100644
--- a/radio/aidl/android/hardware/radio/voice/IRadioVoice.aidl
+++ b/radio/aidl/android/hardware/radio/voice/IRadioVoice.aidl
@@ -41,6 +41,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioVoiceResponse.acceptCallResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void acceptCall(in int serial);
 
@@ -50,6 +52,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioVoiceResponse.cancelPendingUssdResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void cancelPendingUssd(in int serial);
 
@@ -59,6 +63,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioVoiceResponse.conferenceResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void conference(in int serial);
 
@@ -69,6 +75,8 @@
      * @param dialInfo Dial struct
      *
      * Response function is IRadioVoiceResponse.dialResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void dial(in int serial, in Dial dialInfo);
 
@@ -125,6 +133,8 @@
      * @param isTesting Flag indicating if this request is for testing purpose.
      *
      * Response function is IRadioVoiceResponse.emergencyDialResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void emergencyDial(in int serial, in Dial dialInfo, in int categories, in String[] urns,
             in EmergencyCallRouting routing, in boolean hasKnownUserIntentEmergency,
@@ -137,6 +147,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioVoiceResponse.exitEmergencyCallbackModeResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void exitEmergencyCallbackMode(in int serial);
 
@@ -146,6 +158,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioVoiceResponse.explicitCallTransferResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void explicitCallTransfer(in int serial);
 
@@ -156,6 +170,8 @@
      * @param callInfo CallForwardInfo
      *
      * Response function is IRadioVoiceResponse.getCallForwardStatusResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void getCallForwardStatus(in int serial, in CallForwardInfo callInfo);
 
@@ -166,6 +182,8 @@
      * @param serviceClass Service class is the TS 27.007 service class to query
      *
      * Response function is IRadioVoiceResponse.getCallWaitingResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void getCallWaiting(in int serial, in int serviceClass);
 
@@ -175,6 +193,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioVoiceResponse.getClipResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void getClip(in int serial);
 
@@ -184,6 +204,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioVoiceResponse.getClirResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void getClir(in int serial);
 
@@ -193,6 +215,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioVoiceResponse.getCurrentCallsResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void getCurrentCalls(in int serial);
 
@@ -202,6 +226,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioVoiceResponse.getLastCallFailCauseResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void getLastCallFailCause(in int serial);
 
@@ -211,6 +237,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioVoiceResponse.getMuteResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void getMute(in int serial);
 
@@ -220,6 +248,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioVoiceResponse.getPreferredVoicePrivacyResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void getPreferredVoicePrivacy(in int serial);
 
@@ -229,6 +259,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioVoiceResponse.getTtyModeResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void getTtyMode(in int serial);
 
@@ -241,6 +273,8 @@
      * @param accept true = accept the call setup, false = reject the call setup
      *
      * Response function is IRadioVoiceResponse.handleStkCallSetupRequestFromSimResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void handleStkCallSetupRequestFromSim(in int serial, in boolean accept);
 
@@ -252,6 +286,8 @@
      * @param gsmIndex Connection index (value of 'x' in CHLD above)
      *
      * Response function is IRadioVoiceResponse.hangupResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void hangup(in int serial, in int gsmIndex);
 
@@ -262,6 +298,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioVoiceResponse.hangupForegroundResumeBackgroundResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void hangupForegroundResumeBackground(in int serial);
 
@@ -272,6 +310,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioVoiceResponse.hangupWaitingOrBackgroundResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void hangupWaitingOrBackground(in int serial);
 
@@ -281,6 +321,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioVoiceResponse.isVoNrEnabledResponse()
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void isVoNrEnabled(in int serial);
 
@@ -290,6 +332,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioVoiceResponse.rejectCallResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void rejectCall(in int serial);
 
@@ -297,6 +341,8 @@
      * When response type received from a radio indication or radio response is
      * RadioIndicationType:UNSOLICITED_ACK_EXP or RadioResponseType:SOLICITED_ACK_EXP respectively,
      * acknowledge the receipt of those messages by sending responseAcknowledgement().
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void responseAcknowledgement();
 
@@ -309,6 +355,8 @@
      * @param off is the DTMF OFF length in milliseconds, or 0 to use default
      *
      * Response function is IRadioVoiceResponse.sendBurstDtmfResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void sendBurstDtmf(in int serial, in String dtmf, in int on, in int off);
 
@@ -319,6 +367,8 @@
      * @param featureCode String associated with Flash command
      *
      * Response function is IRadioVoiceResponse.sendCdmaFeatureCodeResponse()
+     *
+     * This is available when android.hardware.telephony.cdma is defined.
      */
     void sendCdmaFeatureCode(in int serial, in String featureCode);
 
@@ -330,6 +380,8 @@
      * @param s string with single char having one of 12 values: 0-9, *, #
      *
      * Response function is IRadioVoiceResponse.sendDtmfResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void sendDtmf(in int serial, in String s);
 
@@ -347,6 +399,8 @@
      * @param ussd string containing the USSD request in UTF-8 format
      *
      * Response function is IRadioVoiceResponse.sendUssdResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void sendUssd(in int serial, in String ussd);
 
@@ -363,6 +417,8 @@
      * @param gsmIndex contains Connection index (value of 'x' in CHLD above)
      *
      * Response function is IRadioVoiceResponse.separateConnectionResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void separateConnection(in int serial, in int gsmIndex);
 
@@ -373,6 +429,8 @@
      * @param callInfo CallForwardInfo
      *
      * Response function is IRadioVoiceResponse.setCallForwardResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void setCallForward(in int serial, in CallForwardInfo callInfo);
 
@@ -384,6 +442,8 @@
      * @param serviceClass is the TS 27.007 service class bit vector of services to modify
      *
      * Response function is IRadioVoiceResponse.setCallWaitingResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void setCallWaiting(in int serial, in boolean enable, in int serviceClass);
 
@@ -394,6 +454,8 @@
      * @param status "n" parameter from TS 27.007 7.7
      *
      * Response function is IRadioVoiceResponse.setClirResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void setClir(in int serial, in int status);
 
@@ -405,6 +467,8 @@
      * @param enable true for "enable mute" and false for "disable mute"
      *
      * Response function is IRadioVoiceResponse.setMuteResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void setMute(in int serial, in boolean enable);
 
@@ -416,6 +480,8 @@
      *        true for Enhanced Privacy Mode (Private Long Code Mask)
      *
      * Response function is IRadioVoiceResponse.setPreferredVoicePrivacyResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void setPreferredVoicePrivacy(in int serial, in boolean enable);
 
@@ -424,6 +490,8 @@
      *
      * @param radioVoiceResponse Object containing response functions
      * @param radioVoiceIndication Object containing radio indications
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void setResponseFunctions(in IRadioVoiceResponse radioVoiceResponse,
             in IRadioVoiceIndication radioVoiceIndication);
@@ -435,6 +503,8 @@
      * @param mode TtyMode
      *
      * Response function is IRadioVoiceResponse.setTtyModeResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void setTtyMode(in int serial, in TtyMode mode);
 
@@ -445,6 +515,8 @@
      * @param enable true for "enable vonr" and false for "disable vonr"
      *
      * Response function is IRadioVoiceResponse.setVoNrEnabledResponse()
+     *
+     * This is available when android.hardware.telephony.ims is defined.
      */
     void setVoNrEnabled(in int serial, in boolean enable);
 
@@ -457,6 +529,8 @@
      * @param s string having a single character with one of 12 values: 0-9,*,#
      *
      * Response function is IRadioVoiceResponse.startDtmfResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void startDtmf(in int serial, in String s);
 
@@ -466,6 +540,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioVoiceResponse.stopDtmfResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void stopDtmf(in int serial);
 
@@ -483,6 +559,8 @@
      * @param serial Serial number of request.
      *
      * Response function is IRadioVoiceResponse.switchWaitingOrHoldingAndActiveResponse()
+     *
+     * This is available when android.hardware.telephony.calling is defined.
      */
     void switchWaitingOrHoldingAndActive(in int serial);
 }
diff --git a/radio/aidl/android/hardware/radio/voice/IRadioVoiceResponse.aidl b/radio/aidl/android/hardware/radio/voice/IRadioVoiceResponse.aidl
index 21264df..a904eaa 100644
--- a/radio/aidl/android/hardware/radio/voice/IRadioVoiceResponse.aidl
+++ b/radio/aidl/android/hardware/radio/voice/IRadioVoiceResponse.aidl
@@ -33,6 +33,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_STATE
@@ -61,6 +62,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SIM_BUSY
@@ -81,6 +83,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE (radio resetting)
      *   RadioError:NO_MEMORY
@@ -101,6 +104,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE (radio resetting)
      *   RadioError:DIAL_MODIFIED_TO_USSD
@@ -129,6 +133,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE (radio resetting)
      *   RadioError:DIAL_MODIFIED_TO_USSD
@@ -152,6 +157,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:OPERATION_NO_ALLOWED
@@ -170,6 +176,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -196,6 +203,7 @@
      *        CallForwardInfo must be returned with the service class set to "data + voice = 3".
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SS_MODIFIED_TO_DIAL
@@ -223,6 +231,7 @@
      *        for data and voice and disabled for everything else.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SS_MODIFIED_TO_DIAL
@@ -245,6 +254,7 @@
      * @param status indicates CLIP status
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -264,6 +274,7 @@
      * @param m is "m" parameter from TS 27.007 7.7
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SS_MODIFIED_TO_DIAL
@@ -285,6 +296,7 @@
      * @param calls Current call list
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NO_MEMORY
      *   RadioError:INTERNAL_ERR
      *   RadioError:SYSTEM_ERR
@@ -327,6 +339,7 @@
      * for tone generation or error notification.
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:NO_MEMORY
      *   RadioError:RADIO_NOT_AVAILABLE
@@ -346,6 +359,7 @@
      * @param enable true for "mute enabled" and false for "mute disabled"
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SS_MODIFIED_TO_DIAL
@@ -367,6 +381,7 @@
      *        true for Enhanced Privacy Mode (Private Long Code Mask)
      *
      * Valid errors:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -384,6 +399,7 @@
      * @param mode TtyMode
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -400,6 +416,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:NO_MEMORY
@@ -417,6 +434,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE (radio resetting)
      *   RadioError:INVALID_ARGUMENTS
@@ -434,6 +452,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE (radio resetting)
      *   RadioError:INVALID_STATE
@@ -454,6 +473,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE (radio resetting)
      *   RadioError:INVALID_STATE
@@ -474,6 +494,7 @@
      * @param enable true for "vonr enabled" and false for "vonr disabled"
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.ims is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:MODEM_ERR
@@ -486,6 +507,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE (radio resetting)
      *   RadioError:INVALID_STATE
@@ -507,6 +529,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -527,6 +550,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.cdma is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -547,6 +571,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -565,6 +590,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:FDN_CHECK_FAILURE
@@ -590,6 +616,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -610,6 +637,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SS_MODIFIED_TO_DIAL
@@ -632,6 +660,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:SS_MODIFIED_TO_DIAL
@@ -654,6 +683,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:INVALID_ARGUMENTS
      *   RadioError:RADIO_NOT_AVAILABLE
@@ -672,6 +702,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -688,6 +719,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -705,6 +737,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -721,6 +754,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -734,6 +768,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -753,6 +788,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_ARGUMENTS
@@ -772,6 +808,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED when android.hardware.telephony.calling is not defined
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE (radio resetting)
      *   RadioError:INVALID_STATE
diff --git a/radio/aidl/compat/libradiocompat/Android.bp b/radio/aidl/compat/libradiocompat/Android.bp
index f5000b9..66970db 100644
--- a/radio/aidl/compat/libradiocompat/Android.bp
+++ b/radio/aidl/compat/libradiocompat/Android.bp
@@ -43,7 +43,6 @@
         "android.hardware.radio.modem-V3-ndk",
         "android.hardware.radio.network-V3-ndk",
         "android.hardware.radio.sap-V1-ndk",
-        "android.hardware.radio.satellite-V1-ndk",
         "android.hardware.radio.sim-V3-ndk",
         "android.hardware.radio.voice-V3-ndk",
         "android.hardware.radio@1.0",
@@ -94,9 +93,6 @@
         "sap/Sap.cpp",
         "sap/SapCallback.cpp",
         "sap/structs.cpp",
-        "satellite/RadioIndication-satellite.cpp",
-        "satellite/RadioResponse-satellite.cpp",
-        "satellite/RadioSatellite.cpp",
         "sim/RadioIndication-sim.cpp",
         "sim/RadioResponse-sim.cpp",
         "sim/RadioSim.cpp",
diff --git a/radio/aidl/compat/libradiocompat/ims/media/RadioImsMediaSession.cpp b/radio/aidl/compat/libradiocompat/ims/media/RadioImsMediaSession.cpp
index ae86914..d509300 100644
--- a/radio/aidl/compat/libradiocompat/ims/media/RadioImsMediaSession.cpp
+++ b/radio/aidl/compat/libradiocompat/ims/media/RadioImsMediaSession.cpp
@@ -60,5 +60,12 @@
     LOG(ERROR) << " setMediaQualityThreshold is unsupported by HIDL HALs";
     return ok();
 }
-
+ScopedAStatus RadioImsMediaSession::requestRtpReceptionStats(int32_t /*in_intervalMs*/) {
+    LOG(ERROR) << " requestRtpReceptionStats is unsupported by HIDL HALs";
+    return ok();
+}
+ScopedAStatus RadioImsMediaSession::adjustDelay(int32_t /*in_delayMs*/) {
+    LOG(ERROR) << " adjustDelay is unsupported by HIDL HALs";
+    return ok();
+}
 }  // namespace android::hardware::radio::compat
diff --git a/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioImsMediaSession.h b/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioImsMediaSession.h
index 00f21fc..715fc77 100644
--- a/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioImsMediaSession.h
+++ b/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioImsMediaSession.h
@@ -38,6 +38,8 @@
     ::ndk::ScopedAStatus setMediaQualityThreshold(
             const ::aidl::android::hardware::radio::ims::media::MediaQualityThreshold& in_threshold)
             override;
+    ::ndk::ScopedAStatus requestRtpReceptionStats(int32_t in_intervalMs) override;
+    ::ndk::ScopedAStatus adjustDelay(int32_t in_delayMs) override;
 
   protected:
   public:
diff --git a/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioIndication.h b/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioIndication.h
index ad9127e..e6f2516 100644
--- a/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioIndication.h
+++ b/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioIndication.h
@@ -23,10 +23,10 @@
 #include <aidl/android/hardware/radio/messaging/IRadioMessagingIndication.h>
 #include <aidl/android/hardware/radio/modem/IRadioModemIndication.h>
 #include <aidl/android/hardware/radio/network/IRadioNetworkIndication.h>
-#include <aidl/android/hardware/radio/satellite/IRadioSatelliteIndication.h>
 #include <aidl/android/hardware/radio/sim/IRadioSimIndication.h>
 #include <aidl/android/hardware/radio/voice/IRadioVoiceIndication.h>
 #include <android/hardware/radio/1.6/IRadioIndication.h>
+#include <aidl/android/hardware/radio/modem/ImeiInfo.h>
 
 namespace android::hardware::radio::compat {
 
@@ -61,10 +61,6 @@
             ::aidl::android::hardware::radio::ims::IRadioImsIndication,
             ::aidl::android::hardware::radio::ims::IRadioImsIndicationDefault, true>
             mImsCb;
-    GuaranteedCallback<  //
-            ::aidl::android::hardware::radio::satellite::IRadioSatelliteIndication,
-            ::aidl::android::hardware::radio::satellite::IRadioSatelliteIndicationDefault, true>
-            mSatelliteCb;
 
     // IRadioIndication @ 1.0
     Return<void> radioStateChanged(V1_0::RadioIndicationType type,
@@ -213,7 +209,8 @@
     Return<void> simPhonebookRecordsReceived(
             V1_0::RadioIndicationType type, V1_6::PbReceivedStatus status,
             const hidl_vec<V1_6::PhonebookRecordInfo>& records) override;
-
+    Return<void> onImeiMappingChanged(V1_0::RadioIndicationType type,
+                                      ::aidl::android::hardware::radio::modem::ImeiInfo config);
   public:
     RadioIndication(std::shared_ptr<DriverContext> context);
 
@@ -232,9 +229,6 @@
             std::shared_ptr<::aidl::android::hardware::radio::voice::IRadioVoiceIndication> voicCb);
     void setResponseFunction(
             std::shared_ptr<::aidl::android::hardware::radio::ims::IRadioImsIndication> imsCb);
-    void setResponseFunction(
-            std::shared_ptr<::aidl::android::hardware::radio::satellite::IRadioSatelliteIndication>
-                    satelliteCb);
 
     std::shared_ptr<::aidl::android::hardware::radio::data::IRadioDataIndication> dataCb();
     std::shared_ptr<::aidl::android::hardware::radio::messaging::IRadioMessagingIndication>
@@ -244,8 +238,6 @@
     std::shared_ptr<::aidl::android::hardware::radio::sim::IRadioSimIndication> simCb();
     std::shared_ptr<::aidl::android::hardware::radio::voice::IRadioVoiceIndication> voiceCb();
     std::shared_ptr<::aidl::android::hardware::radio::ims::IRadioImsIndication> imsCb();
-    std::shared_ptr<::aidl::android::hardware::radio::satellite::IRadioSatelliteIndication>
-    satelliteCb();
 };
 
 }  // namespace android::hardware::radio::compat
diff --git a/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioNetwork.h b/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioNetwork.h
index d57c83d..56724ae 100644
--- a/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioNetwork.h
+++ b/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioNetwork.h
@@ -107,6 +107,12 @@
 
     ::ndk::ScopedAStatus setNullCipherAndIntegrityEnabled(int32_t serial, bool enabled) override;
     ::ndk::ScopedAStatus isNullCipherAndIntegrityEnabled(int32_t serial) override;
+    ::ndk::ScopedAStatus isCellularIdentifierTransparencyEnabled(int32_t serial) override;
+    ::ndk::ScopedAStatus setCellularIdentifierTransparencyEnabled(int32_t serial,
+                                                                  bool enabled) override;
+
+    ::ndk::ScopedAStatus setSecurityAlgorithmsUpdatedEnabled(int32_t serial, bool enabled) override;
+    ::ndk::ScopedAStatus isSecurityAlgorithmsUpdatedEnabled(int32_t serial) override;
 
   protected:
     std::shared_ptr<::aidl::android::hardware::radio::network::IRadioNetworkResponse> respond();
diff --git a/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioResponse.h b/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioResponse.h
index b976435..e20eed9 100644
--- a/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioResponse.h
+++ b/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioResponse.h
@@ -24,7 +24,6 @@
 #include <aidl/android/hardware/radio/messaging/IRadioMessagingResponse.h>
 #include <aidl/android/hardware/radio/modem/IRadioModemResponse.h>
 #include <aidl/android/hardware/radio/network/IRadioNetworkResponse.h>
-#include <aidl/android/hardware/radio/satellite/IRadioSatelliteResponse.h>
 #include <aidl/android/hardware/radio/sim/IRadioSimResponse.h>
 #include <aidl/android/hardware/radio/voice/IRadioVoiceResponse.h>
 #include <android/hardware/radio/1.6/IRadioResponse.h>
@@ -55,9 +54,6 @@
     GuaranteedCallback<::aidl::android::hardware::radio::ims::IRadioImsResponse,
                        ::aidl::android::hardware::radio::ims::IRadioImsResponseDefault>
             mImsCb;
-    GuaranteedCallback<::aidl::android::hardware::radio::satellite::IRadioSatelliteResponse,
-                       ::aidl::android::hardware::radio::satellite::IRadioSatelliteResponseDefault>
-            mSatelliteCb;
 
     // IRadioResponse @ 1.0
     Return<void> getIccCardStatusResponse(const V1_0::RadioResponseInfo& info,
@@ -451,9 +447,6 @@
             std::shared_ptr<::aidl::android::hardware::radio::voice::IRadioVoiceResponse> voiceCb);
     void setResponseFunction(
             std::shared_ptr<::aidl::android::hardware::radio::ims::IRadioImsResponse> imsCb);
-    void setResponseFunction(
-            std::shared_ptr<::aidl::android::hardware::radio::satellite::IRadioSatelliteResponse>
-                    satelliteCb);
 
     std::shared_ptr<::aidl::android::hardware::radio::data::IRadioDataResponse> dataCb();
     std::shared_ptr<::aidl::android::hardware::radio::messaging::IRadioMessagingResponse>
@@ -463,8 +456,6 @@
     std::shared_ptr<::aidl::android::hardware::radio::sim::IRadioSimResponse> simCb();
     std::shared_ptr<::aidl::android::hardware::radio::voice::IRadioVoiceResponse> voiceCb();
     std::shared_ptr<::aidl::android::hardware::radio::ims::IRadioImsResponse> imsCb();
-    std::shared_ptr<::aidl::android::hardware::radio::satellite::IRadioSatelliteResponse>
-    satelliteCb();
 };
 
 }  // namespace android::hardware::radio::compat
diff --git a/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioSatellite.h b/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioSatellite.h
deleted file mode 100644
index 3ee6db2..0000000
--- a/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioSatellite.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include "RadioCompatBase.h"
-
-#include <aidl/android/hardware/radio/satellite/BnRadioSatellite.h>
-
-namespace android::hardware::radio::compat {
-
-class RadioSatellite : public RadioCompatBase,
-                       public aidl::android::hardware::radio::satellite::BnRadioSatellite {
-    ::ndk::ScopedAStatus responseAcknowledgement() override;
-    ::ndk::ScopedAStatus getCapabilities(int32_t serial) override;
-    ::ndk::ScopedAStatus setPower(int32_t serial, bool on) override;
-    ::ndk::ScopedAStatus getPowerState(int32_t serial) override;
-    ::ndk::ScopedAStatus provisionService(
-            int32_t serial, const std::string& imei, const std::string& msisdn,
-            const std::string& imsi,
-            const std::vector<::aidl::android::hardware::radio::satellite::SatelliteFeature>&
-                    features) override;
-    ::ndk::ScopedAStatus addAllowedSatelliteContacts(
-            int32_t serial, const std::vector<std::string>& contacts) override;
-    ::ndk::ScopedAStatus removeAllowedSatelliteContacts(
-            int32_t serial, const std::vector<std::string>& contacts) override;
-    ::ndk::ScopedAStatus sendMessages(int32_t serial, const std::vector<std::string>& messages,
-                                      const std::string& destination, double latitude,
-                                      double longitude) override;
-    ::ndk::ScopedAStatus getPendingMessages(int32_t serial) override;
-    ::ndk::ScopedAStatus getSatelliteMode(int32_t serial) override;
-    ::ndk::ScopedAStatus setIndicationFilter(int32_t serial, int32_t filterBitmask) override;
-    ::ndk::ScopedAStatus startSendingSatellitePointingInfo(int32_t serial) override;
-    ::ndk::ScopedAStatus stopSendingSatellitePointingInfo(int32_t serial) override;
-    ::ndk::ScopedAStatus getMaxCharactersPerTextMessage(int32_t serial) override;
-    ::ndk::ScopedAStatus getTimeForNextSatelliteVisibility(int32_t serial) override;
-
-    ::ndk::ScopedAStatus setResponseFunctions(
-            const std::shared_ptr<
-                    ::aidl::android::hardware::radio::satellite::IRadioSatelliteResponse>&
-                    radioSatelliteResponse,
-            const std::shared_ptr<
-                    ::aidl::android::hardware::radio::satellite::IRadioSatelliteIndication>&
-                    radioSatelliteIndication) override;
-
-  protected:
-    std::shared_ptr<::aidl::android::hardware::radio::satellite::IRadioSatelliteResponse> respond();
-
-  public:
-    using RadioCompatBase::RadioCompatBase;
-};
-
-}  // namespace android::hardware::radio::compat
diff --git a/radio/aidl/compat/libradiocompat/modem/RadioIndication-modem.cpp b/radio/aidl/compat/libradiocompat/modem/RadioIndication-modem.cpp
index 851c93b..990ccff 100644
--- a/radio/aidl/compat/libradiocompat/modem/RadioIndication-modem.cpp
+++ b/radio/aidl/compat/libradiocompat/modem/RadioIndication-modem.cpp
@@ -68,4 +68,11 @@
     return {};
 }
 
+Return<void> RadioIndication::onImeiMappingChanged(V1_0::RadioIndicationType type,
+                                    ::aidl::android::hardware::radio::modem::ImeiInfo imeiInfo) {
+    LOG_CALL << type;
+    modemCb()->onImeiMappingChanged(toAidl(type), imeiInfo);
+    return {};
+}
+
 }  // namespace android::hardware::radio::compat
diff --git a/radio/aidl/compat/libradiocompat/network/RadioNetwork.cpp b/radio/aidl/compat/libradiocompat/network/RadioNetwork.cpp
index a379eec..1e43789 100644
--- a/radio/aidl/compat/libradiocompat/network/RadioNetwork.cpp
+++ b/radio/aidl/compat/libradiocompat/network/RadioNetwork.cpp
@@ -372,4 +372,34 @@
     respond()->setN1ModeEnabledResponse(notSupported(serial));
     return ok();
 }
+
+ScopedAStatus RadioNetwork::isCellularIdentifierTransparencyEnabled(int32_t serial) {
+    LOG_CALL << serial;
+    LOG(ERROR) << " isCellularIdentifierTransparencyEnabled is unsupported by HIDL HALs";
+    respond()->isCellularIdentifierTransparencyEnabledResponse(notSupported(serial), false);
+    return ok();
+}
+
+ScopedAStatus RadioNetwork::setCellularIdentifierTransparencyEnabled(int32_t serial,
+                                                                     bool /*enabled*/) {
+    LOG_CALL << serial;
+    LOG(ERROR) << " setCellularIdentifierTransparencyEnabled is unsupported by HIDL HALs";
+    respond()->setCellularIdentifierTransparencyEnabledResponse(notSupported(serial));
+    return ok();
+}
+
+ScopedAStatus RadioNetwork::isSecurityAlgorithmsUpdatedEnabled(int32_t serial) {
+    LOG_CALL << serial;
+    LOG(ERROR) << " isSecurityAlgorithmsUpdatedEnabled is unsupported by HIDL HALs";
+    respond()->isSecurityAlgorithmsUpdatedEnabledResponse(notSupported(serial), false);
+    return ok();
+}
+
+ScopedAStatus RadioNetwork::setSecurityAlgorithmsUpdatedEnabled(int32_t serial, bool /*enable*/) {
+    LOG_CALL << serial;
+    LOG(ERROR) << " setSecurityAlgorithmsUpdatedEnabled is unsupported by HIDL HALs";
+    respond()->setSecurityAlgorithmsUpdatedEnabledResponse(notSupported(serial));
+    return ok();
+}
+
 }  // namespace android::hardware::radio::compat
diff --git a/radio/aidl/compat/libradiocompat/satellite/RadioIndication-satellite.cpp b/radio/aidl/compat/libradiocompat/satellite/RadioIndication-satellite.cpp
deleted file mode 100644
index 39da43d..0000000
--- a/radio/aidl/compat/libradiocompat/satellite/RadioIndication-satellite.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <libradiocompat/RadioIndication.h>
-
-#include "commonStructs.h"
-#include "debug.h"
-
-#include "collections.h"
-
-#define RADIO_MODULE "RadioSatelliteIndication"
-
-namespace android::hardware::radio::compat {
-
-namespace aidl = ::aidl::android::hardware::radio::satellite;
-
-void RadioIndication::setResponseFunction(
-        std::shared_ptr<aidl::IRadioSatelliteIndication> satelliteCb) {
-    mSatelliteCb = satelliteCb;
-}
-
-std::shared_ptr<aidl::IRadioSatelliteIndication> RadioIndication::satelliteCb() {
-    return mSatelliteCb.get();
-}
-
-}  // namespace android::hardware::radio::compat
diff --git a/radio/aidl/compat/libradiocompat/satellite/RadioResponse-satellite.cpp b/radio/aidl/compat/libradiocompat/satellite/RadioResponse-satellite.cpp
deleted file mode 100644
index 2209c93..0000000
--- a/radio/aidl/compat/libradiocompat/satellite/RadioResponse-satellite.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <libradiocompat/RadioResponse.h>
-
-#include "commonStructs.h"
-#include "debug.h"
-
-#include "collections.h"
-
-#define RADIO_MODULE "RadioSatelliteResponse"
-
-namespace android::hardware::radio::compat {
-
-namespace aidl = ::aidl::android::hardware::radio::satellite;
-
-void RadioResponse::setResponseFunction(
-        std::shared_ptr<aidl::IRadioSatelliteResponse> satelliteCb) {
-    mSatelliteCb = satelliteCb;
-}
-
-std::shared_ptr<aidl::IRadioSatelliteResponse> RadioResponse::satelliteCb() {
-    return mSatelliteCb.get();
-}
-
-}  // namespace android::hardware::radio::compat
diff --git a/radio/aidl/compat/libradiocompat/satellite/RadioSatellite.cpp b/radio/aidl/compat/libradiocompat/satellite/RadioSatellite.cpp
deleted file mode 100644
index 16a3167..0000000
--- a/radio/aidl/compat/libradiocompat/satellite/RadioSatellite.cpp
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <libradiocompat/RadioSatellite.h>
-
-#include "commonStructs.h"
-#include "debug.h"
-
-#include "collections.h"
-
-#define RADIO_MODULE "RadioSatellite"
-
-namespace android::hardware::radio::compat {
-
-using ::ndk::ScopedAStatus;
-namespace aidl = ::aidl::android::hardware::radio::satellite;
-constexpr auto ok = &ScopedAStatus::ok;
-
-std::shared_ptr<aidl::IRadioSatelliteResponse> RadioSatellite::respond() {
-    return mCallbackManager->response().satelliteCb();
-}
-
-ScopedAStatus RadioSatellite::responseAcknowledgement() {
-    LOG(ERROR) << " responseAcknowledgement is unsupported by HIDL HALs";
-    return ok();
-}
-ScopedAStatus RadioSatellite::getCapabilities(int32_t serial) {
-    LOG_CALL << serial;
-    LOG(ERROR) << " getCapabilities is unsupported by HIDL HALs";
-    return ok();
-}
-ScopedAStatus RadioSatellite::setPower(int32_t serial, bool /*on*/) {
-    LOG_CALL << serial;
-    LOG(ERROR) << " setPower is unsupported by HIDL HALs";
-    return ok();
-}
-ScopedAStatus RadioSatellite::getPowerState(int32_t serial) {
-    LOG_CALL << serial;
-    LOG(ERROR) << " getPowerSate is unsupported by HIDL HALs";
-    return ok();
-}
-ScopedAStatus RadioSatellite::provisionService(
-        int32_t serial, const std::string& /*imei*/, const std::string& /*msisdn*/,
-        const std::string& /*imsi*/,
-        const std::vector<
-                ::aidl::android::hardware::radio::satellite::SatelliteFeature>& /*features*/) {
-    LOG_CALL << serial;
-    LOG(ERROR) << " provisionService is unsupported by HIDL HALs";
-    return ok();
-}
-ScopedAStatus RadioSatellite::addAllowedSatelliteContacts(
-        int32_t serial, const std::vector<std::string>& /*contacts*/) {
-    LOG_CALL << serial;
-    LOG(ERROR) << " addAllowedSatelliteContacts is unsupported by HIDL HALs";
-    return ok();
-}
-ScopedAStatus RadioSatellite::removeAllowedSatelliteContacts(
-        int32_t serial, const std::vector<std::string>& /*contacts*/) {
-    LOG_CALL << serial;
-    LOG(ERROR) << " removeAllowedSatelliteContacts is unsupported by HIDL HALs";
-    return ok();
-}
-ScopedAStatus RadioSatellite::sendMessages(int32_t serial,
-                                           const std::vector<std::string>& /*messages*/,
-                                           const std::string& /*destination*/, double /*latitude*/,
-                                           double /*longitude*/) {
-    LOG_CALL << serial;
-    LOG(ERROR) << " sendMessage is unsupported by HIDL HALs";
-    return ok();
-}
-ScopedAStatus RadioSatellite::getPendingMessages(int32_t serial) {
-    LOG_CALL << serial;
-    LOG(ERROR) << " getPendingMessages is unsupported by HIDL HALs";
-    return ok();
-}
-ScopedAStatus RadioSatellite::getSatelliteMode(int32_t serial) {
-    LOG_CALL << serial;
-    LOG(ERROR) << " getSatelliteMode is unsupported by HIDL HALs";
-    return ok();
-}
-ScopedAStatus RadioSatellite::setIndicationFilter(int32_t serial, int32_t /*filterBitmask*/) {
-    LOG_CALL << serial;
-    LOG(ERROR) << " setIndicationFilter is unsupported by HIDL HALs";
-    return ok();
-}
-ScopedAStatus RadioSatellite::startSendingSatellitePointingInfo(int32_t serial) {
-    LOG_CALL << serial;
-    LOG(ERROR) << " startSendingSatellitePointingInfo is unsupported by HIDL HALs";
-    return ok();
-}
-ScopedAStatus RadioSatellite::stopSendingSatellitePointingInfo(int32_t serial) {
-    LOG_CALL << serial;
-    LOG(ERROR) << " stopSendingSatellitePointingInfo is unsupported by HIDL HALs";
-    return ok();
-}
-ScopedAStatus RadioSatellite::getMaxCharactersPerTextMessage(int32_t serial) {
-    LOG_CALL << serial;
-    LOG(ERROR) << " getMaxCharactersPerTextMessage is unsupported by HIDL HALs";
-    return ok();
-}
-ScopedAStatus RadioSatellite::getTimeForNextSatelliteVisibility(int32_t serial) {
-    LOG_CALL << serial;
-    LOG(ERROR) << " getTimeForNextSatelliteVisibility is unsupported by HIDL HALs";
-    return ok();
-}
-
-ScopedAStatus RadioSatellite::setResponseFunctions(
-        const std::shared_ptr<aidl::IRadioSatelliteResponse>& response,
-        const std::shared_ptr<aidl::IRadioSatelliteIndication>& indication) {
-    LOG_CALL << response << ' ' << indication;
-    mCallbackManager->setResponseFunctions(response, indication);
-    return ok();
-}
-
-}  // namespace android::hardware::radio::compat
diff --git a/radio/aidl/compat/service/Android.bp b/radio/aidl/compat/service/Android.bp
index 1a6ea48..62c99fe 100644
--- a/radio/aidl/compat/service/Android.bp
+++ b/radio/aidl/compat/service/Android.bp
@@ -46,7 +46,6 @@
         "android.hardware.radio.modem-V3-ndk",
         "android.hardware.radio.network-V3-ndk",
         "android.hardware.radio.sap-V1-ndk",
-        "android.hardware.radio.satellite-V1-ndk",
         "android.hardware.radio.sim-V3-ndk",
         "android.hardware.radio.voice-V3-ndk",
         "android.hardware.radio@1.0",
diff --git a/radio/aidl/vts/Android.bp b/radio/aidl/vts/Android.bp
index 8301f10..d985686 100644
--- a/radio/aidl/vts/Android.bp
+++ b/radio/aidl/vts/Android.bp
@@ -58,9 +58,6 @@
         "radio_network_test.cpp",
         "radio_sap_callback.cpp",
         "radio_sap_test.cpp",
-        "radio_satellite_indication.cpp",
-        "radio_satellite_response.cpp",
-        "radio_satellite_test.cpp",
         "radio_sim_indication.cpp",
         "radio_sim_response.cpp",
         "radio_sim_test.cpp",
@@ -69,9 +66,13 @@
         "radio_voice_test.cpp",
         "VtsHalRadioTargetTest.cpp",
     ],
+    header_libs: [
+        "jni_headers",
+    ],
     shared_libs: [
         "libbinder_ndk",
         "libvintf",
+        "server_configurable_flags",
     ],
     static_libs: [
         "android.hardware.radio-V3-ndk",
@@ -83,9 +84,9 @@
         "android.hardware.radio.modem-V3-ndk",
         "android.hardware.radio.network-V3-ndk",
         "android.hardware.radio.sap-V1-ndk",
-        "android.hardware.radio.satellite-V1-ndk",
         "android.hardware.radio.sim-V3-ndk",
         "android.hardware.radio.voice-V3-ndk",
+        "telephony_flags_c_lib",
     ],
     test_suites: [
         "general-tests",
diff --git a/radio/aidl/vts/VtsHalRadioTargetTest.cpp b/radio/aidl/vts/VtsHalRadioTargetTest.cpp
index 86c1099..c04173b 100644
--- a/radio/aidl/vts/VtsHalRadioTargetTest.cpp
+++ b/radio/aidl/vts/VtsHalRadioTargetTest.cpp
@@ -24,7 +24,6 @@
 #include "radio_modem_utils.h"
 #include "radio_network_utils.h"
 #include "radio_sap_utils.h"
-#include "radio_satellite_utils.h"
 #include "radio_sim_utils.h"
 #include "radio_voice_utils.h"
 
@@ -80,12 +79,6 @@
         testing::ValuesIn(android::getAidlHalInstanceNames(IRadioIms::descriptor)),
         android::PrintInstanceNameToString);
 
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RadioSatelliteTest);
-INSTANTIATE_TEST_SUITE_P(
-        PerInstance, RadioSatelliteTest,
-        testing::ValuesIn(android::getAidlHalInstanceNames(IRadioSatellite::descriptor)),
-        android::PrintInstanceNameToString);
-
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RadioImsMediaTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, RadioImsMediaTest,
                          testing::ValuesIn(android::getAidlHalInstanceNames(IImsMedia::descriptor)),
diff --git a/radio/aidl/vts/radio_aidl_hal_utils.h b/radio/aidl/vts/radio_aidl_hal_utils.h
index 1690e89..aea5cee 100644
--- a/radio/aidl/vts/radio_aidl_hal_utils.h
+++ b/radio/aidl/vts/radio_aidl_hal_utils.h
@@ -24,6 +24,7 @@
 #include <aidl/android/hardware/radio/network/RegState.h>
 #include <aidl/android/hardware/radio/sim/CardStatus.h>
 #include <aidl/android/hardware/radio/sim/IRadioSim.h>
+#include <com_android_internal_telephony_flags.h>
 #include <utils/Log.h>
 
 using namespace aidl::android::hardware::radio;
@@ -31,6 +32,8 @@
 using aidl::android::hardware::radio::network::RegState;
 using aidl::android::hardware::radio::sim::CardStatus;
 
+namespace telephony_flags = com::android::internal::telephony::flags;
+
 extern CardStatus cardStatus;
 extern SimSlotStatus slotStatus;
 extern int serial;
@@ -68,7 +71,17 @@
 
 static constexpr const char* FEATURE_TELEPHONY_IMS = "android.hardware.telephony.ims";
 
-static constexpr const char* FEATURE_TELEPHONY_SATELLITE = "android.hardware.telephony.satellite";
+static constexpr const char* FEATURE_TELEPHONY_CALLING = "android.hardware.telephony.calling";
+
+static constexpr const char* FEATURE_TELEPHONY_DATA = "android.hardware.telephony.data";
+
+static constexpr const char* FEATURE_TELEPHONY_MESSAGING = "android.hardware.telephony.messaging";
+
+static constexpr const char* FEATURE_TELEPHONY_SUBSCRIPTION =
+        "android.hardware.telephony.subscription";
+
+static constexpr const char* FEATURE_TELEPHONY_RADIO_ACCESS =
+        "android.hardware.telephony.radio.access";
 
 #define MODEM_EMERGENCY_CALL_ESTABLISH_TIME 3
 #define MODEM_EMERGENCY_CALL_DISCONNECT_TIME 3
diff --git a/radio/aidl/vts/radio_config_test.cpp b/radio/aidl/vts/radio_config_test.cpp
index aed3b05..d8c0142 100644
--- a/radio/aidl/vts/radio_config_test.cpp
+++ b/radio/aidl/vts/radio_config_test.cpp
@@ -54,6 +54,13 @@
  * Test IRadioConfig.getHalDeviceCapabilities() for the response returned.
  */
 TEST_P(RadioConfigTest, getHalDeviceCapabilities) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY)) {
+            GTEST_SKIP() << "Skipping getHalDeviceCapabilities "
+                            "due to undefined FEATURE_TELEPHONY";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     ndk::ScopedAStatus res = radio_config->getHalDeviceCapabilities(serial);
     ASSERT_OK(res);
@@ -66,6 +73,13 @@
  * Test IRadioConfig.getSimSlotsStatus() for the response returned.
  */
 TEST_P(RadioConfigTest, getSimSlotsStatus) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping getSimSlotsStatus "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     ndk::ScopedAStatus res = radio_config->getSimSlotsStatus(serial);
     ASSERT_OK(res);
@@ -78,6 +92,13 @@
  * Test IRadioConfig.getPhoneCapability() for the response returned.
  */
 TEST_P(RadioConfigTest, getPhoneCapability) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY)) {
+            GTEST_SKIP() << "Skipping getPhoneCapability "
+                            "due to undefined FEATURE_TELEPHONY";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     ndk::ScopedAStatus res = radio_config->getPhoneCapability(serial);
     ASSERT_OK(res);
@@ -104,6 +125,13 @@
  * Test IRadioConfig.setPreferredDataModem() for the response returned.
  */
 TEST_P(RadioConfigTest, setPreferredDataModem) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_DATA)) {
+            GTEST_SKIP() << "Skipping setPreferredDataModem "
+                            "due to undefined FEATURE_TELEPHONY_DATA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     ndk::ScopedAStatus res = radio_config->getPhoneCapability(serial);
     ASSERT_OK(res);
@@ -146,6 +174,13 @@
  * Test IRadioConfig.setPreferredDataModem() with invalid arguments.
  */
 TEST_P(RadioConfigTest, setPreferredDataModem_invalidArgument) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_DATA)) {
+            GTEST_SKIP() << "Skipping setPreferredDataModem_invalidArgument "
+                            "due to undefined FEATURE_TELEPHONY_DATA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     uint8_t modemId = -1;
     ndk::ScopedAStatus res = radio_config->setPreferredDataModem(serial, modemId);
@@ -166,6 +201,13 @@
  * Test IRadioConfig.setSimSlotsMapping() for the response returned.
  */
 TEST_P(RadioConfigTest, setSimSlotsMapping) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping setSimSlotsMapping "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     // get slot status and set SIM slots mapping based on the result.
     updateSimSlotStatus();
     if (radioRsp_config->rspInfo.error == RadioError::NONE) {
@@ -227,6 +269,13 @@
  */
 
 TEST_P(RadioConfigTest, checkPortInfoExistsAndPortActive) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping checkPortInfoExistsAndPortActive "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     ndk::ScopedAStatus res = radio_config->getSimSlotsStatus(serial);
     ASSERT_OK(res);
diff --git a/radio/aidl/vts/radio_data_test.cpp b/radio/aidl/vts/radio_data_test.cpp
index f31c254..2aa5508 100644
--- a/radio/aidl/vts/radio_data_test.cpp
+++ b/radio/aidl/vts/radio_data_test.cpp
@@ -68,6 +68,12 @@
  * Test IRadioData.setupDataCall() for the response returned.
  */
 TEST_P(RadioDataTest, setupDataCall) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_DATA)) {
+            GTEST_SKIP() << "setupDataCall : required FEATURE_TELEPHONY_DATA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     AccessNetwork accessNetwork = AccessNetwork::EUTRAN;
@@ -135,6 +141,13 @@
  * Test IRadioData.setupDataCall() with osAppId for the response returned.
  */
 TEST_P(RadioDataTest, setupDataCall_osAppId) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_DATA)) {
+            GTEST_SKIP() << "Skipping setupDataCall_osAppId "
+                            "due to undefined FEATURE_TELEPHONY_DATA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     AccessNetwork accessNetwork = AccessNetwork::EUTRAN;
@@ -227,6 +240,13 @@
  * Test IRadioData.getSlicingConfig() for the response returned.
  */
 TEST_P(RadioDataTest, getSlicingConfig) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_DATA)) {
+            GTEST_SKIP() << "Skipping getSlicingConfig "
+                            "due to undefined FEATURE_TELEPHONY_DATA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     radio_data->getSlicingConfig(serial);
     EXPECT_EQ(std::cv_status::no_timeout, wait());
@@ -242,6 +262,13 @@
  * Test IRadioData.setDataThrottling() for the response returned.
  */
 TEST_P(RadioDataTest, setDataThrottling) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_DATA)) {
+            GTEST_SKIP() << "Skipping setDataThrottling "
+                            "due to undefined FEATURE_TELEPHONY_DATA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     ndk::ScopedAStatus res = radio_data->setDataThrottling(
@@ -320,6 +347,13 @@
  * Test IRadioData.setInitialAttachApn() for the response returned.
  */
 TEST_P(RadioDataTest, setInitialAttachApn) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_DATA)) {
+            GTEST_SKIP() << "Skipping setInitialAttachApn "
+                            "due to undefined FEATURE_TELEPHONY_DATA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // Create a dataProfileInfo
@@ -363,6 +397,13 @@
  * Test IRadioData.setDataProfile() for the response returned.
  */
 TEST_P(RadioDataTest, setDataProfile) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_DATA)) {
+            GTEST_SKIP() << "Skipping setDataProfile "
+                            "due to undefined FEATURE_TELEPHONY_DATA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // Create a dataProfileInfo
@@ -409,6 +450,13 @@
  * Test IRadioData.deactivateDataCall() for the response returned.
  */
 TEST_P(RadioDataTest, deactivateDataCall) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_DATA)) {
+            GTEST_SKIP() << "Skipping deactivateDataCall "
+                            "due to undefined FEATURE_TELEPHONY_DATA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     int cid = 1;
     DataRequestReason reason = DataRequestReason::NORMAL;
@@ -440,6 +488,13 @@
  * Test IRadioData.startKeepalive() for the response returned.
  */
 TEST_P(RadioDataTest, startKeepalive) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_DATA)) {
+            GTEST_SKIP() << "Skipping startKeepalive "
+                            "due to undefined FEATURE_TELEPHONY_DATA";
+        }
+    }
+
     std::vector<KeepaliveRequest> requests = {
             {
                     // Invalid IPv4 source address
@@ -538,6 +593,13 @@
  * Test IRadioData.stopKeepalive() for the response returned.
  */
 TEST_P(RadioDataTest, stopKeepalive) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_DATA)) {
+            GTEST_SKIP() << "Skipping stopKeepalive "
+                            "due to undefined FEATURE_TELEPHONY_DATA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_data->stopKeepalive(serial, 0xBAD);
@@ -554,6 +616,13 @@
  * Test IRadioData.getDataCallList() for the response returned.
  */
 TEST_P(RadioDataTest, getDataCallList) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_DATA)) {
+            GTEST_SKIP() << "Skipping getDataCallList "
+                            "due to undefined FEATURE_TELEPHONY_DATA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_data->getDataCallList(serial);
@@ -573,6 +642,13 @@
  * Test IRadioData.setDataAllowed() for the response returned.
  */
 TEST_P(RadioDataTest, setDataAllowed) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_DATA)) {
+            GTEST_SKIP() << "Skipping setDataAllowed "
+                            "due to undefined FEATURE_TELEPHONY_DATA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     bool allow = true;
 
diff --git a/radio/aidl/vts/radio_imsmedia_session_listener.cpp b/radio/aidl/vts/radio_imsmedia_session_listener.cpp
index 986cab2..638a0e4 100644
--- a/radio/aidl/vts/radio_imsmedia_session_listener.cpp
+++ b/radio/aidl/vts/radio_imsmedia_session_listener.cpp
@@ -49,3 +49,7 @@
         const CallQuality& /*in_callQuality*/) {
     return ndk::ScopedAStatus::ok();
 }
+ndk::ScopedAStatus ImsMediaSessionListener::notifyRtpReceptionStats(
+        const RtpReceptionStats& /*in_stats*/) {
+    return ndk::ScopedAStatus::ok();
+}
diff --git a/radio/aidl/vts/radio_imsmedia_test.cpp b/radio/aidl/vts/radio_imsmedia_test.cpp
index 425f6b4..e479e64 100644
--- a/radio/aidl/vts/radio_imsmedia_test.cpp
+++ b/radio/aidl/vts/radio_imsmedia_test.cpp
@@ -260,6 +260,59 @@
     return result;
 }
 
+TEST_P(RadioImsMediaTest, testAvSyncOperation) {
+    int32_t sessionId = 1;
+    RtpConfig modifyRtpConfig;
+    int32_t receptionInterval = 1000;
+    int32_t delay = 200;
+
+    modifyRtpConfig.direction = static_cast<int32_t>(MediaDirection::RTP_TX) |
+                                static_cast<int32_t>(MediaDirection::RTP_RX) |
+                                static_cast<int32_t>(MediaDirection::RTCP_TX) |
+                                static_cast<int32_t>(MediaDirection::RTCP_RX);
+    modifyRtpConfig.remoteAddress.ipAddress = "122.22.22.33";
+    modifyRtpConfig.remoteAddress.portNumber = 1234;
+
+    if (!deviceSupportsFeature(FEATURE_TELEPHONY_IMS)) {
+        ALOGI("Skipping setListener because ims is not supported in device");
+        return;
+    } else {
+        ALOGI("Running setListener because ims is supported in device");
+    }
+
+    ndk::ScopedAStatus res = radio_imsmedia->setListener(radio_imsmedialistener);
+    ASSERT_OK(res);
+
+    serial = SERIAL_OPEN_SESSION;
+    res = triggerOpenSession(sessionId);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(sessionId, radio_imsmedialistener->mSessionId);
+    ASSERT_NE(nullptr, radio_imsmedialistener->mSession);
+
+    radio_imsmediasession = radio_imsmedialistener->mSession;
+    radio_imsmediasession->setListener(radio_imsmediasessionlistener);
+    ASSERT_OK(res);
+
+    serial = SERIAL_MODIFY_SESSION;
+    res = radio_imsmediasession->modifySession(modifyRtpConfig);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(modifyRtpConfig, radio_imsmediasessionlistener->mConfig);
+    verifyError(radio_imsmediasessionlistener->mError);
+
+    res = radio_imsmediasession->requestRtpReceptionStats(receptionInterval);
+    ASSERT_OK(res);
+
+    res = radio_imsmediasession->adjustDelay(delay);
+    ASSERT_OK(res);
+
+    serial = SERIAL_CLOSE_SESSION;
+    res = radio_imsmedia->closeSession(sessionId);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+}
+
 void RadioImsMediaTest::verifyError(RtpError error) {
     switch (error) {
         case RtpError::NONE:
diff --git a/radio/aidl/vts/radio_imsmedia_utils.h b/radio/aidl/vts/radio_imsmedia_utils.h
index 87f1b00..407ba95 100644
--- a/radio/aidl/vts/radio_imsmedia_utils.h
+++ b/radio/aidl/vts/radio_imsmedia_utils.h
@@ -76,6 +76,7 @@
     virtual ndk::ScopedAStatus onDtmfReceived(char16_t in_dtmfDigit,
                                               int32_t in_durationMs) override;
     virtual ndk::ScopedAStatus onCallQualityChanged(const CallQuality& in_callQuality) override;
+    virtual ndk::ScopedAStatus notifyRtpReceptionStats(const RtpReceptionStats& in_stats) override;
 };
 
 /* The main test class for Radio AIDL ImsMedia. */
diff --git a/radio/aidl/vts/radio_messaging_test.cpp b/radio/aidl/vts/radio_messaging_test.cpp
index 4ab88d2..95e2617 100644
--- a/radio/aidl/vts/radio_messaging_test.cpp
+++ b/radio/aidl/vts/radio_messaging_test.cpp
@@ -59,6 +59,13 @@
  * Test IRadioMessaging.sendSms() for the response returned.
  */
 TEST_P(RadioMessagingTest, sendSms) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_MESSAGING)) {
+            GTEST_SKIP() << "Skipping sendSms "
+                            "due to undefined FEATURE_TELEPHONY_MESSAGING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     GsmSmsMessage msg;
     msg.smscPdu = "";
@@ -83,6 +90,13 @@
  * Test IRadioMessaging.sendSmsExpectMore() for the response returned.
  */
 TEST_P(RadioMessagingTest, sendSmsExpectMore) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_MESSAGING)) {
+            GTEST_SKIP() << "Skipping sendSmsExpectMore "
+                            "due to undefined FEATURE_TELEPHONY_MESSAGING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     GsmSmsMessage msg;
     msg.smscPdu = "";
@@ -106,6 +120,13 @@
  * Test IRadioMessaging.sendCdmaSms() for the response returned.
  */
 TEST_P(RadioMessagingTest, sendCdmaSms) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+            GTEST_SKIP() << "Skipping sendCdmaSms "
+                            "due to undefined FEATURE_TELEPHONY_CDMA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // Create a CdmaSmsAddress
@@ -150,6 +171,13 @@
  * Test IRadioMessaging.sendCdmaSmsExpectMore() for the response returned.
  */
 TEST_P(RadioMessagingTest, sendCdmaSmsExpectMore) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+            GTEST_SKIP() << "Skipping sendCdmaSmsExpectMore "
+                            "due to undefined FEATURE_TELEPHONY_CDMA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // Create a CdmaSmsAddress
@@ -194,6 +222,13 @@
  * Test IRadioMessaging.setGsmBroadcastConfig() for the response returned.
  */
 TEST_P(RadioMessagingTest, setGsmBroadcastConfig) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_MESSAGING)) {
+            GTEST_SKIP() << "Skipping setGsmBroadcastConfig "
+                            "due to undefined FEATURE_TELEPHONY_MESSAGING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // Create GsmBroadcastSmsConfigInfo #1
@@ -257,6 +292,13 @@
  * Test IRadioMessaging.getGsmBroadcastConfig() for the response returned.
  */
 TEST_P(RadioMessagingTest, getGsmBroadcastConfig) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_MESSAGING)) {
+            GTEST_SKIP() << "Skipping getGsmBroadcastConfig "
+                            "due to undefined FEATURE_TELEPHONY_MESSAGING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_messaging->getGsmBroadcastConfig(serial);
@@ -277,6 +319,13 @@
  * Test IRadioMessaging.setCdmaBroadcastConfig() for the response returned.
  */
 TEST_P(RadioMessagingTest, setCdmaBroadcastConfig) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+            GTEST_SKIP() << "Skipping setCdmaBroadcastConfig "
+                            "due to undefined FEATURE_TELEPHONY_CDMA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     CdmaBroadcastSmsConfigInfo cbSmsConfig;
@@ -303,6 +352,13 @@
  * Test IRadioMessaging.getCdmaBroadcastConfig() for the response returned.
  */
 TEST_P(RadioMessagingTest, getCdmaBroadcastConfig) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+            GTEST_SKIP() << "Skipping getCdmaBroadcastConfig "
+                            "due to undefined FEATURE_TELEPHONY_CDMA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_messaging->getCdmaBroadcastConfig(serial);
@@ -321,6 +377,13 @@
  * Test IRadioMessaging.setCdmaBroadcastActivation() for the response returned.
  */
 TEST_P(RadioMessagingTest, setCdmaBroadcastActivation) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+            GTEST_SKIP() << "Skipping setCdmaBroadcastActivation "
+                            "due to undefined FEATURE_TELEPHONY_CDMA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     bool activate = false;
 
@@ -341,6 +404,13 @@
  * Test IRadioMessaging.setGsmBroadcastActivation() for the response returned.
  */
 TEST_P(RadioMessagingTest, setGsmBroadcastActivation) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_MESSAGING)) {
+            GTEST_SKIP() << "Skipping setGsmBroadcastActivation "
+                            "due to undefined FEATURE_TELEPHONY_MESSAGING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     bool activate = false;
 
@@ -363,6 +433,13 @@
  * Test IRadioMessaging.acknowledgeLastIncomingGsmSms() for the response returned.
  */
 TEST_P(RadioMessagingTest, acknowledgeLastIncomingGsmSms) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_MESSAGING)) {
+            GTEST_SKIP() << "Skipping acknowledgeLastIncomingGsmSms "
+                            "due to undefined FEATURE_TELEPHONY_MESSAGING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     bool success = true;
 
@@ -384,6 +461,13 @@
  * Test IRadioMessaging.acknowledgeIncomingGsmSmsWithPdu() for the response returned.
  */
 TEST_P(RadioMessagingTest, acknowledgeIncomingGsmSmsWithPdu) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_MESSAGING)) {
+            GTEST_SKIP() << "Skipping acknowledgeIncomingGsmSmsWithPdu "
+                            "due to undefined FEATURE_TELEPHONY_MESSAGING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     bool success = true;
     std::string ackPdu = "";
@@ -405,6 +489,13 @@
  * Test IRadioMessaging.acknowledgeLastIncomingCdmaSms() for the response returned.
  */
 TEST_P(RadioMessagingTest, acknowledgeLastIncomingCdmaSms) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+            GTEST_SKIP() << "Skipping acknowledgeIncomingGsmSmsWithPdu "
+                            "due to undefined FEATURE_TELEPHONY_CDMA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // Create a CdmaSmsAck
@@ -429,6 +520,13 @@
  * Test IRadioMessaging.sendImsSms() for the response returned.
  */
 TEST_P(RadioMessagingTest, sendImsSms) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_IMS)) {
+            GTEST_SKIP() << "Skipping acknowledgeIncomingGsmSmsWithPdu "
+                            "due to undefined FEATURE_TELEPHONY_IMS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // Create a CdmaSmsAddress
@@ -479,6 +577,13 @@
  * Test IRadioMessaging.getSmscAddress() for the response returned.
  */
 TEST_P(RadioMessagingTest, getSmscAddress) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_MESSAGING)) {
+            GTEST_SKIP() << "Skipping getSmscAddress "
+                            "due to undefined FEATURE_TELEPHONY_MESSAGING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_messaging->getSmscAddress(serial);
@@ -499,6 +604,13 @@
  * Test IRadioMessaging.setSmscAddress() for the response returned.
  */
 TEST_P(RadioMessagingTest, setSmscAddress) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_MESSAGING)) {
+            GTEST_SKIP() << "Skipping setSmscAddress "
+                            "due to undefined FEATURE_TELEPHONY_MESSAGING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     std::string address = std::string("smscAddress");
 
@@ -520,6 +632,13 @@
  * Test IRadioMessaging.writeSmsToSim() for the response returned.
  */
 TEST_P(RadioMessagingTest, writeSmsToSim) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_MESSAGING)) {
+            GTEST_SKIP() << "Skipping writeSmsToSim "
+                            "due to undefined FEATURE_TELEPHONY_MESSAGING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     SmsWriteArgs smsWriteArgs;
     smsWriteArgs.status = SmsWriteArgs::STATUS_REC_UNREAD;
@@ -546,6 +665,13 @@
  * Test IRadioMessaging.deleteSmsOnSim() for the response returned.
  */
 TEST_P(RadioMessagingTest, deleteSmsOnSim) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_MESSAGING)) {
+            GTEST_SKIP() << "Skipping deleteSmsOnSim "
+                            "due to undefined FEATURE_TELEPHONY_MESSAGING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     int index = 1;
 
@@ -569,6 +695,13 @@
  * Test IRadioMessaging.writeSmsToRuim() for the response returned.
  */
 TEST_P(RadioMessagingTest, writeSmsToRuim) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+            GTEST_SKIP() << "Skipping writeSmsToRuim "
+                            "due to undefined FEATURE_TELEPHONY_CDMA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // Create a CdmaSmsAddress
@@ -620,6 +753,13 @@
  * Test IRadioMessaging.deleteSmsOnRuim() for the response returned.
  */
 TEST_P(RadioMessagingTest, deleteSmsOnRuim) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+            GTEST_SKIP() << "Skipping deleteSmsOnRuim "
+                            "due to undefined FEATURE_TELEPHONY_CDMA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     int index = 1;
 
@@ -671,6 +811,13 @@
  * Test IRadioMessaging.reportSmsMemoryStatus() for the response returned.
  */
 TEST_P(RadioMessagingTest, reportSmsMemoryStatus) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_MESSAGING)) {
+            GTEST_SKIP() << "Skipping reportSmsMemoryStatus "
+                            "due to undefined FEATURE_TELEPHONY_MESSAGING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     bool available = true;
 
diff --git a/radio/aidl/vts/radio_modem_indication.cpp b/radio/aidl/vts/radio_modem_indication.cpp
index 0bfcd66..9f63cb0 100644
--- a/radio/aidl/vts/radio_modem_indication.cpp
+++ b/radio/aidl/vts/radio_modem_indication.cpp
@@ -41,3 +41,8 @@
 ndk::ScopedAStatus RadioModemIndication::rilConnected(RadioIndicationType /*type*/) {
     return ndk::ScopedAStatus::ok();
 }
+
+ndk::ScopedAStatus RadioModemIndication::onImeiMappingChanged(RadioIndicationType /*type*/,
+     const ::aidl::android::hardware::radio::modem::ImeiInfo& /*imeiInfo*/) {
+    return ndk::ScopedAStatus::ok();
+}
diff --git a/radio/aidl/vts/radio_modem_test.cpp b/radio/aidl/vts/radio_modem_test.cpp
index c48a461..6a9996b 100644
--- a/radio/aidl/vts/radio_modem_test.cpp
+++ b/radio/aidl/vts/radio_modem_test.cpp
@@ -59,6 +59,13 @@
  * Test IRadioModem.setRadioPower() for the response returned.
  */
 TEST_P(RadioModemTest, setRadioPower_emergencyCall_cancelled) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setRadioPower_emergencyCall_cancelled "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     // Set radio power to off.
     serial = GetRandomSerialNumber();
     radio_modem->setRadioPower(serial, false, false, false);
@@ -90,6 +97,13 @@
  * Test IRadioModem.enableModem() for the response returned.
  */
 TEST_P(RadioModemTest, enableModem) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY)) {
+            GTEST_SKIP() << "Skipping enableModem "
+                            "due to undefined FEATURE_TELEPHONY";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     if (isSsSsEnabled()) {
@@ -134,6 +148,13 @@
  * Test IRadioModem.getModemStackStatus() for the response returned.
  */
 TEST_P(RadioModemTest, getModemStackStatus) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY)) {
+            GTEST_SKIP() << "Skipping getModemStackStatus "
+                            "due to undefined FEATURE_TELEPHONY";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     ndk::ScopedAStatus res = radio_modem->getModemStackStatus(serial);
@@ -152,6 +173,13 @@
  * Test IRadioModem.getBasebandVersion() for the response returned.
  */
 TEST_P(RadioModemTest, getBasebandVersion) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY)) {
+            GTEST_SKIP() << "Skipping getBasebandVersion "
+                            "due to undefined FEATURE_TELEPHONY";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_modem->getBasebandVersion(serial);
@@ -168,6 +196,13 @@
  * Test IRadioModem.getDeviceIdentity() for the response returned.
  */
 TEST_P(RadioModemTest, getDeviceIdentity) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY)) {
+            GTEST_SKIP() << "Skipping getDeviceIdentity "
+                            "due to undefined FEATURE_TELEPHONY";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_modem->getDeviceIdentity(serial);
@@ -185,6 +220,13 @@
  * Test IRadioModem.getImei() for the response returned.
  */
 TEST_P(RadioModemTest, getImei) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_GSM)) {
+            GTEST_SKIP() << "Skipping getImei "
+                            "due to undefined FEATURE_TELEPHONY_GSM";
+        }
+    }
+
     int32_t aidl_version;
     ndk::ScopedAStatus aidl_status = radio_modem->getInterfaceVersion(&aidl_version);
     ASSERT_OK(aidl_status);
@@ -246,6 +288,13 @@
  * Test IRadioModem.nvWriteCdmaPrl() for the response returned.
  */
 TEST_P(RadioModemTest, nvWriteCdmaPrl) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+            GTEST_SKIP() << "Skipping nvWriteCdmaPrl "
+                            "due to undefined FEATURE_TELEPHONY_CDMA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     std::vector<uint8_t> prl = {1, 2, 3, 4, 5};
 
@@ -283,6 +332,13 @@
  * Test IRadioModem.getHardwareConfig() for the response returned.
  */
 TEST_P(RadioModemTest, getHardwareConfig) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY)) {
+            GTEST_SKIP() << "Skipping getHardwareConfig "
+                            "due to undefined FEATURE_TELEPHONY";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_modem->getHardwareConfig(serial);
@@ -302,6 +358,13 @@
  * Test IRadioModem.requestShutdown() for the response returned.
  */
 TEST_P(RadioModemTest, DISABLED_requestShutdown) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping DISABLED_requestShutdown "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_modem->requestShutdown(serial);
@@ -319,6 +382,13 @@
  * Test IRadioModem.getRadioCapability() for the response returned.
  */
 TEST_P(RadioModemTest, getRadioCapability) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping getRadioCapability "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_modem->getRadioCapability(serial);
@@ -335,6 +405,13 @@
  * Test IRadioModem.setRadioCapability() for the response returned.
  */
 TEST_P(RadioModemTest, setRadioCapability) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setRadioCapability "
+                            "due to undefined FEATURE_TELEPHONY";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     RadioCapability rc;
     memset(&rc, 0, sizeof(rc));
@@ -356,6 +433,13 @@
  * Test IRadioModem.getModemActivityInfo() for the response returned.
  */
 TEST_P(RadioModemTest, getModemActivityInfo) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping getModemActivityInfo "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_modem->getModemActivityInfo(serial);
@@ -373,6 +457,13 @@
  * Test IRadioModem.sendDeviceState() for the response returned.
  */
 TEST_P(RadioModemTest, sendDeviceState) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY)) {
+            GTEST_SKIP() << "Skipping sendDeviceState "
+                            "due to undefined FEATURE_TELEPHONY";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_modem->sendDeviceState(serial, DeviceStateType::POWER_SAVE_MODE, true);
diff --git a/radio/aidl/vts/radio_modem_utils.h b/radio/aidl/vts/radio_modem_utils.h
index d47bdeb..aa99ea3 100644
--- a/radio/aidl/vts/radio_modem_utils.h
+++ b/radio/aidl/vts/radio_modem_utils.h
@@ -109,6 +109,9 @@
                                                  RadioState radioState) override;
 
     virtual ndk::ScopedAStatus rilConnected(RadioIndicationType type) override;
+
+    virtual ndk::ScopedAStatus onImeiMappingChanged(RadioIndicationType type,
+            const ::aidl::android::hardware::radio::modem::ImeiInfo& imeiInfo) override;
 };
 
 // The main test class for Radio AIDL Modem.
diff --git a/radio/aidl/vts/radio_network_indication.cpp b/radio/aidl/vts/radio_network_indication.cpp
index ae3bd4b..9614783 100644
--- a/radio/aidl/vts/radio_network_indication.cpp
+++ b/radio/aidl/vts/radio_network_indication.cpp
@@ -97,3 +97,14 @@
         RadioIndicationType /*type*/, const EmergencyRegResult& /*result*/) {
     return ndk::ScopedAStatus::ok();
 }
+
+ndk::ScopedAStatus RadioNetworkIndication::cellularIdentifierDisclosed(
+        RadioIndicationType /*type*/,
+        const CellularIdentifierDisclosure& /*disclosures*/) {
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus RadioNetworkIndication::securityAlgorithmsUpdated(
+        RadioIndicationType /*type*/, const SecurityAlgorithmUpdate& /*securityAlgorithmUpdate*/) {
+    return ndk::ScopedAStatus::ok();
+}
diff --git a/radio/aidl/vts/radio_network_response.cpp b/radio/aidl/vts/radio_network_response.cpp
index 25d45a5..4d452d0 100644
--- a/radio/aidl/vts/radio_network_response.cpp
+++ b/radio/aidl/vts/radio_network_response.cpp
@@ -320,3 +320,33 @@
     parent_network.notify(info.serial);
     return ndk::ScopedAStatus::ok();
 }
+
+ndk::ScopedAStatus RadioNetworkResponse::setCellularIdentifierTransparencyEnabledResponse(
+        const RadioResponseInfo& info) {
+    rspInfo = info;
+    parent_network.notify(info.serial);
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus RadioNetworkResponse::isCellularIdentifierTransparencyEnabledResponse(
+        const RadioResponseInfo& info, bool enabled) {
+    rspInfo = info;
+    this->isCellularIdentifierTransparencyEnabled = enabled;
+    parent_network.notify(info.serial);
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus RadioNetworkResponse::setSecurityAlgorithmsUpdatedEnabledResponse(
+        const RadioResponseInfo& info) {
+    rspInfo = info;
+    parent_network.notify(info.serial);
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus RadioNetworkResponse::isSecurityAlgorithmsUpdatedEnabledResponse(
+        const RadioResponseInfo& info, bool enabled) {
+    rspInfo = info;
+    this->isSecurityAlgorithmsUpdatedEnabled = enabled;
+    parent_network.notify(info.serial);
+    return ndk::ScopedAStatus::ok();
+}
diff --git a/radio/aidl/vts/radio_network_test.cpp b/radio/aidl/vts/radio_network_test.cpp
index 6643c1e..c893553 100644
--- a/radio/aidl/vts/radio_network_test.cpp
+++ b/radio/aidl/vts/radio_network_test.cpp
@@ -81,6 +81,13 @@
  * for the response returned.
  */
 TEST_P(RadioNetworkTest, setGetAllowedNetworkTypesBitmap) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setGetAllowedNetworkTypesBitmap "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // save current value
@@ -133,6 +140,13 @@
  * Test IRadioNetwork.setNrDualConnectivityState() for the response returned.
  */
 TEST_P(RadioNetworkTest, setNrDualConnectivityState) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setNrDualConnectivityState "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     ndk::ScopedAStatus res =
@@ -157,6 +171,13 @@
  * Test IRadioNetwork.isNrDualConnectivityEnabled() for the response returned.
  */
 TEST_P(RadioNetworkTest, isNrDualConnectivityEnabled) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping isNrDualConnectivityEnabled "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     ndk::ScopedAStatus res = radio_network->isNrDualConnectivityEnabled(serial);
@@ -195,6 +216,13 @@
  * Verify that the usage setting can be retrieved.
  */
 TEST_P(RadioNetworkTest, getUsageSetting) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY)) {
+            GTEST_SKIP() << "Skipping getUsageSetting "
+                            "due to undefined FEATURE_TELEPHONY";
+        }
+    }
+
     invokeAndExpectResponse([&](int serial) { return radio_network->getUsageSetting(serial); },
                             {RadioError::RADIO_NOT_AVAILABLE, RadioError::INVALID_STATE,
                              RadioError::SIM_ABSENT, RadioError::INTERNAL_ERR, RadioError::NONE});
@@ -232,6 +260,13 @@
  * -That the usage setting cannot be set to invalid values.
  */
 TEST_P(RadioNetworkTest, setUsageSetting) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY)) {
+            GTEST_SKIP() << "Skipping setUsageSetting "
+                            "due to undefined FEATURE_TELEPHONY";
+        }
+    }
+
     invokeAndExpectResponse([&](int serial) { return radio_network->getUsageSetting(serial); },
                             {RadioError::RADIO_NOT_AVAILABLE, RadioError::INVALID_STATE,
                              RadioError::SIM_ABSENT, RadioError::INTERNAL_ERR, RadioError::NONE});
@@ -294,6 +329,13 @@
  * Test IRadioNetwork.setSignalStrengthReportingCriteria() with invalid hysteresisDb
  */
 TEST_P(RadioNetworkTest, setSignalStrengthReportingCriteria_invalidHysteresisDb) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setSignalStrengthReportingCriteria_invalidHysteresisDb "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     SignalThresholdInfo signalThresholdInfo;
@@ -320,6 +362,13 @@
  * Test IRadioNetwork.setSignalStrengthReportingCriteria() with empty thresholds
  */
 TEST_P(RadioNetworkTest, setSignalStrengthReportingCriteria_EmptyThresholds) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setSignalStrengthReportingCriteria_EmptyThresholds "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     SignalThresholdInfo signalThresholdInfo;
@@ -345,6 +394,13 @@
  * Test IRadioNetwork.setSignalStrengthReportingCriteria() for GERAN
  */
 TEST_P(RadioNetworkTest, setSignalStrengthReportingCriteria_Geran) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setSignalStrengthReportingCriteria_Geran "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     SignalThresholdInfo signalThresholdInfo;
@@ -372,6 +428,13 @@
  * Test IRadioNetwork.setSignalStrengthReportingCriteria() for UTRAN
  */
 TEST_P(RadioNetworkTest, setSignalStrengthReportingCriteria_Utran_Rscp) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setSignalStrengthReportingCriteria_Utran_Rscp "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     SignalThresholdInfo signalThresholdInfo;
@@ -398,6 +461,13 @@
  * Test IRadioNetwork.setSignalStrengthReportingCriteria() for UTRAN
  */
 TEST_P(RadioNetworkTest, setSignalStrengthReportingCriteria_Utran_Ecno) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setSignalStrengthReportingCriteria_Utran_Ecno "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     SignalThresholdInfo signalThresholdInfo;
@@ -425,6 +495,13 @@
  * Test IRadioNetwork.setSignalStrengthReportingCriteria() for EUTRAN
  */
 TEST_P(RadioNetworkTest, setSignalStrengthReportingCriteria_Eutran_RSRP) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setSignalStrengthReportingCriteria_Eutran_RSRP "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     SignalThresholdInfo signalThresholdInfo;
@@ -451,6 +528,13 @@
  * Test IRadioNetwork.setSignalStrengthReportingCriteria() for EUTRAN
  */
 TEST_P(RadioNetworkTest, setSignalStrengthReportingCriteria_Eutran_RSRQ) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setSignalStrengthReportingCriteria_Eutran_RSRQ "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     SignalThresholdInfo signalThresholdInfo;
@@ -477,6 +561,13 @@
  * Test IRadioNetwork.setSignalStrengthReportingCriteria() for EUTRAN
  */
 TEST_P(RadioNetworkTest, setSignalStrengthReportingCriteria_Eutran_RSSNR) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setSignalStrengthReportingCriteria_Eutran_RSSNR "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     SignalThresholdInfo signalThresholdInfo;
@@ -499,6 +590,13 @@
  * Test IRadioNetwork.setSignalStrengthReportingCriteria() for CDMA2000
  */
 TEST_P(RadioNetworkTest, setSignalStrengthReportingCriteria_Cdma2000) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setSignalStrengthReportingCriteria_Cdma2000 "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     SignalThresholdInfo signalThresholdInfo;
@@ -525,6 +623,13 @@
  * Test IRadioNetwork.setSignalStrengthReportingCriteria() for NGRAN_SSRSRP
  */
 TEST_P(RadioNetworkTest, setSignalStrengthReportingCriteria_NGRAN_SSRSRP) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setSignalStrengthReportingCriteria_NGRAN_SSRSRP "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     SignalThresholdInfo signalThresholdInfo;
@@ -555,6 +660,13 @@
  * Test IRadioNetwork.setSignalStrengthReportingCriteria() for NGRAN_SSRSRQ
  */
 TEST_P(RadioNetworkTest, setSignalStrengthReportingCriteria_NGRAN_SSRSRQ) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setSignalStrengthReportingCriteria_NGRAN_SSRSRQ "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     SignalThresholdInfo signalThresholdInfo;
@@ -585,6 +697,13 @@
  * Test IRadioNetwork.setSignalStrengthReportingCriteria() for EUTRAN
  */
 TEST_P(RadioNetworkTest, setSignalStrengthReportingCriteria_Disable_RSSNR) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setSignalStrengthReportingCriteria_Disable_RSSNR "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     SignalThresholdInfo signalThresholdInfo;
@@ -607,6 +726,13 @@
  * Test IRadioNetwork.setSignalStrengthReportingCriteria() for NGRAN_SSSINR
  */
 TEST_P(RadioNetworkTest, setSignalStrengthReportingCriteria_NGRAN_SSSINR) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setSignalStrengthReportingCriteria_NGRAN_SSSINR "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     SignalThresholdInfo signalThresholdInfo;
@@ -637,6 +763,13 @@
  * Test IRadioNetwork.setSignalStrengthReportingCriteria() for multi-RANs per request
  */
 TEST_P(RadioNetworkTest, setSignalStrengthReportingCriteria_multiRansPerRequest) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setSignalStrengthReportingCriteria_multiRansPerRequest "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     SignalThresholdInfo signalThresholdInfoGeran;
     signalThresholdInfoGeran.signalMeasurement = SignalThresholdInfo::SIGNAL_MEASUREMENT_TYPE_RSSI;
     signalThresholdInfoGeran.hysteresisMs = 5000;
@@ -720,6 +853,13 @@
  * Test IRadioNetwork.setLinkCapacityReportingCriteria() invalid hysteresisDlKbps
  */
 TEST_P(RadioNetworkTest, setLinkCapacityReportingCriteria_invalidHysteresisDlKbps) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setLinkCapacityReportingCriteria_invalidHysteresisDlKbps "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     ndk::ScopedAStatus res = radio_network->setLinkCapacityReportingCriteria(
@@ -740,6 +880,13 @@
  * Test IRadioNetwork.setLinkCapacityReportingCriteria() invalid hysteresisUlKbps
  */
 TEST_P(RadioNetworkTest, setLinkCapacityReportingCriteria_invalidHysteresisUlKbps) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setLinkCapacityReportingCriteria_invalidHysteresisUlKbps "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     ndk::ScopedAStatus res = radio_network->setLinkCapacityReportingCriteria(
@@ -759,6 +906,13 @@
  * Test IRadioNetwork.setLinkCapacityReportingCriteria() empty params
  */
 TEST_P(RadioNetworkTest, setLinkCapacityReportingCriteria_emptyParams) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setLinkCapacityReportingCriteria_emptyParams "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     ndk::ScopedAStatus res = radio_network->setLinkCapacityReportingCriteria(
@@ -777,6 +931,13 @@
  * Test IRadioNetwork.setLinkCapacityReportingCriteria() for GERAN
  */
 TEST_P(RadioNetworkTest, setLinkCapacityReportingCriteria_Geran) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setLinkCapacityReportingCriteria_Geran "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     ndk::ScopedAStatus res = radio_network->setLinkCapacityReportingCriteria(
@@ -799,6 +960,13 @@
  * Test IRadioNetwork.setSystemSelectionChannels() for the response returned.
  */
 TEST_P(RadioNetworkTest, setSystemSelectionChannels) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setSystemSelectionChannels "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     ndk::ScopedAStatus res = radio_network->getSystemSelectionChannels(serial);
     EXPECT_EQ(std::cv_status::no_timeout, wait());
@@ -848,6 +1016,13 @@
  * Test IRadioNetwork.startNetworkScan() for the response returned.
  */
 TEST_P(RadioNetworkTest, startNetworkScan) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping startNetworkScan "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     NetworkScanRequest request = {.type = NetworkScanRequest::SCAN_TYPE_ONE_SHOT,
@@ -890,6 +1065,13 @@
  * Test IRadioNetwork.startNetworkScan() with invalid specifier.
  */
 TEST_P(RadioNetworkTest, startNetworkScan_InvalidArgument) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping startNetworkScan_InvalidArgument "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     NetworkScanRequest request = {.type = NetworkScanRequest::SCAN_TYPE_ONE_SHOT, .interval = 60};
@@ -915,6 +1097,13 @@
  * Test IRadioNetwork.startNetworkScan() with invalid interval (lower boundary).
  */
 TEST_P(RadioNetworkTest, startNetworkScan_InvalidInterval1) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping startNetworkScan_InvalidInterval1 "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     NetworkScanRequest request = {.type = NetworkScanRequest::SCAN_TYPE_PERIODIC,
@@ -944,6 +1133,13 @@
  * Test IRadioNetwork.startNetworkScan() with invalid interval (upper boundary).
  */
 TEST_P(RadioNetworkTest, startNetworkScan_InvalidInterval2) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping startNetworkScan_InvalidInterval2 "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     NetworkScanRequest request = {.type = NetworkScanRequest::SCAN_TYPE_PERIODIC,
@@ -973,6 +1169,13 @@
  * Test IRadioNetwork.startNetworkScan() with invalid max search time (lower boundary).
  */
 TEST_P(RadioNetworkTest, startNetworkScan_InvalidMaxSearchTime1) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping startNetworkScan_InvalidMaxSearchTime1 "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     NetworkScanRequest request = {.type = NetworkScanRequest::SCAN_TYPE_ONE_SHOT,
@@ -1002,6 +1205,13 @@
  * Test IRadioNetwork.startNetworkScan() with invalid max search time (upper boundary).
  */
 TEST_P(RadioNetworkTest, startNetworkScan_InvalidMaxSearchTime2) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping startNetworkScan_InvalidMaxSearchTime2 "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     NetworkScanRequest request = {.type = NetworkScanRequest::SCAN_TYPE_ONE_SHOT,
@@ -1031,6 +1241,13 @@
  * Test IRadioNetwork.startNetworkScan() with invalid periodicity (lower boundary).
  */
 TEST_P(RadioNetworkTest, startNetworkScan_InvalidPeriodicity1) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping startNetworkScan_InvalidPeriodicity1 "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     NetworkScanRequest request = {.type = NetworkScanRequest::SCAN_TYPE_ONE_SHOT,
@@ -1060,6 +1277,13 @@
  * Test IRadioNetwork.startNetworkScan() with invalid periodicity (upper boundary).
  */
 TEST_P(RadioNetworkTest, startNetworkScan_InvalidPeriodicity2) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping startNetworkScan_InvalidPeriodicity2 "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     NetworkScanRequest request = {.type = NetworkScanRequest::SCAN_TYPE_ONE_SHOT,
@@ -1089,6 +1313,13 @@
  * Test IRadioNetwork.startNetworkScan() with valid periodicity
  */
 TEST_P(RadioNetworkTest, startNetworkScan_GoodRequest1) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping startNetworkScan_GoodRequest1 "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     NetworkScanRequest request = {.type = NetworkScanRequest::SCAN_TYPE_ONE_SHOT,
@@ -1123,6 +1354,13 @@
  * Test IRadioNetwork.startNetworkScan() with valid periodicity and plmns
  */
 TEST_P(RadioNetworkTest, startNetworkScan_GoodRequest2) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping startNetworkScan_GoodRequest2 "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     NetworkScanRequest request = {.type = NetworkScanRequest::SCAN_TYPE_ONE_SHOT,
@@ -1158,6 +1396,13 @@
  * Test IRadioNetwork.setNetworkSelectionModeManual() for the response returned.
  */
 TEST_P(RadioNetworkTest, setNetworkSelectionModeManual) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setNetworkSelectionModeManual "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // can't camp on nonexistent MCCMNC, so we expect this to fail.
@@ -1186,6 +1431,13 @@
  * Test IRadioNetwork.getBarringInfo() for the response returned.
  */
 TEST_P(RadioNetworkTest, getBarringInfo) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping getBarringInfo "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     ndk::ScopedAStatus res = radio_network->getBarringInfo(serial);
     EXPECT_EQ(std::cv_status::no_timeout, wait());
@@ -1290,6 +1542,13 @@
  * Test IRadioNetwork.getSignalStrength() for the response returned.
  */
 TEST_P(RadioNetworkTest, getSignalStrength) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping getSignalStrength "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_network->getSignalStrength(serial);
@@ -1309,6 +1568,13 @@
  * Test IRadioNetwork.getCellInfoList() for the response returned.
  */
 TEST_P(RadioNetworkTest, getCellInfoList) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping getCellInfoList "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     ndk::ScopedAStatus res = radio_network->getCellInfoList(serial);
@@ -1327,6 +1593,13 @@
  * Test IRadioNetwork.getVoiceRegistrationState() for the response returned.
  */
 TEST_P(RadioNetworkTest, getVoiceRegistrationState) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping getVoiceRegistrationState "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     ndk::ScopedAStatus res = radio_network->getVoiceRegistrationState(serial);
@@ -1345,6 +1618,13 @@
  * Test IRadioNetwork.getDataRegistrationState() for the response returned.
  */
 TEST_P(RadioNetworkTest, getDataRegistrationState) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping getDataRegistrationState "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     ndk::ScopedAStatus res = radio_network->getDataRegistrationState(serial);
@@ -1440,6 +1720,13 @@
  * Test IRadioNetwork.getAvailableBandModes() for the response returned.
  */
 TEST_P(RadioNetworkTest, getAvailableBandModes) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping getAvailableBandModes "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     ndk::ScopedAStatus res = radio_network->getAvailableBandModes(serial);
@@ -1469,6 +1756,13 @@
  * Test IRadioNetwork.setIndicationFilter()
  */
 TEST_P(RadioNetworkTest, setIndicationFilter) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setIndicationFilter "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     ndk::ScopedAStatus res =
@@ -1487,6 +1781,13 @@
  * Test IRadioNetwork.setBarringPassword() for the response returned.
  */
 TEST_P(RadioNetworkTest, setBarringPassword) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setBarringPassword "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     std::string facility = "";
     std::string oldPassword = "";
@@ -1510,6 +1811,13 @@
  * Test IRadioNetwork.setSuppServiceNotifications() for the response returned.
  */
 TEST_P(RadioNetworkTest, setSuppServiceNotifications) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping setSuppServiceNotifications "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     bool enable = false;
 
@@ -1529,6 +1837,13 @@
  * Test IRadioNetwork.getImsRegistrationState() for the response returned.
  */
 TEST_P(RadioNetworkTest, getImsRegistrationState) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_IMS)) {
+            GTEST_SKIP() << "Skipping getImsRegistrationState "
+                            "due to undefined FEATURE_TELEPHONY_IMS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_network->getImsRegistrationState(serial);
@@ -1549,6 +1864,13 @@
  * Test IRadioNetwork.getOperator() for the response returned.
  */
 TEST_P(RadioNetworkTest, getOperator) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping getOperator "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_network->getOperator(serial);
@@ -1565,6 +1887,13 @@
  * Test IRadioNetwork.getNetworkSelectionMode() for the response returned.
  */
 TEST_P(RadioNetworkTest, getNetworkSelectionMode) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping getNetworkSelectionMode "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_network->getNetworkSelectionMode(serial);
@@ -1581,6 +1910,13 @@
  * Test IRadioNetwork.setNetworkSelectionModeAutomatic() for the response returned.
  */
 TEST_P(RadioNetworkTest, setNetworkSelectionModeAutomatic) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setNetworkSelectionModeAutomatic "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_network->setNetworkSelectionModeAutomatic(serial);
@@ -1600,6 +1936,13 @@
  * Test IRadioNetwork.getAvailableNetworks() for the response returned.
  */
 TEST_P(RadioNetworkTest, getAvailableNetworks) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping getAvailableNetworks "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_network->getAvailableNetworks(serial);
@@ -1621,6 +1964,13 @@
  * Test IRadioNetwork.setBandMode() for the response returned.
  */
 TEST_P(RadioNetworkTest, setBandMode) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setBandMode "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_network->setBandMode(serial, RadioBandMode::BAND_MODE_USA);
@@ -1638,6 +1988,13 @@
  * Test IRadioNetwork.setLocationUpdates() for the response returned.
  */
 TEST_P(RadioNetworkTest, setLocationUpdates) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setLocationUpdates "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_network->setLocationUpdates(serial, true);
@@ -1655,6 +2012,13 @@
  * Test IRadioNetwork.setCdmaRoamingPreference() for the response returned.
  */
 TEST_P(RadioNetworkTest, setCdmaRoamingPreference) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+            GTEST_SKIP() << "Skipping setCdmaRoamingPreference "
+                            "due to undefined FEATURE_TELEPHONY_CDMA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_network->setCdmaRoamingPreference(serial, CdmaRoamingType::HOME_NETWORK);
@@ -1673,6 +2037,13 @@
  * Test IRadioNetwork.getCdmaRoamingPreference() for the response returned.
  */
 TEST_P(RadioNetworkTest, getCdmaRoamingPreference) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+            GTEST_SKIP() << "Skipping getCdmaRoamingPreference "
+                            "due to undefined FEATURE_TELEPHONY_CDMA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_network->getCdmaRoamingPreference(serial);
@@ -1692,6 +2063,13 @@
  * Test IRadioNetwork.getVoiceRadioTechnology() for the response returned.
  */
 TEST_P(RadioNetworkTest, getVoiceRadioTechnology) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping getVoiceRadioTechnology "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_network->getVoiceRadioTechnology(serial);
@@ -1708,6 +2086,13 @@
  * Test IRadioNetwork.setCellInfoListRate() for the response returned.
  */
 TEST_P(RadioNetworkTest, setCellInfoListRate) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setCellInfoListRate "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_network->setCellInfoListRate(serial, 10);
@@ -1725,6 +2110,13 @@
  * Test IRadioNetwork.supplyNetworkDepersonalization() for the response returned.
  */
 TEST_P(RadioNetworkTest, supplyNetworkDepersonalization) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping supplyNetworkDepersonalization "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_network->supplyNetworkDepersonalization(serial, std::string("test"));
@@ -1745,6 +2137,13 @@
  * Test IRadioNetwork.setEmergencyMode() for the response returned.
  */
 TEST_P(RadioNetworkTest, setEmergencyMode) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setEmergencyMode "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     int32_t aidl_version;
     ndk::ScopedAStatus aidl_status = radio_network->getInterfaceVersion(&aidl_version);
     ASSERT_OK(aidl_status);
@@ -1774,6 +2173,13 @@
  * Test IRadioNetwork.triggerEmergencyNetworkScan() for the response returned.
  */
 TEST_P(RadioNetworkTest, triggerEmergencyNetworkScan) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping triggerEmergencyNetworkScan "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     int32_t aidl_version;
     ndk::ScopedAStatus aidl_status = radio_network->getInterfaceVersion(&aidl_version);
     ASSERT_OK(aidl_status);
@@ -1804,6 +2210,13 @@
  * Test IRadioNetwork.cancelEmergencyNetworkScan() for the response returned.
  */
 TEST_P(RadioNetworkTest, cancelEmergencyNetworkScan) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping cancelEmergencyNetworkScan "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     int32_t aidl_version;
     ndk::ScopedAStatus aidl_status = radio_network->getInterfaceVersion(&aidl_version);
     ASSERT_OK(aidl_status);
@@ -1828,6 +2241,13 @@
  * Test IRadioNetwork.exitEmergencyMode() for the response returned.
  */
 TEST_P(RadioNetworkTest, exitEmergencyMode) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping exitEmergencyMode "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     int32_t aidl_version;
     ndk::ScopedAStatus aidl_status = radio_network->getInterfaceVersion(&aidl_version);
     ASSERT_OK(aidl_status);
@@ -1852,6 +2272,13 @@
  * Test IRadioNetwork.setN1ModeEnabled() for the response returned.
  */
 TEST_P(RadioNetworkTest, setN1ModeEnabled) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setN1ModeEnabled "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     int32_t aidl_version;
     ndk::ScopedAStatus aidl_status = radio_network->getInterfaceVersion(&aidl_version);
     ASSERT_OK(aidl_status);
@@ -1884,6 +2311,13 @@
  * Test IRadioNetwork.isN1ModeEnabled() for the response returned.
  */
 TEST_P(RadioNetworkTest, isN1ModeEnabled) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping isN1ModeEnabled "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     int32_t aidl_version;
     ndk::ScopedAStatus aidl_status = radio_network->getInterfaceVersion(&aidl_version);
     ASSERT_OK(aidl_status);
@@ -1915,6 +2349,13 @@
  * Test IRadioNetwork.setNullCipherAndIntegrityEnabled() for the response returned.
  */
 TEST_P(RadioNetworkTest, setNullCipherAndIntegrityEnabled) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping setNullCipherAndIntegrityEnabled "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     int32_t aidl_version;
     ndk::ScopedAStatus aidl_status = radio_network->getInterfaceVersion(&aidl_version);
     ASSERT_OK(aidl_status);
@@ -1931,15 +2372,22 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_network->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_network->rspInfo.serial);
 
-    ASSERT_TRUE(CheckAnyOfErrors(radioRsp_network->rspInfo.error,
-                                 {RadioError::NONE, RadioError::REQUEST_NOT_SUPPORTED,
-                                  RadioError::RADIO_NOT_AVAILABLE, RadioError::MODEM_ERR}));
+    ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_network->rspInfo.error,
+            {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::MODEM_ERR}));
 }
 
 /**
  * Test IRadioNetwork.isNullCipherAndIntegrityEnabled() for the response returned.
  */
 TEST_P(RadioNetworkTest, isNullCipherAndIntegrityEnabled) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_RADIO_ACCESS)) {
+            GTEST_SKIP() << "Skipping isNullCipherAndIntegrityEnabled "
+                            "due to undefined FEATURE_TELEPHONY_RADIO_ACCESS";
+        }
+    }
+
     int32_t aidl_version;
     ndk::ScopedAStatus aidl_status = radio_network->getInterfaceVersion(&aidl_version);
     ASSERT_OK(aidl_status);
@@ -1958,7 +2406,168 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_network->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_network->rspInfo.serial);
 
-    ASSERT_TRUE(CheckAnyOfErrors(radioRsp_network->rspInfo.error,
-                                 {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE,
-                                  RadioError::MODEM_ERR, RadioError::REQUEST_NOT_SUPPORTED}));
+    ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_network->rspInfo.error,
+            {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::MODEM_ERR}));
+}
+
+TEST_P(RadioNetworkTest, isCellularIdentifierTransparencyEnabled) {
+    int32_t aidl_version;
+    ndk::ScopedAStatus aidl_status = radio_network->getInterfaceVersion(&aidl_version);
+    ASSERT_OK(aidl_status);
+    if (aidl_version < 3) {
+        ALOGI("Skipped the test since"
+                " isCellularIdentifierTransparencyEnabled is not supported on version < 3.");
+        GTEST_SKIP();
+    }
+
+    serial = GetRandomSerialNumber();
+
+    ndk::ScopedAStatus res = radio_network->isCellularIdentifierTransparencyEnabled(serial);
+    ASSERT_OK(res);
+
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_network->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_network->rspInfo.serial);
+
+    ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_network->rspInfo.error,
+            {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::MODEM_ERR}));
+
+    // the default value should be true if we have not called the setter
+    EXPECT_TRUE(radioRsp_network->isCellularIdentifierTransparencyEnabled);
+
+}
+
+TEST_P(RadioNetworkTest, setCellularIdentifierTransparencyEnabled) {
+    int32_t aidl_version;
+    ndk::ScopedAStatus aidl_status = radio_network->getInterfaceVersion(&aidl_version);
+    ASSERT_OK(aidl_status);
+    if (aidl_version < 3) {
+        ALOGI("Skipped the test since"
+                " setCellularIdentifierTransparencyEnabled is not supported on version < 3.");
+        GTEST_SKIP();
+    }
+
+    // Get current value
+    serial = GetRandomSerialNumber();
+    radio_network->isCellularIdentifierTransparencyEnabled(serial);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    bool originalTransparencySetting = radioRsp_network->isCellularIdentifierTransparencyEnabled;
+
+    // We want to test flipping the value, so we are going to set it to the opposite of what
+    // the existing setting is. The test for isCellularIdentifierTransparencyEnabled should check
+    // for the right default value.
+    bool valueToSet = !originalTransparencySetting;
+    serial = GetRandomSerialNumber();
+    radio_network->setCellularIdentifierTransparencyEnabled(serial, valueToSet);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_network->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_network->rspInfo.serial);
+
+    ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_network->rspInfo.error,
+            {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::MODEM_ERR}));
+
+    // Assert the value has changed
+    serial = GetRandomSerialNumber();
+    ndk::ScopedAStatus res = radio_network->isCellularIdentifierTransparencyEnabled(serial);
+
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_network->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_network->rspInfo.serial);
+    ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_network->rspInfo.error,
+            {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::MODEM_ERR}));
+    EXPECT_EQ(valueToSet, radioRsp_network->isCellularIdentifierTransparencyEnabled);
+
+    // Reset original state
+    radio_network->setCellularIdentifierTransparencyEnabled(serial, originalTransparencySetting);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_network->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_network->rspInfo.serial);
+}
+
+/*
+ * Test IRadioNetwork.setSecurityAlgorithmsUpdatedEnabled for the response returned.
+ */
+TEST_P(RadioNetworkTest, setSecurityAlgorithmsUpdatedEnabled) {
+    int32_t aidl_version;
+    ndk::ScopedAStatus aidl_status = radio_network->getInterfaceVersion(&aidl_version);
+    ASSERT_OK(aidl_status);
+    if (aidl_version < 3) {
+        ALOGI("Skipped the test since"
+              " setSecurityAlgorithmsUpdatedEnabled is not supported on version < 3");
+        GTEST_SKIP();
+    }
+
+    // Get current value
+    serial = GetRandomSerialNumber();
+    radio_network->isSecurityAlgorithmsUpdatedEnabled(serial);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    bool originalSecuritySetting = radioRsp_network->isSecurityAlgorithmsUpdatedEnabled;
+
+    // We want to test flipping the value, so we are going to set it to the opposite of what
+    // the existing setting is. The test for isSecurityAlgorithmsUpdatedEnabled should check
+    // for the right default value.
+    bool valueToSet = !originalSecuritySetting;
+    serial = GetRandomSerialNumber();
+    radio_network->setSecurityAlgorithmsUpdatedEnabled(serial, valueToSet);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_network->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_network->rspInfo.serial);
+
+    ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_network->rspInfo.error,
+            {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::MODEM_ERR}));
+
+    // Assert the value has changed
+    serial = GetRandomSerialNumber();
+    ndk::ScopedAStatus res = radio_network->isSecurityAlgorithmsUpdatedEnabled(serial);
+
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_network->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_network->rspInfo.serial);
+    ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_network->rspInfo.error,
+            {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::MODEM_ERR}));
+    EXPECT_EQ(valueToSet, radioRsp_network->isSecurityAlgorithmsUpdatedEnabled);
+
+    // Reset original state
+    radio_network->setSecurityAlgorithmsUpdatedEnabled(serial, originalSecuritySetting);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_network->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_network->rspInfo.serial);
+}
+
+/**
+ * Test IRadioNetwork.isSecurityAlgorithmsUpdatedEnabled for the response returned.
+ */
+TEST_P(RadioNetworkTest, isSecurityAlgorithmsUpdatedEnabled) {
+    int32_t aidl_version;
+    ndk::ScopedAStatus aidl_status = radio_network->getInterfaceVersion(&aidl_version);
+    ASSERT_OK(aidl_status);
+    if (aidl_version < 3) {
+        ALOGI("Skipped the test since"
+              " isSecurityAlgorithmsUpdatedEnabled is not supported on version < 3");
+        GTEST_SKIP();
+    }
+
+    serial = GetRandomSerialNumber();
+
+    ndk::ScopedAStatus res = radio_network->isSecurityAlgorithmsUpdatedEnabled(serial);
+    ASSERT_OK(res);
+
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_network->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_network->rspInfo.serial);
+
+    ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_network->rspInfo.error,
+            {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::MODEM_ERR}));
+
+    // the default value should be true if we have not called the setter
+    EXPECT_TRUE(radioRsp_network->isSecurityAlgorithmsUpdatedEnabled);
 }
diff --git a/radio/aidl/vts/radio_network_utils.h b/radio/aidl/vts/radio_network_utils.h
index 8f8f6b0..470ee73 100644
--- a/radio/aidl/vts/radio_network_utils.h
+++ b/radio/aidl/vts/radio_network_utils.h
@@ -46,6 +46,8 @@
     std::vector<BarringInfo> barringInfoList;
     UsageSetting usageSetting;
     std::vector<RadioAccessSpecifier> specifiers;
+    bool isCellularIdentifierTransparencyEnabled;
+    bool isSecurityAlgorithmsUpdatedEnabled;
 
     virtual ndk::ScopedAStatus acknowledgeRequest(int32_t serial) override;
 
@@ -169,6 +171,18 @@
             const RadioResponseInfo& info, bool isEnabled) override;
 
     virtual ndk::ScopedAStatus setN1ModeEnabledResponse(const RadioResponseInfo& info) override;
+
+    virtual ndk::ScopedAStatus setCellularIdentifierTransparencyEnabledResponse(
+            const RadioResponseInfo& info) override;
+
+    virtual ndk::ScopedAStatus isCellularIdentifierTransparencyEnabledResponse(
+            const RadioResponseInfo& info, bool /*enabled*/) override;
+
+    virtual ndk::ScopedAStatus isSecurityAlgorithmsUpdatedEnabledResponse(
+            const RadioResponseInfo& info, bool isEnabled) override;
+
+    virtual ndk::ScopedAStatus setSecurityAlgorithmsUpdatedEnabledResponse(
+            const RadioResponseInfo& info) override;
 };
 
 /* Callback class for radio network indication */
@@ -226,6 +240,13 @@
 
     virtual ndk::ScopedAStatus emergencyNetworkScanResult(
             RadioIndicationType type, const EmergencyRegResult& result) override;
+
+    virtual ndk::ScopedAStatus cellularIdentifierDisclosed(
+            RadioIndicationType type, const CellularIdentifierDisclosure& disclosures) override;
+
+    virtual ndk::ScopedAStatus securityAlgorithmsUpdated(
+            RadioIndicationType type,
+            const SecurityAlgorithmUpdate& securityAlgorithmUpdate) override;
 };
 
 // The main test class for Radio AIDL Network.
diff --git a/radio/aidl/vts/radio_sap_test.cpp b/radio/aidl/vts/radio_sap_test.cpp
index 9a1c145..6d283e9 100644
--- a/radio/aidl/vts/radio_sap_test.cpp
+++ b/radio/aidl/vts/radio_sap_test.cpp
@@ -85,6 +85,13 @@
  * Test ISap.connectReq() for the response returned.
  */
 TEST_P(SapTest, connectReq) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping connectReq "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     int32_t maxMsgSize = 100;
 
@@ -103,6 +110,13 @@
  * Test ISap.disconnectReq() for the response returned
  */
 TEST_P(SapTest, disconnectReq) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping disconnectReq "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     ndk::ScopedAStatus res = sap->disconnectReq(serial);
@@ -116,6 +130,13 @@
  * Test ISap.apduReq() for the response returned.
  */
 TEST_P(SapTest, apduReq) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping apduReq "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     SapApduType sapApduType = SapApduType::APDU;
     std::vector<uint8_t> command = {};
@@ -137,6 +158,13 @@
  * Test ISap.transferAtrReq() for the response returned.
  */
 TEST_P(SapTest, transferAtrReq) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping transferAtrReq "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     ndk::ScopedAStatus res = sap->transferAtrReq(serial);
@@ -155,6 +183,13 @@
  * Test ISap.powerReq() for the response returned.
  */
 TEST_P(SapTest, powerReq) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping powerReq "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     bool state = true;
 
@@ -175,6 +210,13 @@
  * Test ISap.resetSimReq() for the response returned.
  */
 TEST_P(SapTest, resetSimReq) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping resetSimReq "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     ndk::ScopedAStatus res = sap->resetSimReq(serial);
@@ -194,6 +236,13 @@
  * Test ISap.transferCardReaderStatusReq() for the response returned.
  */
 TEST_P(SapTest, transferCardReaderStatusReq) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping transferCardReaderStatusReq "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     ndk::ScopedAStatus res = sap->transferCardReaderStatusReq(serial);
@@ -211,6 +260,13 @@
  * Test ISap.setTransferProtocolReq() for the response returned.
  */
 TEST_P(SapTest, setTransferProtocolReq) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping setTransferProtocolReq "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     SapTransferProtocol sapTransferProtocol = SapTransferProtocol::T0;
 
diff --git a/radio/aidl/vts/radio_satellite_indication.cpp b/radio/aidl/vts/radio_satellite_indication.cpp
deleted file mode 100644
index 13e4453..0000000
--- a/radio/aidl/vts/radio_satellite_indication.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "radio_satellite_utils.h"
-
-RadioSatelliteIndication::RadioSatelliteIndication(RadioServiceTest& parent)
-    : parent_satellite(parent) {}
-
-ndk::ScopedAStatus RadioSatelliteIndication::onPendingMessageCount(RadioIndicationType /*type*/,
-                                                                   int32_t /*count*/) {
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteIndication::onNewMessages(
-        RadioIndicationType /*type*/, const std::vector<std::string>& /*messages*/) {
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteIndication::onMessagesTransferComplete(
-        RadioIndicationType /*type*/, bool /*complete*/) {
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteIndication::onSatellitePointingInfoChanged(
-        RadioIndicationType /*type*/, const PointingInfo& /*pointingInfo*/) {
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteIndication::onSatelliteModeChanged(RadioIndicationType /*type*/,
-                                                                    SatelliteMode /*mode*/) {
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteIndication::onSatelliteRadioTechnologyChanged(
-        RadioIndicationType /*type*/, NTRadioTechnology /*technology*/) {
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteIndication::onProvisionStateChanged(
-        RadioIndicationType /*type*/, bool /*provisioned*/,
-        const std::vector<SatelliteFeature>& /*features*/) {
-    return ndk::ScopedAStatus::ok();
-}
diff --git a/radio/aidl/vts/radio_satellite_response.cpp b/radio/aidl/vts/radio_satellite_response.cpp
deleted file mode 100644
index 84d57b2..0000000
--- a/radio/aidl/vts/radio_satellite_response.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "radio_satellite_utils.h"
-
-RadioSatelliteResponse::RadioSatelliteResponse(RadioServiceTest& parent)
-    : parent_satellite(parent) {}
-
-ndk::ScopedAStatus RadioSatelliteResponse::acknowledgeRequest(int32_t /*serial*/) {
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteResponse::getCapabilitiesResponse(
-        const RadioResponseInfo& info, const SatelliteCapabilities& /*capabilities*/) {
-    rspInfo = info;
-    parent_satellite.notify(info.serial);
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteResponse::setPowerResponse(const RadioResponseInfo& info) {
-    rspInfo = info;
-    parent_satellite.notify(info.serial);
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteResponse::getPowerStateResponse(const RadioResponseInfo& info,
-                                                                 bool /*on*/) {
-    rspInfo = info;
-    parent_satellite.notify(info.serial);
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteResponse::provisionServiceResponse(const RadioResponseInfo& info,
-                                                                    bool /*provisioned*/) {
-    rspInfo = info;
-    parent_satellite.notify(info.serial);
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteResponse::addAllowedSatelliteContactsResponse(
-        const RadioResponseInfo& info) {
-    rspInfo = info;
-    parent_satellite.notify(info.serial);
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteResponse::removeAllowedSatelliteContactsResponse(
-        const RadioResponseInfo& info) {
-    rspInfo = info;
-    parent_satellite.notify(info.serial);
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteResponse::sendMessagesResponse(const RadioResponseInfo& info) {
-    rspInfo = info;
-    parent_satellite.notify(info.serial);
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteResponse::getPendingMessagesResponse(
-        const RadioResponseInfo& info, const std::vector<std::string>& /*messages*/) {
-    rspInfo = info;
-    parent_satellite.notify(info.serial);
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteResponse::getSatelliteModeResponse(
-        const RadioResponseInfo& info, SatelliteMode /*mode*/, NTRadioTechnology /*technology*/) {
-    rspInfo = info;
-    parent_satellite.notify(info.serial);
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteResponse::setIndicationFilterResponse(
-        const RadioResponseInfo& info) {
-    rspInfo = info;
-    parent_satellite.notify(info.serial);
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteResponse::startSendingSatellitePointingInfoResponse(
-        const RadioResponseInfo& info) {
-    rspInfo = info;
-    parent_satellite.notify(info.serial);
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteResponse::stopSendingSatellitePointingInfoResponse(
-        const RadioResponseInfo& info) {
-    rspInfo = info;
-    parent_satellite.notify(info.serial);
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteResponse::getMaxCharactersPerTextMessageResponse(
-        const RadioResponseInfo& info, int32_t /*charLimit*/) {
-    rspInfo = info;
-    parent_satellite.notify(info.serial);
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus RadioSatelliteResponse::getTimeForNextSatelliteVisibilityResponse(
-        const RadioResponseInfo& info, int32_t /*timeInSeconds*/) {
-    rspInfo = info;
-    parent_satellite.notify(info.serial);
-    return ndk::ScopedAStatus::ok();
-}
\ No newline at end of file
diff --git a/radio/aidl/vts/radio_satellite_test.cpp b/radio/aidl/vts/radio_satellite_test.cpp
deleted file mode 100644
index b646ef6..0000000
--- a/radio/aidl/vts/radio_satellite_test.cpp
+++ /dev/null
@@ -1,519 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <aidl/android/hardware/radio/config/IRadioConfig.h>
-#include <android/binder_manager.h>
-
-#include "radio_satellite_utils.h"
-
-#define ASSERT_OK(ret) ASSERT_TRUE(((ret).isOk()))
-
-void RadioSatelliteTest::SetUp() {
-    RadioServiceTest::SetUp();
-    std::string serviceName = GetParam();
-
-    if (!isServiceValidForDeviceConfiguration(serviceName)) {
-        ALOGI("Skipped the radio satellite tests due to device configuration.");
-        GTEST_SKIP();
-    }
-
-    if (!deviceSupportsFeature(FEATURE_TELEPHONY_SATELLITE)) {
-        ALOGI("Skipping test because satellite is not supported in device.");
-        GTEST_SKIP();
-    }
-
-    satellite = IRadioSatellite::fromBinder(
-            ndk::SpAIBinder(AServiceManager_waitForService(GetParam().c_str())));
-    ASSERT_NE(nullptr, satellite.get());
-
-    rsp_satellite = ndk::SharedRefBase::make<RadioSatelliteResponse>(*this);
-    ASSERT_NE(nullptr, rsp_satellite.get());
-
-    ind_satellite = ndk::SharedRefBase::make<RadioSatelliteIndication>(*this);
-    ASSERT_NE(nullptr, ind_satellite.get());
-
-    satellite->setResponseFunctions(rsp_satellite, ind_satellite);
-
-    // Assert IRadioConfig exists before testing
-    radio_config = config::IRadioConfig::fromBinder(ndk::SpAIBinder(
-            AServiceManager_waitForService("android.hardware.radio.config.IRadioConfig/default")));
-    ASSERT_NE(nullptr, radio_config.get());
-}
-
-/*
- * Test IRadioSatellite.getCapabilities() for the response returned.
- */
-TEST_P(RadioSatelliteTest, getCapabilities) {
-    serial = GetRandomSerialNumber();
-    ndk::ScopedAStatus res = satellite->getCapabilities(serial);
-    ASSERT_OK(res);
-    EXPECT_EQ(std::cv_status::no_timeout, wait());
-    EXPECT_EQ(RadioResponseType::SOLICITED, rsp_satellite->rspInfo.type);
-    EXPECT_EQ(serial, rsp_satellite->rspInfo.serial);
-
-    ALOGI("getCapabilities, rspInfo.error = %s\n", toString(rsp_satellite->rspInfo.error).c_str());
-
-    ASSERT_TRUE(CheckAnyOfErrors(
-            rsp_satellite->rspInfo.error,
-            {RadioError::NONE, RadioError::INTERNAL_ERR, RadioError::INVALID_ARGUMENTS,
-             RadioError::INVALID_MODEM_STATE, RadioError::INVALID_SIM_STATE,
-             RadioError::INVALID_STATE, RadioError::MODEM_ERR, RadioError::NO_MEMORY,
-             RadioError::NO_RESOURCES, RadioError::RADIO_NOT_AVAILABLE,
-             RadioError::REQUEST_NOT_SUPPORTED, RadioError::REQUEST_RATE_LIMITED,
-             RadioError::SYSTEM_ERR}));
-}
-
-/*
- * Test IRadioSatellite.setPower() for the response returned.
- */
-TEST_P(RadioSatelliteTest, setPower) {
-    serial = GetRandomSerialNumber();
-    ndk::ScopedAStatus res = satellite->setPower(serial, true);
-    ASSERT_OK(res);
-    EXPECT_EQ(std::cv_status::no_timeout, wait());
-    EXPECT_EQ(RadioResponseType::SOLICITED, rsp_satellite->rspInfo.type);
-    EXPECT_EQ(serial, rsp_satellite->rspInfo.serial);
-
-    ALOGI("setPower, rspInfo.error = %s\n", toString(rsp_satellite->rspInfo.error).c_str());
-
-    ASSERT_TRUE(CheckAnyOfErrors(
-            rsp_satellite->rspInfo.error,
-            {RadioError::NONE, RadioError::INTERNAL_ERR, RadioError::INVALID_ARGUMENTS,
-             RadioError::INVALID_MODEM_STATE, RadioError::INVALID_SIM_STATE,
-             RadioError::INVALID_STATE, RadioError::MODEM_ERR, RadioError::NO_MEMORY,
-             RadioError::NO_RESOURCES, RadioError::RADIO_NOT_AVAILABLE,
-             RadioError::REQUEST_NOT_SUPPORTED, RadioError::REQUEST_RATE_LIMITED,
-             RadioError::SYSTEM_ERR}));
-}
-
-/*
- * Test IRadioSatellite.getPowerSate() for the response returned.
- */
-TEST_P(RadioSatelliteTest, getPowerSate) {
-    serial = GetRandomSerialNumber();
-    ndk::ScopedAStatus res = satellite->getPowerState(serial);
-    ASSERT_OK(res);
-    EXPECT_EQ(std::cv_status::no_timeout, wait());
-    EXPECT_EQ(RadioResponseType::SOLICITED, rsp_satellite->rspInfo.type);
-    EXPECT_EQ(serial, rsp_satellite->rspInfo.serial);
-
-    ALOGI("getPowerState, rspInfo.error = %s\n", toString(rsp_satellite->rspInfo.error).c_str());
-
-    ASSERT_TRUE(CheckAnyOfErrors(
-            rsp_satellite->rspInfo.error,
-            {RadioError::NONE, RadioError::INTERNAL_ERR, RadioError::INVALID_ARGUMENTS,
-             RadioError::INVALID_MODEM_STATE, RadioError::INVALID_SIM_STATE,
-             RadioError::INVALID_STATE, RadioError::MODEM_ERR, RadioError::NO_MEMORY,
-             RadioError::NO_RESOURCES, RadioError::RADIO_NOT_AVAILABLE,
-             RadioError::REQUEST_NOT_SUPPORTED, RadioError::REQUEST_RATE_LIMITED,
-             RadioError::SYSTEM_ERR}));
-}
-
-/*
- * Test IRadioSatellite.provisionService() for the response returned.
- */
-TEST_P(RadioSatelliteTest, provisionService) {
-    serial = GetRandomSerialNumber();
-    std::string imei = "imei";
-    std::string msisdn = "msisdn";
-    std::string imsi = "imsi";
-    const std::vector<SatelliteFeature> features{
-            SatelliteFeature::SOS_SMS, SatelliteFeature::EMERGENCY_SMS, SatelliteFeature::SMS};
-    ndk::ScopedAStatus res = satellite->provisionService(serial, imei, msisdn, imsi, features);
-    ASSERT_OK(res);
-    EXPECT_EQ(std::cv_status::no_timeout, wait());
-    EXPECT_EQ(RadioResponseType::SOLICITED, rsp_satellite->rspInfo.type);
-    EXPECT_EQ(serial, rsp_satellite->rspInfo.serial);
-
-    ALOGI("provisionService, rspInfo.error = %s\n", toString(rsp_satellite->rspInfo.error).c_str());
-
-    ASSERT_TRUE(CheckAnyOfErrors(rsp_satellite->rspInfo.error,
-                                 {RadioError::NONE,
-                                  RadioError::ABORTED,
-                                  RadioError::ACCESS_BARRED,
-                                  RadioError::CANCELLED,
-                                  RadioError::FEATURE_NOT_SUPPORTED,
-                                  RadioError::INTERNAL_ERR,
-                                  RadioError::INVALID_ARGUMENTS,
-                                  RadioError::INVALID_MODEM_STATE,
-                                  RadioError::INVALID_SIM_STATE,
-                                  RadioError::INVALID_STATE,
-                                  RadioError::MODEM_ERR,
-                                  RadioError::MODEM_INCOMPATIBLE,
-                                  RadioError::NETWORK_ERR,
-                                  RadioError::NETWORK_NOT_READY,
-                                  RadioError::NETWORK_REJECT,
-                                  RadioError::NETWORK_TIMEOUT,
-                                  RadioError::NO_MEMORY,
-                                  RadioError::NO_NETWORK_FOUND,
-                                  RadioError::NO_RESOURCES,
-                                  RadioError::NO_SATELLITE_SIGNAL,
-                                  RadioError::NO_SUBSCRIPTION,
-                                  RadioError::OPERATION_NOT_ALLOWED,
-                                  RadioError::RADIO_NOT_AVAILABLE,
-                                  RadioError::RADIO_TECHNOLOGY_NOT_SUPPORTED,
-                                  RadioError::REQUEST_NOT_SUPPORTED,
-                                  RadioError::REQUEST_RATE_LIMITED,
-                                  RadioError::SIM_ABSENT,
-                                  RadioError::SIM_BUSY,
-                                  RadioError::SIM_ERR,
-                                  RadioError::SIM_FULL,
-                                  RadioError::SUBSCRIBER_NOT_AUTHORIZED,
-                                  RadioError::SYSTEM_ERR}));
-}
-
-/*
- * Test IRadioSatellite.addAllowedSatelliteContacts() for the response returned.
- */
-TEST_P(RadioSatelliteTest, addAllowedSatelliteContacts) {
-    serial = GetRandomSerialNumber();
-    const std::vector<std::string> contacts = {"contact 1", "contact 2"};
-    ndk::ScopedAStatus res = satellite->addAllowedSatelliteContacts(serial, contacts);
-    ASSERT_OK(res);
-    EXPECT_EQ(std::cv_status::no_timeout, wait());
-    EXPECT_EQ(RadioResponseType::SOLICITED, rsp_satellite->rspInfo.type);
-    EXPECT_EQ(serial, rsp_satellite->rspInfo.serial);
-
-    ALOGI("addAllowedSatelliteContacts, rspInfo.error = %s\n",
-          toString(rsp_satellite->rspInfo.error).c_str());
-
-    ASSERT_TRUE(CheckAnyOfErrors(rsp_satellite->rspInfo.error,
-                                 {RadioError::NONE,
-                                  RadioError::ABORTED,
-                                  RadioError::ACCESS_BARRED,
-                                  RadioError::CANCELLED,
-                                  RadioError::INTERNAL_ERR,
-                                  RadioError::INVALID_ARGUMENTS,
-                                  RadioError::INVALID_CONTACT,
-                                  RadioError::INVALID_MODEM_STATE,
-                                  RadioError::INVALID_SIM_STATE,
-                                  RadioError::INVALID_STATE,
-                                  RadioError::MODEM_ERR,
-                                  RadioError::NETWORK_ERR,
-                                  RadioError::NETWORK_NOT_READY,
-                                  RadioError::NOT_SUFFICIENT_ACCOUNT_BALANCE,
-                                  RadioError::OPERATION_NOT_ALLOWED,
-                                  RadioError::RADIO_NOT_AVAILABLE,
-                                  RadioError::REQUEST_NOT_SUPPORTED,
-                                  RadioError::REQUEST_RATE_LIMITED,
-                                  RadioError::SIM_ABSENT,
-                                  RadioError::SIM_BUSY,
-                                  RadioError::SIM_ERR,
-                                  RadioError::SIM_FULL,
-                                  RadioError::SYSTEM_ERR,
-                                  RadioError::UNIDENTIFIED_SUBSCRIBER}));
-}
-
-/*
- * Test IRadioSatellite.removeAllowedSatelliteContacts() for the response returned.
- */
-TEST_P(RadioSatelliteTest, removeAllowedSatelliteContacts) {
-    serial = GetRandomSerialNumber();
-    const std::vector<std::string> contacts = {"contact 1", "contact 2"};
-    ndk::ScopedAStatus res = satellite->removeAllowedSatelliteContacts(serial, contacts);
-    ASSERT_OK(res);
-    EXPECT_EQ(std::cv_status::no_timeout, wait());
-    EXPECT_EQ(RadioResponseType::SOLICITED, rsp_satellite->rspInfo.type);
-    EXPECT_EQ(serial, rsp_satellite->rspInfo.serial);
-
-    ALOGI("removeAllowedSatelliteContacts, rspInfo.error = %s\n",
-          toString(rsp_satellite->rspInfo.error).c_str());
-
-    ASSERT_TRUE(CheckAnyOfErrors(rsp_satellite->rspInfo.error,
-                                 {RadioError::NONE,
-                                  RadioError::ABORTED,
-                                  RadioError::ACCESS_BARRED,
-                                  RadioError::CANCELLED,
-                                  RadioError::INTERNAL_ERR,
-                                  RadioError::INVALID_ARGUMENTS,
-                                  RadioError::INVALID_CONTACT,
-                                  RadioError::INVALID_MODEM_STATE,
-                                  RadioError::INVALID_SIM_STATE,
-                                  RadioError::INVALID_STATE,
-                                  RadioError::MODEM_ERR,
-                                  RadioError::NETWORK_ERR,
-                                  RadioError::NETWORK_NOT_READY,
-                                  RadioError::NETWORK_REJECT,
-                                  RadioError::NETWORK_TIMEOUT,
-                                  RadioError::NO_MEMORY,
-                                  RadioError::NO_NETWORK_FOUND,
-                                  RadioError::NO_RESOURCES,
-                                  RadioError::NO_SATELLITE_SIGNAL,
-                                  RadioError::NO_SUBSCRIPTION,
-                                  RadioError::NOT_SUFFICIENT_ACCOUNT_BALANCE,
-                                  RadioError::OPERATION_NOT_ALLOWED,
-                                  RadioError::RADIO_NOT_AVAILABLE,
-                                  RadioError::REQUEST_NOT_SUPPORTED,
-                                  RadioError::REQUEST_RATE_LIMITED,
-                                  RadioError::SIM_ABSENT,
-                                  RadioError::SIM_BUSY,
-                                  RadioError::SIM_ERR,
-                                  RadioError::SIM_FULL,
-                                  RadioError::SYSTEM_ERR,
-                                  RadioError::UNIDENTIFIED_SUBSCRIBER}));
-}
-
-/*
- * Test IRadioSatellite.sendMessages() for the response returned.
- */
-TEST_P(RadioSatelliteTest, sendMessages) {
-    serial = GetRandomSerialNumber();
-    const std::vector<std::string> messages = {"message 1", "message 2"};
-    std::string destination = "0123456789";
-    ndk::ScopedAStatus res = satellite->sendMessages(serial, messages, destination, 1.0, 2.0);
-    ASSERT_OK(res);
-    EXPECT_EQ(std::cv_status::no_timeout, wait());
-    EXPECT_EQ(RadioResponseType::SOLICITED, rsp_satellite->rspInfo.type);
-    EXPECT_EQ(serial, rsp_satellite->rspInfo.serial);
-
-    ALOGI("sendMessages, rspInfo.error = %s\n", toString(rsp_satellite->rspInfo.error).c_str());
-
-    ASSERT_TRUE(CheckAnyOfErrors(rsp_satellite->rspInfo.error,
-                                 {RadioError::NONE,
-                                  RadioError::ABORTED,
-                                  RadioError::ACCESS_BARRED,
-                                  RadioError::BLOCKED_DUE_TO_CALL,
-                                  RadioError::CANCELLED,
-                                  RadioError::ENCODING_ERR,
-                                  RadioError::ENCODING_NOT_SUPPORTED,
-                                  RadioError::INTERNAL_ERR,
-                                  RadioError::INVALID_ARGUMENTS,
-                                  RadioError::INVALID_MODEM_STATE,
-                                  RadioError::INVALID_SIM_STATE,
-                                  RadioError::INVALID_SMS_FORMAT,
-                                  RadioError::INVALID_STATE,
-                                  RadioError::MODEM_ERR,
-                                  RadioError::NETWORK_ERR,
-                                  RadioError::NETWORK_NOT_READY,
-                                  RadioError::NETWORK_REJECT,
-                                  RadioError::NETWORK_TIMEOUT,
-                                  RadioError::NO_MEMORY,
-                                  RadioError::NO_NETWORK_FOUND,
-                                  RadioError::NO_RESOURCES,
-                                  RadioError::NO_SMS_TO_ACK,
-                                  RadioError::NO_SATELLITE_SIGNAL,
-                                  RadioError::NO_SUBSCRIPTION,
-                                  RadioError::NOT_SUFFICIENT_ACCOUNT_BALANCE,
-                                  RadioError::OPERATION_NOT_ALLOWED,
-                                  RadioError::RADIO_NOT_AVAILABLE,
-                                  RadioError::REQUEST_NOT_SUPPORTED,
-                                  RadioError::REQUEST_RATE_LIMITED,
-                                  RadioError::SIM_ABSENT,
-                                  RadioError::SIM_BUSY,
-                                  RadioError::SIM_ERR,
-                                  RadioError::SIM_FULL,
-                                  RadioError::SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED,
-                                  RadioError::SMS_SEND_FAIL_RETRY,
-                                  RadioError::SYSTEM_ERR,
-                                  RadioError::SWITCHED_FROM_SATELLITE_TO_TERRESTRIAL,
-                                  RadioError::UNIDENTIFIED_SUBSCRIBER}));
-}
-
-/*
- * Test IRadioSatellite.getPendingMessages() for the response returned.
- */
-TEST_P(RadioSatelliteTest, getPendingMessages) {
-    serial = GetRandomSerialNumber();
-    ndk::ScopedAStatus res = satellite->getPendingMessages(serial);
-    ASSERT_OK(res);
-    EXPECT_EQ(std::cv_status::no_timeout, wait());
-    EXPECT_EQ(RadioResponseType::SOLICITED, rsp_satellite->rspInfo.type);
-    EXPECT_EQ(serial, rsp_satellite->rspInfo.serial);
-
-    ALOGI("getPendingMessages, rspInfo.error = %s\n",
-          toString(rsp_satellite->rspInfo.error).c_str());
-
-    ASSERT_TRUE(CheckAnyOfErrors(rsp_satellite->rspInfo.error,
-                                 {RadioError::NONE,
-                                  RadioError::ABORTED,
-                                  RadioError::ACCESS_BARRED,
-                                  RadioError::BLOCKED_DUE_TO_CALL,
-                                  RadioError::CANCELLED,
-                                  RadioError::ENCODING_ERR,
-                                  RadioError::ENCODING_NOT_SUPPORTED,
-                                  RadioError::INTERNAL_ERR,
-                                  RadioError::INVALID_ARGUMENTS,
-                                  RadioError::INVALID_MODEM_STATE,
-                                  RadioError::INVALID_SIM_STATE,
-                                  RadioError::INVALID_SMS_FORMAT,
-                                  RadioError::INVALID_STATE,
-                                  RadioError::MODEM_ERR,
-                                  RadioError::NETWORK_ERR,
-                                  RadioError::NETWORK_NOT_READY,
-                                  RadioError::NETWORK_REJECT,
-                                  RadioError::NETWORK_TIMEOUT,
-                                  RadioError::NO_MEMORY,
-                                  RadioError::NO_NETWORK_FOUND,
-                                  RadioError::NO_RESOURCES,
-                                  RadioError::NO_SMS_TO_ACK,
-                                  RadioError::NO_SATELLITE_SIGNAL,
-                                  RadioError::NO_SUBSCRIPTION,
-                                  RadioError::NOT_SUFFICIENT_ACCOUNT_BALANCE,
-                                  RadioError::OPERATION_NOT_ALLOWED,
-                                  RadioError::RADIO_NOT_AVAILABLE,
-                                  RadioError::REQUEST_NOT_SUPPORTED,
-                                  RadioError::REQUEST_RATE_LIMITED,
-                                  RadioError::SIM_ABSENT,
-                                  RadioError::SIM_BUSY,
-                                  RadioError::SIM_ERR,
-                                  RadioError::SIM_FULL,
-                                  RadioError::SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED,
-                                  RadioError::SYSTEM_ERR,
-                                  RadioError::SWITCHED_FROM_SATELLITE_TO_TERRESTRIAL}));
-}
-
-/*
- * Test IRadioSatellite.getSatelliteMode() for the response returned.
- */
-TEST_P(RadioSatelliteTest, getSatelliteMode) {
-    serial = GetRandomSerialNumber();
-    ndk::ScopedAStatus res = satellite->getSatelliteMode(serial);
-    ASSERT_OK(res);
-    EXPECT_EQ(std::cv_status::no_timeout, wait());
-    EXPECT_EQ(RadioResponseType::SOLICITED, rsp_satellite->rspInfo.type);
-    EXPECT_EQ(serial, rsp_satellite->rspInfo.serial);
-
-    ALOGI("getSatelliteMode, rspInfo.error = %s\n", toString(rsp_satellite->rspInfo.error).c_str());
-
-    ASSERT_TRUE(CheckAnyOfErrors(
-            rsp_satellite->rspInfo.error,
-            {RadioError::NONE, RadioError::INTERNAL_ERR, RadioError::INVALID_ARGUMENTS,
-             RadioError::INVALID_MODEM_STATE, RadioError::INVALID_SIM_STATE,
-             RadioError::INVALID_STATE, RadioError::MODEM_ERR, RadioError::NO_MEMORY,
-             RadioError::NO_RESOURCES, RadioError::RADIO_NOT_AVAILABLE,
-             RadioError::REQUEST_NOT_SUPPORTED, RadioError::REQUEST_RATE_LIMITED,
-             RadioError::SYSTEM_ERR}));
-}
-
-/*
- * Test IRadioSatellite.setIndicationFilter() for the response returned.
- */
-TEST_P(RadioSatelliteTest, setIndicationFilter) {
-    serial = GetRandomSerialNumber();
-    ndk::ScopedAStatus res = satellite->setIndicationFilter(serial, 0);
-    ASSERT_OK(res);
-    EXPECT_EQ(std::cv_status::no_timeout, wait());
-    EXPECT_EQ(RadioResponseType::SOLICITED, rsp_satellite->rspInfo.type);
-    EXPECT_EQ(serial, rsp_satellite->rspInfo.serial);
-
-    ALOGI("setIndicationFilter, rspInfo.error = %s\n",
-          toString(rsp_satellite->rspInfo.error).c_str());
-
-    ASSERT_TRUE(CheckAnyOfErrors(
-            rsp_satellite->rspInfo.error,
-            {RadioError::NONE, RadioError::INTERNAL_ERR, RadioError::INVALID_ARGUMENTS,
-             RadioError::INVALID_MODEM_STATE, RadioError::INVALID_SIM_STATE,
-             RadioError::INVALID_STATE, RadioError::MODEM_ERR, RadioError::NO_MEMORY,
-             RadioError::NO_RESOURCES, RadioError::RADIO_NOT_AVAILABLE,
-             RadioError::REQUEST_NOT_SUPPORTED, RadioError::REQUEST_RATE_LIMITED,
-             RadioError::SYSTEM_ERR}));
-}
-
-/*
- * Test IRadioSatellite.startSendingSatellitePointingInfo() for the response returned.
- */
-TEST_P(RadioSatelliteTest, startSendingSatellitePointingInfo) {
-    serial = GetRandomSerialNumber();
-    ndk::ScopedAStatus res = satellite->startSendingSatellitePointingInfo(serial);
-    ASSERT_OK(res);
-    EXPECT_EQ(std::cv_status::no_timeout, wait());
-    EXPECT_EQ(RadioResponseType::SOLICITED, rsp_satellite->rspInfo.type);
-    EXPECT_EQ(serial, rsp_satellite->rspInfo.serial);
-
-    ALOGI("startSendingSatellitePointingInfo, rspInfo.error = %s\n",
-          toString(rsp_satellite->rspInfo.error).c_str());
-
-    ASSERT_TRUE(CheckAnyOfErrors(
-            rsp_satellite->rspInfo.error,
-            {RadioError::NONE, RadioError::INTERNAL_ERR, RadioError::INVALID_ARGUMENTS,
-             RadioError::INVALID_MODEM_STATE, RadioError::INVALID_SIM_STATE,
-             RadioError::INVALID_STATE, RadioError::MODEM_ERR, RadioError::NO_MEMORY,
-             RadioError::NO_RESOURCES, RadioError::RADIO_NOT_AVAILABLE,
-             RadioError::REQUEST_NOT_SUPPORTED, RadioError::REQUEST_RATE_LIMITED,
-             RadioError::SYSTEM_ERR}));
-}
-
-/*
- * Test IRadioSatellite.stopSatelliteLocationUpdate() for the response returned.
- */
-TEST_P(RadioSatelliteTest, stopSatelliteLocationUpdate) {
-    serial = GetRandomSerialNumber();
-    ndk::ScopedAStatus res = satellite->stopSendingSatellitePointingInfo(serial);
-    ASSERT_OK(res);
-    EXPECT_EQ(std::cv_status::no_timeout, wait());
-    EXPECT_EQ(RadioResponseType::SOLICITED, rsp_satellite->rspInfo.type);
-    EXPECT_EQ(serial, rsp_satellite->rspInfo.serial);
-
-    ALOGI("stopSendingSatellitePointingInfo, rspInfo.error = %s\n",
-          toString(rsp_satellite->rspInfo.error).c_str());
-
-    ASSERT_TRUE(CheckAnyOfErrors(
-            rsp_satellite->rspInfo.error,
-            {RadioError::NONE, RadioError::INTERNAL_ERR, RadioError::INVALID_ARGUMENTS,
-             RadioError::INVALID_MODEM_STATE, RadioError::INVALID_SIM_STATE,
-             RadioError::INVALID_STATE, RadioError::MODEM_ERR, RadioError::NO_MEMORY,
-             RadioError::NO_RESOURCES, RadioError::RADIO_NOT_AVAILABLE,
-             RadioError::REQUEST_NOT_SUPPORTED, RadioError::REQUEST_RATE_LIMITED,
-             RadioError::SYSTEM_ERR}));
-}
-
-/*
- * Test IRadioSatellite.getMaxCharactersPerTextMessage() for the response returned.
- */
-TEST_P(RadioSatelliteTest, getMaxCharactersPerTextMessage) {
-    serial = GetRandomSerialNumber();
-    ndk::ScopedAStatus res = satellite->getMaxCharactersPerTextMessage(serial);
-    ASSERT_OK(res);
-    EXPECT_EQ(std::cv_status::no_timeout, wait());
-    EXPECT_EQ(RadioResponseType::SOLICITED, rsp_satellite->rspInfo.type);
-    EXPECT_EQ(serial, rsp_satellite->rspInfo.serial);
-
-    ALOGI("getMaxCharactersPerTextMessage, rspInfo.error = %s\n",
-          toString(rsp_satellite->rspInfo.error).c_str());
-
-    ASSERT_TRUE(CheckAnyOfErrors(
-            rsp_satellite->rspInfo.error,
-            {RadioError::NONE, RadioError::INTERNAL_ERR, RadioError::INVALID_ARGUMENTS,
-             RadioError::INVALID_MODEM_STATE, RadioError::INVALID_SIM_STATE,
-             RadioError::INVALID_STATE, RadioError::MODEM_ERR, RadioError::NO_MEMORY,
-             RadioError::NO_RESOURCES, RadioError::RADIO_NOT_AVAILABLE,
-             RadioError::REQUEST_NOT_SUPPORTED, RadioError::REQUEST_RATE_LIMITED,
-             RadioError::SYSTEM_ERR}));
-}
-
-/*
- * Test IRadioSatellite.getTimeForNextSatelliteVisibility() for the response returned.
- */
-TEST_P(RadioSatelliteTest, getTimeForNextSatelliteVisibility) {
-    serial = GetRandomSerialNumber();
-    ndk::ScopedAStatus res = satellite->getTimeForNextSatelliteVisibility(serial);
-    ASSERT_OK(res);
-    EXPECT_EQ(std::cv_status::no_timeout, wait());
-    EXPECT_EQ(RadioResponseType::SOLICITED, rsp_satellite->rspInfo.type);
-    EXPECT_EQ(serial, rsp_satellite->rspInfo.serial);
-
-    ALOGI("getTimeForNextSatelliteVisibility, rspInfo.error = %s\n",
-          toString(rsp_satellite->rspInfo.error).c_str());
-
-    ASSERT_TRUE(CheckAnyOfErrors(
-            rsp_satellite->rspInfo.error,
-            {RadioError::NONE, RadioError::INTERNAL_ERR, RadioError::INVALID_ARGUMENTS,
-             RadioError::INVALID_MODEM_STATE, RadioError::INVALID_SIM_STATE,
-             RadioError::INVALID_STATE, RadioError::MODEM_ERR, RadioError::NO_MEMORY,
-             RadioError::NO_RESOURCES, RadioError::RADIO_NOT_AVAILABLE,
-             RadioError::REQUEST_NOT_SUPPORTED, RadioError::REQUEST_RATE_LIMITED,
-             RadioError::SYSTEM_ERR}));
-}
diff --git a/radio/aidl/vts/radio_satellite_utils.h b/radio/aidl/vts/radio_satellite_utils.h
deleted file mode 100644
index a0fdc7e..0000000
--- a/radio/aidl/vts/radio_satellite_utils.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <aidl/android/hardware/radio/satellite/BnRadioSatelliteIndication.h>
-#include <aidl/android/hardware/radio/satellite/BnRadioSatelliteResponse.h>
-#include <aidl/android/hardware/radio/satellite/IRadioSatellite.h>
-
-#include "radio_aidl_hal_utils.h"
-
-using namespace aidl::android::hardware::radio::satellite;
-
-class RadioSatelliteTest;
-
-/* Callback class for Satellite response */
-class RadioSatelliteResponse : public BnRadioSatelliteResponse {
-  protected:
-    RadioServiceTest& parent_satellite;
-
-  public:
-    RadioSatelliteResponse(RadioServiceTest& parent_satellite);
-    virtual ~RadioSatelliteResponse() = default;
-
-    RadioResponseInfo rspInfo;
-
-    virtual ndk::ScopedAStatus acknowledgeRequest(int32_t serial) override;
-
-    virtual ndk::ScopedAStatus getCapabilitiesResponse(
-            const RadioResponseInfo& info, const SatelliteCapabilities& capabilities) override;
-
-    virtual ndk::ScopedAStatus setPowerResponse(const RadioResponseInfo& info) override;
-
-    virtual ndk::ScopedAStatus getPowerStateResponse(const RadioResponseInfo& info,
-                                                     bool on) override;
-
-    virtual ndk::ScopedAStatus provisionServiceResponse(const RadioResponseInfo& info,
-                                                        bool provisioned) override;
-
-    virtual ndk::ScopedAStatus addAllowedSatelliteContactsResponse(
-            const RadioResponseInfo& info) override;
-
-    virtual ndk::ScopedAStatus removeAllowedSatelliteContactsResponse(
-            const RadioResponseInfo& info) override;
-
-    virtual ndk::ScopedAStatus sendMessagesResponse(const RadioResponseInfo& info) override;
-
-    virtual ndk::ScopedAStatus getPendingMessagesResponse(
-            const RadioResponseInfo& info, const std::vector<std::string>& /*messages*/) override;
-
-    virtual ndk::ScopedAStatus getSatelliteModeResponse(
-            const RadioResponseInfo& info, SatelliteMode mode,
-            satellite::NTRadioTechnology technology) override;
-
-    virtual ndk::ScopedAStatus setIndicationFilterResponse(const RadioResponseInfo& info) override;
-
-    virtual ndk::ScopedAStatus startSendingSatellitePointingInfoResponse(
-            const RadioResponseInfo& info) override;
-
-    virtual ndk::ScopedAStatus stopSendingSatellitePointingInfoResponse(
-            const RadioResponseInfo& info) override;
-
-    virtual ndk::ScopedAStatus getMaxCharactersPerTextMessageResponse(const RadioResponseInfo& info,
-                                                                      int32_t charLimit) override;
-
-    virtual ndk::ScopedAStatus getTimeForNextSatelliteVisibilityResponse(
-            const RadioResponseInfo& info, int32_t timeInSeconds) override;
-};
-
-/* Callback class for Satellite indication */
-class RadioSatelliteIndication : public BnRadioSatelliteIndication {
-  protected:
-    RadioServiceTest& parent_satellite;
-
-  public:
-    RadioSatelliteIndication(RadioServiceTest& parent_satellite);
-    virtual ~RadioSatelliteIndication() = default;
-
-    virtual ndk::ScopedAStatus onPendingMessageCount(RadioIndicationType type,
-                                                     int32_t count) override;
-
-    virtual ndk::ScopedAStatus onNewMessages(RadioIndicationType type,
-                                             const std::vector<std::string>& messages) override;
-
-    virtual ndk::ScopedAStatus onMessagesTransferComplete(RadioIndicationType type,
-                                                          bool complete) override;
-
-    virtual ndk::ScopedAStatus onSatellitePointingInfoChanged(
-            RadioIndicationType type, const PointingInfo& pointingInfo) override;
-
-    virtual ndk::ScopedAStatus onSatelliteModeChanged(RadioIndicationType type,
-                                                      SatelliteMode mode) override;
-
-    virtual ndk::ScopedAStatus onSatelliteRadioTechnologyChanged(
-            RadioIndicationType type, satellite::NTRadioTechnology technology) override;
-
-    virtual ndk::ScopedAStatus onProvisionStateChanged(
-            RadioIndicationType type, bool provisioned,
-            const std::vector<SatelliteFeature>& features) override;
-};
-
-// The main test class for AIDL Satellite.
-class RadioSatelliteTest : public RadioServiceTest {
-  public:
-    void SetUp() override;
-
-    /* Radio Satellite service handle */
-    std::shared_ptr<IRadioSatellite> satellite;
-    /* Radio Satellite response handle */
-    std::shared_ptr<RadioSatelliteResponse> rsp_satellite;
-    /* Radio Satellite indication handle */
-    std::shared_ptr<RadioSatelliteIndication> ind_satellite;
-};
diff --git a/radio/aidl/vts/radio_sim_test.cpp b/radio/aidl/vts/radio_sim_test.cpp
index d906588..06654c2 100644
--- a/radio/aidl/vts/radio_sim_test.cpp
+++ b/radio/aidl/vts/radio_sim_test.cpp
@@ -65,6 +65,13 @@
  * Test IRadioSim.setSimCardPower() for the response returned.
  */
 TEST_P(RadioSimTest, setSimCardPower) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping setSimCardPower "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     /* Test setSimCardPower power down */
     serial = GetRandomSerialNumber();
     radio_sim->setSimCardPower(serial, CardPowerState::POWER_DOWN);
@@ -120,6 +127,13 @@
  * Test IRadioSim.setCarrierInfoForImsiEncryption() for the response returned.
  */
 TEST_P(RadioSimTest, setCarrierInfoForImsiEncryption) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping setCarrierInfoForImsiEncryption "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     ImsiEncryptionInfo imsiInfo;
     imsiInfo.mcc = "310";
@@ -144,6 +158,13 @@
  * Test IRadioSim.getSimPhonebookRecords() for the response returned.
  */
 TEST_P(RadioSimTest, getSimPhonebookRecords) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping getSimPhonebookRecords "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     radio_sim->getSimPhonebookRecords(serial);
     EXPECT_EQ(std::cv_status::no_timeout, wait());
@@ -167,6 +188,13 @@
  * Test IRadioSim.getSimPhonebookCapacity for the response returned.
  */
 TEST_P(RadioSimTest, getSimPhonebookCapacity) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping getSimPhonebookCapacity "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     radio_sim->getSimPhonebookCapacity(serial);
     EXPECT_EQ(std::cv_status::no_timeout, wait());
@@ -207,6 +235,13 @@
  * Test IRadioSim.updateSimPhonebookRecords() for the response returned.
  */
 TEST_P(RadioSimTest, updateSimPhonebookRecords) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping updateSimPhonebookRecords "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     radio_sim->getSimPhonebookCapacity(serial);
     EXPECT_EQ(std::cv_status::no_timeout, wait());
@@ -271,6 +306,13 @@
  * For SIM ABSENT case.
  */
 TEST_P(RadioSimTest, togglingUiccApplicationsSimAbsent) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping togglingUiccApplicationsSimAbsent "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     // This test case only test SIM ABSENT case.
     if (cardStatus.cardState != CardStatus::STATE_ABSENT) return;
 
@@ -298,6 +340,13 @@
  * For SIM PRESENT case.
  */
 TEST_P(RadioSimTest, togglingUiccApplicationsSimPresent) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping togglingUiccApplicationsSimPresent "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     // This test case only test SIM ABSENT case.
     if (cardStatus.cardState != CardStatus::STATE_PRESENT) return;
     if (cardStatus.applications.size() == 0) return;
@@ -345,6 +394,13 @@
  * Test IRadioSim.areUiccApplicationsEnabled() for the response returned.
  */
 TEST_P(RadioSimTest, areUiccApplicationsEnabled) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping areUiccApplicationsEnabled "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     // Disable Uicc applications.
     serial = GetRandomSerialNumber();
     radio_sim->areUiccApplicationsEnabled(serial);
@@ -365,6 +421,13 @@
  * Test IRadioSim.getAllowedCarriers() for the response returned.
  */
 TEST_P(RadioSimTest, getAllowedCarriers) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping getAllowedCarriers "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_sim->getAllowedCarriers(serial);
@@ -380,6 +443,13 @@
  * Test IRadioSim.setAllowedCarriers() for the response returned.
  */
 TEST_P(RadioSimTest, setAllowedCarriers) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping setAllowedCarriers "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     CarrierRestrictions carrierRestrictions;
     memset(&carrierRestrictions, 0, sizeof(carrierRestrictions));
@@ -479,6 +549,13 @@
  * Test IRadioSim.getIccCardStatus() for the response returned.
  */
 TEST_P(RadioSimTest, getIccCardStatus) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping getIccCardStatus "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     EXPECT_LE(cardStatus.applications.size(), RadioConst::CARD_MAX_APPS);
     EXPECT_LT(cardStatus.gsmUmtsSubscriptionAppIndex, RadioConst::CARD_MAX_APPS);
     EXPECT_LT(cardStatus.cdmaSubscriptionAppIndex, RadioConst::CARD_MAX_APPS);
@@ -489,6 +566,13 @@
  * Test IRadioSim.supplyIccPinForApp() for the response returned
  */
 TEST_P(RadioSimTest, supplyIccPinForApp) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping supplyIccPinForApp "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // Pass wrong password and check PASSWORD_INCORRECT returned for 3GPP and
@@ -514,6 +598,13 @@
  * Test IRadioSim.supplyIccPukForApp() for the response returned.
  */
 TEST_P(RadioSimTest, supplyIccPukForApp) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping supplyIccPukForApp "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // Pass wrong password and check PASSWORD_INCORRECT returned for 3GPP and
@@ -539,6 +630,13 @@
  * Test IRadioSim.supplyIccPin2ForApp() for the response returned.
  */
 TEST_P(RadioSimTest, supplyIccPin2ForApp) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping supplyIccPin2ForApp "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // Pass wrong password and check PASSWORD_INCORRECT returned for 3GPP and
@@ -565,6 +663,13 @@
  * Test IRadioSim.supplyIccPuk2ForApp() for the response returned.
  */
 TEST_P(RadioSimTest, supplyIccPuk2ForApp) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping supplyIccPuk2ForApp "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // Pass wrong password and check PASSWORD_INCORRECT returned for 3GPP and
@@ -590,6 +695,13 @@
  * Test IRadioSim.changeIccPinForApp() for the response returned.
  */
 TEST_P(RadioSimTest, changeIccPinForApp) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping changeIccPinForApp "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // Pass wrong password and check PASSWORD_INCORRECT returned for 3GPP and
@@ -615,6 +727,13 @@
  * Test IRadioSim.changeIccPin2ForApp() for the response returned.
  */
 TEST_P(RadioSimTest, changeIccPin2ForApp) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping changeIccPin2ForApp "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // Pass wrong password and check PASSWORD_INCORRECT returned for 3GPP and
@@ -641,6 +760,13 @@
  * Test IRadioSim.getImsiForApp() for the response returned.
  */
 TEST_P(RadioSimTest, getImsiForApp) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping getImsiForApp "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // Check success returned while getting imsi for 3GPP and 3GPP2 apps only
@@ -670,6 +796,13 @@
  * Test IRadioSim.iccIoForApp() for the response returned.
  */
 TEST_P(RadioSimTest, iccIoForApp) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping iccIoForApp "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     for (int i = 0; i < (int)cardStatus.applications.size(); i++) {
@@ -695,6 +828,13 @@
  * Test IRadioSim.iccTransmitApduBasicChannel() for the response returned.
  */
 TEST_P(RadioSimTest, iccTransmitApduBasicChannel) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping iccTransmitApduBasicChannel "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     SimApdu msg;
     memset(&msg, 0, sizeof(msg));
@@ -710,6 +850,13 @@
  * Test IRadioSim.iccOpenLogicalChannel() for the response returned.
  */
 TEST_P(RadioSimTest, iccOpenLogicalChannel) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping iccOpenLogicalChannel "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     int p2 = 0x04;
     // Specified in ISO 7816-4 clause 7.1.1 0x04 means that FCP template is requested.
@@ -725,6 +872,13 @@
  * Test IRadioSim.iccCloseLogicalChannel() for the response returned.
  */
 TEST_P(RadioSimTest, iccCloseLogicalChannel) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping iccCloseLogicalChannel "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     // Try closing invalid channel and check INVALID_ARGUMENTS returned as error
     radio_sim->iccCloseLogicalChannel(serial, 0);
@@ -739,6 +893,13 @@
  * Test IRadioSim.iccCloseLogicalChannelWithSessionInfo() for the response returned.
  */
 TEST_P(RadioSimTest, iccCloseLogicalChannelWithSessionInfo) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping iccCloseLogicalChannelWithSessionInfo "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     int32_t aidl_version;
     ndk::ScopedAStatus aidl_status = radio_sim->getInterfaceVersion(&aidl_version);
     ASSERT_OK(aidl_status);
@@ -766,6 +927,13 @@
  * Test IRadioSim.iccTransmitApduLogicalChannel() for the response returned.
  */
 TEST_P(RadioSimTest, iccTransmitApduLogicalChannel) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping iccTransmitApduLogicalChannel "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     SimApdu msg;
     memset(&msg, 0, sizeof(msg));
@@ -781,6 +949,13 @@
  * Test IRadioSim.requestIccSimAuthentication() for the response returned.
  */
 TEST_P(RadioSimTest, requestIccSimAuthentication) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping requestIccSimAuthentication "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // Pass wrong challenge string and check RadioError::INVALID_ARGUMENTS
@@ -801,6 +976,13 @@
  * Test IRadioSim.getFacilityLockForApp() for the response returned.
  */
 TEST_P(RadioSimTest, getFacilityLockForApp) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping getFacilityLockForApp "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     std::string facility = "";
     std::string password = "";
@@ -824,6 +1006,13 @@
  * Test IRadioSim.setFacilityLockForApp() for the response returned.
  */
 TEST_P(RadioSimTest, setFacilityLockForApp) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping setFacilityLockForApp "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     std::string facility = "";
     bool lockState = false;
@@ -848,6 +1037,13 @@
  * Test IRadioSim.getCdmaSubscription() for the response returned.
  */
 TEST_P(RadioSimTest, getCdmaSubscription) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+            GTEST_SKIP() << "Skipping getCdmaSubscription "
+                            "due to undefined FEATURE_TELEPHONY_CDMA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_sim->getCdmaSubscription(serial);
@@ -866,6 +1062,13 @@
  * Test IRadioSim.getCdmaSubscriptionSource() for the response returned.
  */
 TEST_P(RadioSimTest, getCdmaSubscriptionSource) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+            GTEST_SKIP() << "Skipping getCdmaSubscriptionSource "
+                            "due to undefined FEATURE_TELEPHONY_CDMA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_sim->getCdmaSubscriptionSource(serial);
@@ -884,6 +1087,13 @@
  * Test IRadioSim.setCdmaSubscriptionSource() for the response returned.
  */
 TEST_P(RadioSimTest, setCdmaSubscriptionSource) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+            GTEST_SKIP() << "Skipping setCdmaSubscriptionSource "
+                            "due to undefined FEATURE_TELEPHONY_CDMA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_sim->setCdmaSubscriptionSource(serial, CdmaSubscriptionSource::RUIM_SIM);
@@ -903,6 +1113,13 @@
  * Test IRadioSim.setUiccSubscription() for the response returned.
  */
 TEST_P(RadioSimTest, setUiccSubscription) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping setUiccSubscription "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     SelectUiccSub item;
     memset(&item, 0, sizeof(item));
@@ -925,6 +1142,13 @@
  * Test IRadioSim.sendEnvelope() for the response returned.
  */
 TEST_P(RadioSimTest, sendEnvelope) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping sendEnvelope "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // Test with sending empty string
@@ -948,6 +1172,13 @@
  * Test IRadioSim.sendTerminalResponseToSim() for the response returned.
  */
 TEST_P(RadioSimTest, sendTerminalResponseToSim) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping sendTerminalResponseToSim "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // Test with sending empty string
@@ -971,6 +1202,13 @@
  * Test IRadioSim.reportStkServiceIsRunning() for the response returned.
  */
 TEST_P(RadioSimTest, reportStkServiceIsRunning) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping reportStkServiceIsRunning "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_sim->reportStkServiceIsRunning(serial);
@@ -990,6 +1228,13 @@
  * string.
  */
 TEST_P(RadioSimTest, sendEnvelopeWithStatus) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            GTEST_SKIP() << "Skipping sendEnvelopeWithStatus "
+                            "due to undefined FEATURE_TELEPHONY_SUBSCRIPTION";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     // Test with sending empty string
diff --git a/radio/aidl/vts/radio_voice_test.cpp b/radio/aidl/vts/radio_voice_test.cpp
index 397c417..6c68fd5 100644
--- a/radio/aidl/vts/radio_voice_test.cpp
+++ b/radio/aidl/vts/radio_voice_test.cpp
@@ -93,15 +93,22 @@
  * Test IRadioVoice.emergencyDial() for the response returned.
  */
 TEST_P(RadioVoiceTest, emergencyDial) {
-    if (!deviceSupportsFeature(FEATURE_VOICE_CALL)) {
-        ALOGI("Skipping emergencyDial because voice call is not supported in device");
-        return;
-    } else if (!deviceSupportsFeature(FEATURE_TELEPHONY_GSM) &&
-               !deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
-        ALOGI("Skipping emergencyDial because gsm/cdma radio is not supported in device");
-        return;
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping emergencyDial "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
     } else {
-        ALOGI("Running emergencyDial because voice call is supported in device");
+        if (!deviceSupportsFeature(FEATURE_VOICE_CALL)) {
+            ALOGI("Skipping emergencyDial because voice call is not supported in device");
+            return;
+        } else if (!deviceSupportsFeature(FEATURE_TELEPHONY_GSM) &&
+                   !deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+            ALOGI("Skipping emergencyDial because gsm/cdma radio is not supported in device");
+            return;
+        } else {
+            ALOGI("Running emergencyDial because voice call is supported in device");
+        }
     }
 
     serial = GetRandomSerialNumber();
@@ -147,15 +154,22 @@
  * Test IRadioVoice.emergencyDial() with specified service and its response returned.
  */
 TEST_P(RadioVoiceTest, emergencyDial_withServices) {
-    if (!deviceSupportsFeature(FEATURE_VOICE_CALL)) {
-        ALOGI("Skipping emergencyDial because voice call is not supported in device");
-        return;
-    } else if (!deviceSupportsFeature(FEATURE_TELEPHONY_GSM) &&
-               !deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
-        ALOGI("Skipping emergencyDial because gsm/cdma radio is not supported in device");
-        return;
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping emergencyDial_withServices "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
     } else {
-        ALOGI("Running emergencyDial because voice call is supported in device");
+        if (!deviceSupportsFeature(FEATURE_VOICE_CALL)) {
+            ALOGI("Skipping emergencyDial because voice call is not supported in device");
+            return;
+        } else if (!deviceSupportsFeature(FEATURE_TELEPHONY_GSM) &&
+                   !deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+            ALOGI("Skipping emergencyDial because gsm/cdma radio is not supported in device");
+            return;
+        } else {
+            ALOGI("Running emergencyDial because voice call is supported in device");
+        }
     }
 
     serial = GetRandomSerialNumber();
@@ -201,15 +215,22 @@
  * Test IRadioVoice.emergencyDial() with known emergency call routing and its response returned.
  */
 TEST_P(RadioVoiceTest, emergencyDial_withEmergencyRouting) {
-    if (!deviceSupportsFeature(FEATURE_VOICE_CALL)) {
-        ALOGI("Skipping emergencyDial because voice call is not supported in device");
-        return;
-    } else if (!deviceSupportsFeature(FEATURE_TELEPHONY_GSM) &&
-               !deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
-        ALOGI("Skipping emergencyDial because gsm/cdma radio is not supported in device");
-        return;
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping emergencyDial_withEmergencyRouting "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
     } else {
-        ALOGI("Running emergencyDial because voice call is supported in device");
+        if (!deviceSupportsFeature(FEATURE_VOICE_CALL)) {
+            ALOGI("Skipping emergencyDial because voice call is not supported in device");
+            return;
+        } else if (!deviceSupportsFeature(FEATURE_TELEPHONY_GSM) &&
+                   !deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+            ALOGI("Skipping emergencyDial because gsm/cdma radio is not supported in device");
+            return;
+        } else {
+            ALOGI("Running emergencyDial because voice call is supported in device");
+        }
     }
 
     serial = GetRandomSerialNumber();
@@ -256,6 +277,13 @@
  * Test IRadioVoice.getCurrentCalls() for the response returned.
  */
 TEST_P(RadioVoiceTest, getCurrentCalls) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping getCurrentCalls "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     radio_voice->getCurrentCalls(serial);
     EXPECT_EQ(std::cv_status::no_timeout, wait());
@@ -268,6 +296,13 @@
  * Test IRadioVoice.getClir() for the response returned.
  */
 TEST_P(RadioVoiceTest, getClir) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping getClir "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->getClir(serial);
@@ -286,6 +321,13 @@
  * Test IRadioVoice.setClir() for the response returned.
  */
 TEST_P(RadioVoiceTest, setClir) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping setClir "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     int32_t status = 1;
 
@@ -304,6 +346,13 @@
  * Test IRadioVoice.getClip() for the response returned.
  */
 TEST_P(RadioVoiceTest, getClip) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping getClip "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->getClip(serial);
@@ -322,6 +371,13 @@
  * Test IRadioVoice.getTtyMode() for the response returned.
  */
 TEST_P(RadioVoiceTest, getTtyMode) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping getTtyMode "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->getTtyMode(serial);
@@ -338,6 +394,13 @@
  * Test IRadioVoice.setTtyMode() for the response returned.
  */
 TEST_P(RadioVoiceTest, setTtyMode) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping setTtyMode "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->setTtyMode(serial, TtyMode::OFF);
@@ -354,6 +417,13 @@
  * Test IRadioVoice.setPreferredVoicePrivacy() for the response returned.
  */
 TEST_P(RadioVoiceTest, setPreferredVoicePrivacy) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping setPreferredVoicePrivacy "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->setPreferredVoicePrivacy(serial, true);
@@ -371,6 +441,13 @@
  * Test IRadioVoice.getPreferredVoicePrivacy() for the response returned.
  */
 TEST_P(RadioVoiceTest, getPreferredVoicePrivacy) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping getPreferredVoicePrivacy "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->getPreferredVoicePrivacy(serial);
@@ -388,6 +465,13 @@
  * Test IRadioVoice.exitEmergencyCallbackMode() for the response returned.
  */
 TEST_P(RadioVoiceTest, exitEmergencyCallbackMode) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping exitEmergencyCallbackMode "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->exitEmergencyCallbackMode(serial);
@@ -406,6 +490,13 @@
  * Test IRadioVoice.handleStkCallSetupRequestFromSim() for the response returned.
  */
 TEST_P(RadioVoiceTest, handleStkCallSetupRequestFromSim) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping handleStkCallSetupRequestFromSim "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     bool accept = false;
 
@@ -427,6 +518,13 @@
  * Test IRadioVoice.dial() for the response returned.
  */
 TEST_P(RadioVoiceTest, dial) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping dial "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     Dial dialInfo;
@@ -454,6 +552,13 @@
  * Test IRadioVoice.hangup() for the response returned.
  */
 TEST_P(RadioVoiceTest, hangup) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping hangup "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->hangup(serial, 1);
@@ -473,6 +578,13 @@
  * Test IRadioVoice.hangupWaitingOrBackground() for the response returned.
  */
 TEST_P(RadioVoiceTest, hangupWaitingOrBackground) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping hangupWaitingOrBackground "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->hangupWaitingOrBackground(serial);
@@ -491,6 +603,13 @@
  * Test IRadioVoice.hangupForegroundResumeBackground() for the response returned.
  */
 TEST_P(RadioVoiceTest, hangupForegroundResumeBackground) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping hangupForegroundResumeBackground "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->hangupForegroundResumeBackground(serial);
@@ -509,6 +628,13 @@
  * Test IRadioVoice.switchWaitingOrHoldingAndActive() for the response returned.
  */
 TEST_P(RadioVoiceTest, switchWaitingOrHoldingAndActive) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping switchWaitingOrHoldingAndActive "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->switchWaitingOrHoldingAndActive(serial);
@@ -527,6 +653,13 @@
  * Test IRadioVoice.conference() for the response returned.
  */
 TEST_P(RadioVoiceTest, conference) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping conference "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->conference(serial);
@@ -545,6 +678,13 @@
  * Test IRadioVoice.rejectCall() for the response returned.
  */
 TEST_P(RadioVoiceTest, rejectCall) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping rejectCall "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->rejectCall(serial);
@@ -563,6 +703,13 @@
  * Test IRadioVoice.getLastCallFailCause() for the response returned.
  */
 TEST_P(RadioVoiceTest, getLastCallFailCause) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping getLastCallFailCause "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->getLastCallFailCause(serial);
@@ -580,6 +727,13 @@
  * Test IRadioVoice.getCallForwardStatus() for the response returned.
  */
 TEST_P(RadioVoiceTest, getCallForwardStatus) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping getCallForwardStatus "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     CallForwardInfo callInfo;
     memset(&callInfo, 0, sizeof(callInfo));
@@ -602,6 +756,13 @@
  * Test IRadioVoice.setCallForward() for the response returned.
  */
 TEST_P(RadioVoiceTest, setCallForward) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping setCallForward "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     CallForwardInfo callInfo;
     memset(&callInfo, 0, sizeof(callInfo));
@@ -624,6 +785,13 @@
  * Test IRadioVoice.getCallWaiting() for the response returned.
  */
 TEST_P(RadioVoiceTest, getCallWaiting) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping getCallWaiting "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->getCallWaiting(serial, 1);
@@ -643,6 +811,13 @@
  * Test IRadioVoice.setCallWaiting() for the response returned.
  */
 TEST_P(RadioVoiceTest, setCallWaiting) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping setCallWaiting "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->setCallWaiting(serial, true, 1);
@@ -662,6 +837,13 @@
  * Test IRadioVoice.acceptCall() for the response returned.
  */
 TEST_P(RadioVoiceTest, acceptCall) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping acceptCall "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->acceptCall(serial);
@@ -680,6 +862,13 @@
  * Test IRadioVoice.separateConnection() for the response returned.
  */
 TEST_P(RadioVoiceTest, separateConnection) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping separateConnection "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->separateConnection(serial, 1);
@@ -699,6 +888,13 @@
  * Test IRadioVoice.explicitCallTransfer() for the response returned.
  */
 TEST_P(RadioVoiceTest, explicitCallTransfer) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping explicitCallTransfer "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->explicitCallTransfer(serial);
@@ -717,6 +913,13 @@
  * Test IRadioVoice.sendCdmaFeatureCode() for the response returned.
  */
 TEST_P(RadioVoiceTest, sendCdmaFeatureCode) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+            GTEST_SKIP() << "Skipping sendCdmaFeatureCode "
+                            "due to undefined FEATURE_TELEPHONY_CDMA";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->sendCdmaFeatureCode(serial, std::string());
@@ -737,6 +940,13 @@
  * Test IRadioVoice.sendDtmf() for the response returned.
  */
 TEST_P(RadioVoiceTest, sendDtmf) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping sendDtmf "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->sendDtmf(serial, "1");
@@ -757,6 +967,13 @@
  * Test IRadioVoice.startDtmf() for the response returned.
  */
 TEST_P(RadioVoiceTest, startDtmf) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping startDtmf "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->startDtmf(serial, "1");
@@ -777,6 +994,13 @@
  * Test IRadioVoice.stopDtmf() for the response returned.
  */
 TEST_P(RadioVoiceTest, stopDtmf) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping stopDtmf "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->stopDtmf(serial);
@@ -796,6 +1020,13 @@
  * Test IRadioVoice.setMute() for the response returned.
  */
 TEST_P(RadioVoiceTest, setMute) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping setMute "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->setMute(serial, true);
@@ -814,6 +1045,13 @@
  * Test IRadioVoice.getMute() for the response returned.
  */
 TEST_P(RadioVoiceTest, getMute) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping getMute "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->getMute(serial);
@@ -830,6 +1068,13 @@
  * Test IRadioVoice.sendBurstDtmf() for the response returned.
  */
 TEST_P(RadioVoiceTest, sendBurstDtmf) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping sendBurstDtmf "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->sendBurstDtmf(serial, "1", 0, 0);
@@ -849,6 +1094,13 @@
  * Test IRadioVoice.sendUssd() for the response returned.
  */
 TEST_P(RadioVoiceTest, sendUssd) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping sendUssd "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
     radio_voice->sendUssd(serial, std::string("test"));
     EXPECT_EQ(std::cv_status::no_timeout, wait());
@@ -867,6 +1119,13 @@
  * Test IRadioVoice.cancelPendingUssd() for the response returned.
  */
 TEST_P(RadioVoiceTest, cancelPendingUssd) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_CALLING)) {
+            GTEST_SKIP() << "Skipping cancelPendingUssd "
+                            "due to undefined FEATURE_TELEPHONY_CALLING";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->cancelPendingUssd(serial);
@@ -886,6 +1145,13 @@
  * Test IRadioVoice.isVoNrEnabled() for the response returned.
  */
 TEST_P(RadioVoiceTest, isVoNrEnabled) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_IMS)) {
+            GTEST_SKIP() << "Skipping isVoNrEnabled "
+                            "due to undefined FEATURE_TELEPHONY_IMS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->isVoNrEnabled(serial);
@@ -901,6 +1167,13 @@
  * Test IRadioVoice.setVoNrEnabled() for the response returned.
  */
 TEST_P(RadioVoiceTest, setVoNrEnabled) {
+    if (telephony_flags::enforce_telephony_feature_mapping()) {
+        if (!deviceSupportsFeature(FEATURE_TELEPHONY_IMS)) {
+            GTEST_SKIP() << "Skipping setVoNrEnabled "
+                            "due to undefined FEATURE_TELEPHONY_IMS";
+        }
+    }
+
     serial = GetRandomSerialNumber();
 
     radio_voice->setVoNrEnabled(serial, true);
diff --git a/rebootescrow/aidl/default/Android.bp b/rebootescrow/aidl/default/Android.bp
index 4409314..7f9b6d6 100644
--- a/rebootescrow/aidl/default/Android.bp
+++ b/rebootescrow/aidl/default/Android.bp
@@ -42,10 +42,10 @@
 
 cc_binary {
     name: "android.hardware.rebootescrow-service.default",
-    init_rc: ["rebootescrow-default.rc"],
     relative_install_path: "hw",
-    vintf_fragments: ["rebootescrow-default.xml"],
     vendor: true,
+    installable: false, // installed in APEX
+
     srcs: [
         "service.cpp",
     ],
@@ -53,12 +53,14 @@
         "-Wall",
         "-Werror",
     ],
+    stl: "c++_static",
     shared_libs: [
-        "libbase",
         "libbinder_ndk",
-        "android.hardware.rebootescrow-V1-ndk",
+        "liblog",
     ],
     static_libs: [
+        "android.hardware.rebootescrow-V1-ndk",
+        "libbase",
         "libhadamardutils",
         "librebootescrowdefaultimpl",
     ],
@@ -97,3 +99,35 @@
     ],
     test_suites: ["device-tests"],
 }
+
+prebuilt_etc {
+    name: "rebootescrow-default.rc",
+    src: "rebootescrow-default.rc",
+    installable: false,
+}
+
+prebuilt_etc {
+    name: "rebootescrow-default.xml",
+    src: "rebootescrow-default.xml",
+    sub_dir: "vintf",
+    installable: false,
+}
+
+apex {
+    name: "com.android.hardware.rebootescrow",
+    manifest: "apex_manifest.json",
+    file_contexts: "apex_file_contexts",
+    key: "com.android.hardware.key",
+    certificate: ":com.android.hardware.certificate",
+    vendor: true,
+    updatable: false,
+
+    binaries: [
+        "android.hardware.rebootescrow-service.default",
+    ],
+    prebuilts: [
+        "rebootescrow-default.rc",
+        "rebootescrow-default.xml",
+        "android.hardware.reboot_escrow.prebuilt.xml", // <feature>
+    ],
+}
diff --git a/rebootescrow/aidl/default/apex_file_contexts b/rebootescrow/aidl/default/apex_file_contexts
new file mode 100644
index 0000000..aa84984
--- /dev/null
+++ b/rebootescrow/aidl/default/apex_file_contexts
@@ -0,0 +1,3 @@
+(/.*)?                                                      u:object_r:vendor_file:s0
+/etc(/.*)?                                                  u:object_r:vendor_configs_file:s0
+/bin/hw/android\.hardware\.rebootescrow-service\.default    u:object_r:hal_rebootescrow_default_exec:s0
diff --git a/rebootescrow/aidl/default/apex_manifest.json b/rebootescrow/aidl/default/apex_manifest.json
new file mode 100644
index 0000000..8be495b
--- /dev/null
+++ b/rebootescrow/aidl/default/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+    "name": "com.android.hardware.rebootescrow",
+    "version": 1
+}
\ No newline at end of file
diff --git a/rebootescrow/aidl/default/rebootescrow-default.rc b/rebootescrow/aidl/default/rebootescrow-default.rc
index ad90465..024dd6d 100644
--- a/rebootescrow/aidl/default/rebootescrow-default.rc
+++ b/rebootescrow/aidl/default/rebootescrow-default.rc
@@ -1,4 +1,4 @@
-service vendor.rebootescrow-default /vendor/bin/hw/android.hardware.rebootescrow-service.default
+service vendor.rebootescrow-default /apex/com.android.hardware.rebootescrow/bin/hw/android.hardware.rebootescrow-service.default
     interface aidl android.hardware.rebootescrow.IRebootEscrow/default
     class hal
     user system
diff --git a/secure_element/aidl/default/Android.bp b/secure_element/aidl/default/Android.bp
index d1bb393..b382822 100644
--- a/secure_element/aidl/default/Android.bp
+++ b/secure_element/aidl/default/Android.bp
@@ -11,14 +11,50 @@
     name: "android.hardware.secure_element-service.example",
     relative_install_path: "hw",
     vendor: true,
-    init_rc: ["secure_element.rc"],
-    vintf_fragments: ["secure_element.xml"],
+    installable: false, // installed in APEX
+
+    stl: "c++_static",
     shared_libs: [
-        "libbase",
         "libbinder_ndk",
+        "liblog",
+    ],
+    static_libs: [
         "android.hardware.secure_element-V1-ndk",
+        "libbase",
     ],
     srcs: [
         "main.cpp",
     ],
 }
+
+prebuilt_etc {
+    name: "secure_element.rc",
+    src: "secure_element.rc",
+    installable: false,
+}
+
+prebuilt_etc {
+    name: "secure_element.xml",
+    src: "secure_element.xml",
+    sub_dir: "vintf",
+    installable: false,
+}
+
+apex {
+    name: "com.android.hardware.secure_element",
+    manifest: "apex_manifest.json",
+    file_contexts: "apex_file_contexts",
+    key: "com.android.hardware.key",
+    certificate: ":com.android.hardware.certificate",
+    vendor: true,
+    updatable: false,
+
+    binaries: [
+        "android.hardware.secure_element-service.example",
+    ],
+    prebuilts: [
+        "secure_element.rc",
+        "secure_element.xml",
+        "android.hardware.se.omapi.ese.prebuilt.xml", // <feature>
+    ],
+}
diff --git a/secure_element/aidl/default/apex_file_contexts b/secure_element/aidl/default/apex_file_contexts
new file mode 100644
index 0000000..e9e811e
--- /dev/null
+++ b/secure_element/aidl/default/apex_file_contexts
@@ -0,0 +1,3 @@
+(/.*)?                                                      u:object_r:vendor_file:s0
+/etc(/.*)?                                                  u:object_r:vendor_configs_file:s0
+/bin/hw/android\.hardware\.secure_element-service\.example  u:object_r:hal_secure_element_default_exec:s0
\ No newline at end of file
diff --git a/secure_element/aidl/default/apex_manifest.json b/secure_element/aidl/default/apex_manifest.json
new file mode 100644
index 0000000..6e04c11
--- /dev/null
+++ b/secure_element/aidl/default/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+    "name": "com.android.hardware.secure_element",
+    "version": 1
+}
\ No newline at end of file
diff --git a/secure_element/aidl/default/secure_element.rc b/secure_element/aidl/default/secure_element.rc
index 7d21666..b74b2ee 100644
--- a/secure_element/aidl/default/secure_element.rc
+++ b/secure_element/aidl/default/secure_element.rc
@@ -1,4 +1,4 @@
-service vendor.secure_element /vendor/bin/hw/android.hardware.secure_element-service.example
+service vendor.secure_element /apex/com.android.hardware.secure_element/bin/hw/android.hardware.secure_element-service.example
     class hal
     user nobody
     group nobody
diff --git a/security/authgraph/aidl/Android.bp b/security/authgraph/aidl/Android.bp
new file mode 100644
index 0000000..d94f640
--- /dev/null
+++ b/security/authgraph/aidl/Android.bp
@@ -0,0 +1,88 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.security.authgraph",
+    vendor_available: true,
+    srcs: [
+        "android/hardware/security/authgraph/*.aidl",
+    ],
+    stability: "vintf",
+    frozen: false,
+    backend: {
+        java: {
+            platform_apis: true,
+        },
+        ndk: {
+            apps_enabled: false,
+        },
+        rust: {
+            enabled: true,
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.virt",
+            ],
+        },
+    },
+}
+
+// cc_defaults that includes the latest Authgraph AIDL library.
+// Modules that depend on Authgraph directly can include this cc_defaults to avoid
+// managing dependency versions explicitly.
+cc_defaults {
+    name: "authgraph_use_latest_hal_aidl_ndk_static",
+    static_libs: [
+        "android.hardware.security.authgraph-V1-ndk",
+    ],
+}
+
+cc_defaults {
+    name: "authgraph_use_latest_hal_aidl_ndk_shared",
+    shared_libs: [
+        "android.hardware.security.authgraph-V1-ndk",
+    ],
+}
+
+cc_defaults {
+    name: "authgraph_use_latest_hal_aidl_cpp_static",
+    static_libs: [
+        "android.hardware.security.authgraph-V1-cpp",
+    ],
+}
+
+cc_defaults {
+    name: "authgraph_use_latest_hal_aidl_cpp_shared",
+    shared_libs: [
+        "android.hardware.security.authgraph-V1-cpp",
+    ],
+}
+
+// A rust_defaults that includes the latest Authgraph AIDL library.
+// Modules that depend on Authgraph directly can include this rust_defaults to avoid
+// managing dependency versions explicitly.
+rust_defaults {
+    name: "authgraph_use_latest_hal_aidl_rust",
+    rustlibs: [
+        "android.hardware.security.authgraph-V1-rust",
+    ],
+}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Arc.aidl
similarity index 87%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
copy to security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Arc.aidl
index b9df1f5..dc86fbd 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Arc.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,9 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
+package android.hardware.security.authgraph;
 /* @hide */
-@Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+parcelable Arc {
+  byte[] arc;
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/SatelliteCapabilities.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Error.aidl
similarity index 72%
rename from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/SatelliteCapabilities.aidl
rename to security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Error.aidl
index e0af71a..1a78b54 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/SatelliteCapabilities.aidl
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Error.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,13 +31,20 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
+package android.hardware.security.authgraph;
 /* @hide */
-@JavaDerive(toString=true) @VintfStability
-parcelable SatelliteCapabilities {
-  android.hardware.radio.satellite.NTRadioTechnology[] supportedRadioTechnologies;
-  boolean isAlwaysOn;
-  boolean needsPointingToSatellite;
-  android.hardware.radio.satellite.SatelliteFeature[] supportedFeatures;
-  boolean needsSeparateSimProfile;
+@Backing(type="int") @VintfStability
+enum Error {
+  OK = 0,
+  INVALID_PEER_NONCE = (-1) /* -1 */,
+  INVALID_PEER_KE_KEY = (-2) /* -2 */,
+  INVALID_IDENTITY = (-3) /* -3 */,
+  INVALID_CERT_CHAIN = (-4) /* -4 */,
+  INVALID_SIGNATURE = (-5) /* -5 */,
+  INVALID_KE_KEY = (-6) /* -6 */,
+  INVALID_PUB_KEY_IN_KEY = (-7) /* -7 */,
+  INVALID_PRIV_KEY_ARC_IN_KEY = (-8) /* -8 */,
+  INVALID_SHARED_KEY_ARCS = (-9) /* -9 */,
+  MEMORY_ALLOCATION_FAILED = (-10) /* -10 */,
+  INCOMPATIBLE_PROTOCOL_VERSION = (-11) /* -11 */,
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/SatelliteCapabilities.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/IAuthGraphKeyExchange.aidl
similarity index 62%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/SatelliteCapabilities.aidl
copy to security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/IAuthGraphKeyExchange.aidl
index e0af71a..2c56f33 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/SatelliteCapabilities.aidl
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/IAuthGraphKeyExchange.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,13 +31,12 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
+package android.hardware.security.authgraph;
 /* @hide */
-@JavaDerive(toString=true) @VintfStability
-parcelable SatelliteCapabilities {
-  android.hardware.radio.satellite.NTRadioTechnology[] supportedRadioTechnologies;
-  boolean isAlwaysOn;
-  boolean needsPointingToSatellite;
-  android.hardware.radio.satellite.SatelliteFeature[] supportedFeatures;
-  boolean needsSeparateSimProfile;
+@VintfStability
+interface IAuthGraphKeyExchange {
+  android.hardware.security.authgraph.SessionInitiationInfo create();
+  android.hardware.security.authgraph.KeInitResult init(in android.hardware.security.authgraph.PubKey peerPubKey, in android.hardware.security.authgraph.Identity peerId, in byte[] peerNonce, in int peerVersion);
+  android.hardware.security.authgraph.SessionInfo finish(in android.hardware.security.authgraph.PubKey peerPubKey, in android.hardware.security.authgraph.Identity peerId, in android.hardware.security.authgraph.SessionIdSignature peerSignature, in byte[] peerNonce, in int peerVersion, in android.hardware.security.authgraph.Key ownKey);
+  android.hardware.security.authgraph.Arc[2] authenticationComplete(in android.hardware.security.authgraph.SessionIdSignature peerSignature, in android.hardware.security.authgraph.Arc[2] sharedKeys);
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Identity.aidl
similarity index 87%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
copy to security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Identity.aidl
index b9df1f5..bd5453e 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Identity.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,8 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
-/* @hide */
-@Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+package android.hardware.security.authgraph;
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+parcelable Identity {
+  byte[] identity;
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/KeInitResult.aidl
similarity index 82%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
copy to security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/KeInitResult.aidl
index b9df1f5..8c91523 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/KeInitResult.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,9 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
-/* @hide */
-@Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+package android.hardware.security.authgraph;
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+parcelable KeInitResult {
+  android.hardware.security.authgraph.SessionInitiationInfo sessionInitiationInfo;
+  android.hardware.security.authgraph.SessionInfo sessionInfo;
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Key.aidl
similarity index 83%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
copy to security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Key.aidl
index b9df1f5..5b4ebbf 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/Key.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,9 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
-/* @hide */
-@Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+package android.hardware.security.authgraph;
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+parcelable Key {
+  @nullable android.hardware.security.authgraph.PubKey pubKey;
+  @nullable android.hardware.security.authgraph.Arc arcFromPBK;
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/PlainPubKey.aidl
similarity index 87%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
copy to security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/PlainPubKey.aidl
index b9df1f5..f070bfa 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/PlainPubKey.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,8 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
-/* @hide */
-@Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+package android.hardware.security.authgraph;
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+parcelable PlainPubKey {
+  byte[] plainPubKey;
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/PubKey.aidl
similarity index 83%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
copy to security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/PubKey.aidl
index b9df1f5..4c3376e 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/PubKey.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,9 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
-/* @hide */
-@Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+package android.hardware.security.authgraph;
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+union PubKey {
+  android.hardware.security.authgraph.PlainPubKey plainKey;
+  android.hardware.security.authgraph.SignedPubKey signedKey;
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SessionIdSignature.aidl
similarity index 87%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
copy to security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SessionIdSignature.aidl
index b9df1f5..6dabc0a 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SessionIdSignature.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,8 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
-/* @hide */
-@Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+package android.hardware.security.authgraph;
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+parcelable SessionIdSignature {
+  byte[] signature;
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SessionInfo.aidl
similarity index 82%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
copy to security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SessionInfo.aidl
index b9df1f5..427962b 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SessionInfo.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,10 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
-/* @hide */
-@Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+package android.hardware.security.authgraph;
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+parcelable SessionInfo {
+  android.hardware.security.authgraph.Arc[2] sharedKeys;
+  byte[] sessionId;
+  android.hardware.security.authgraph.SessionIdSignature signature;
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SessionInitiationInfo.aidl
similarity index 82%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
copy to security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SessionInitiationInfo.aidl
index b9df1f5..bf55e74 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SessionInitiationInfo.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,11 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
-/* @hide */
-@Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+package android.hardware.security.authgraph;
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+parcelable SessionInitiationInfo {
+  android.hardware.security.authgraph.Key key;
+  android.hardware.security.authgraph.Identity identity;
+  byte[] nonce;
+  int version;
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SignedPubKey.aidl
similarity index 87%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
copy to security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SignedPubKey.aidl
index b9df1f5..3dbaed8 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/security/authgraph/aidl/aidl_api/android.hardware.security.authgraph/current/android/hardware/security/authgraph/SignedPubKey.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,8 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
-/* @hide */
-@Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+package android.hardware.security.authgraph;
+@RustDerive(Clone=true, Eq=true, PartialEq=true) @VintfStability
+parcelable SignedPubKey {
+  byte[] signedPubKey;
 }
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/Arc.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/Arc.aidl
new file mode 100644
index 0000000..855ce5c
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/Arc.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+/**
+ * This is the definition of the data format of an Arc.
+ * @hide
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+parcelable Arc {
+    /**
+     * The messages exchanged between the domains in the AuthGraph protocol are called Arcs.
+     * An arc is simply AES-GCM. Encryption of a payload P with a key K and additional
+     * authentication data (AAD) D: (i.e. Arc = Enc(K, P, D)).
+     *
+     * Data is CBOR-encoded according to the `Arc` CDDL definition in Arc.cddl.
+     */
+    byte[] arc;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/Arc.cddl b/security/authgraph/aidl/android/hardware/security/authgraph/Arc.cddl
new file mode 100644
index 0000000..4c1b965
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/Arc.cddl
@@ -0,0 +1,115 @@
+;
+; Copyright (C) 2023 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+Arc = [                            ; COSE_Encrypt0 [RFC9052 s5.2]
+    protected : bstr .cbor ArcProtectedHeaders,
+    unprotected : {
+        5 : bstr .size 12          ; IV
+    },
+    ciphertext : bstr    ; Enc(K, bstr .cbor Payload, encoded ArcEncStruct)
+]
+
+ArcProtectedHeaders = {
+           1 : 3,                  ; Algorithm: AES-GCM mode w/ 256-bit key, 128-bit tag
+    ? -70001 : { + Permission }, ; One or more Permissions
+    ? -70002 : { + Limitation }, ; One or more Limitations
+    ? -70003 : int,   ; Timestamp in milliseconds since some starting point (generally
+                      ; the most recent device boot) which all of the applications within
+                      ; the secure domain must agree upon
+    ? -70004 : bstr .size 16,      ; Nonce used in key exchange methods
+    ? -70005 : PayloadType,        ; Payload type, if needed to disambiguate, when processing an arc
+    ? -70006 : int,                ; Version of the payload structure (if applicable)
+    ? -70007 : int,                ; Sequence number (if needed to prevent replay attacks)
+    ? -70008 : Direction           ; Direction of the encryption key (i.e. whether it is used to
+                                   ; encrypt incoming messages or outgoing messages)
+    ? -70009 : bool,               ; "authentication_completed" - this is used during authenticated
+                                   ; key exchange indicate whether signature verification is done
+    ? -70010 : bstr .size 32       ; "session_id" computed during key exchange protocol
+}
+
+; Permissions indicate what an arc can be used with. Permissions are added to an arc during the
+; `create()` primitive operation and are propagated during `mint` and `snap` primitive operations.
+Permission = &(
+    -4770552 : IdentityEncoded,  ; "source_id" - in the operations performed by a source, the
+                                 ; source adds its own identity to the permissions of an arc.
+    -4770553 : IdentityEncoded,  ; "sink_id" - in the operations performed by a sink, the sink
+                                 ; adds its own identity to the permissions of an arc.
+    -4770555 : [ +IdentityEncoded ]  ; "minting_allowed" - defines the set of TA identities
+                                     ; to whom the payload key is allowed to be minted.
+    -4770556 : bool                  ; "deleted_on_biometric_change" - A Boolean value that
+                                     ; indicates whether an auth key issued from a biometric TA is
+                                     ; invalidated on new biometric enrollment or removal of all
+                                     ; biometrics.
+)
+
+; Limitations indicate what restrictions are applied on the usage of an arc. Permissions are added
+; to an arc during the `create` primitive operation and are propagated during `snap` primitive
+; operation.
+Limitation = &(
+    -4770554 : bstr,      ; "challenge" - is added to an arc that transfers an auth key to a channel
+                          ; key, in order to ensure the freshness of the authentication.
+                          ; A challenge is issued by a sink (e.g. Keymint TA, Biometric TAs).
+)
+
+; INCLUDE Identity.cddl for: Identity
+IdentityEncoded = bstr .cbor Identity
+
+Direction = &(
+    In:  1,
+    Out: 2,
+)
+
+PayloadType = &(
+    SecretKey: 1,
+    Arc: 2,
+    ; Any other payload types should also be defined here
+)
+
+Payload = &(
+    SecretKey,
+    Arc,
+    ; Any other payload formats should also be defined here
+)
+
+SecretKey = &(                     ; One of the payload types of an Arc is a secret key
+    SymmetricKey,
+    ECPrivateKey,    ; Private key of a key pair generated for key exchange
+)
+
+ECPrivateKey = {              ; COSE_Key [RFC9052 s7]
+    1 : 2,                    ; Key type : EC2
+    3 : -25,                  ; Algorithm: ECDH ES w/ HKDF 256 - generate key directly
+ ?  4 : [7],                  ; Key_ops: [derive key]
+   -1 : 1,                    ; Curve: P-256
+ ? -2 : bstr,                 ; x coordinate
+ ? -3 : bstr,                 ; y coordinate
+   -4 : bstr,                 ; private key (d)
+}
+
+SymmetricKey = {              ; COSE_Key [RFC9052 s7] - For symmetric key encryption
+    1 : 4,                    ; Key type : Symmetric
+    3 : 3,                    ; Algorithm : AES-GCM mode w/ 256-bit key, 128-bit tag
+    4 : [ 4 ],                ; Key_ops: [decrypt]
+   -1 : bstr .size 32,        ; Key value (k)
+}
+
+ArcEncStruct = [              ; COSE_Enc_structure [RFC9052 s5.3]
+    context   : "Encrypt0",
+    protected : bstr .cbor ArcProtectedHeaders,
+    external_aad : bstr .size 0,
+]
+
+; INCLUDE generateCertificateRequestV2.cddl for: PubKeyEd25519, PubKeyECDSA256, PubKeyECDSA384
+; from hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/DicePolicy.cddl b/security/authgraph/aidl/android/hardware/security/authgraph/DicePolicy.cddl
new file mode 100644
index 0000000..a7dcbc6
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/DicePolicy.cddl
@@ -0,0 +1,33 @@
+;
+; Copyright (C) 2023 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+DicePolicy = [
+    1, ; dice policy version
+    + nodeConstraintList ; for each entry in dice chain
+]
+
+nodeConstraintList = [
+    * nodeConstraint
+]
+
+; We may add a hashConstraint item later
+nodeConstraint = exactMatchConstraint / geConstraint
+
+exactMatchConstraint = [1, keySpec, value]
+geConstraint = [2, keySpec, int]
+
+keySpec = [value+]
+
+value = bool / int / tstr / bstr
\ No newline at end of file
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/Error.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/Error.aidl
new file mode 100644
index 0000000..1ad6054
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/Error.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+/**
+ * AuthGraph error codes. Aidl will return these error codes as service specific errors in
+ * EX_SERVICE_SPECIFIC.
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+enum Error {
+    /* Success */
+    OK = 0,
+    /* Invalid peer nonce for key exchange */
+    INVALID_PEER_NONCE = -1,
+    /* Invalid key exchange public key by the peer */
+    INVALID_PEER_KE_KEY = -2,
+    /* Invalid identity of the peer */
+    INVALID_IDENTITY = -3,
+    /* Invalid certificate chain in the identity of the peer */
+    INVALID_CERT_CHAIN = -4,
+    /* Invalid signature by the peer */
+    INVALID_SIGNATURE = -5,
+    /* Invalid key exchange key created by a particular party themselves to be used as a handle */
+    INVALID_KE_KEY = -6,
+    /* Invalid public key in the `Key` struct */
+    INVALID_PUB_KEY_IN_KEY = -7,
+    /* Invalid private key arc in the `Key` struct */
+    INVALID_PRIV_KEY_ARC_IN_KEY = -8,
+    /* Invalid shared key arcs */
+    INVALID_SHARED_KEY_ARCS = -9,
+    /* Memory allocation failed */
+    MEMORY_ALLOCATION_FAILED = -10,
+    /* The protocol version negotiated with the sink is incompatible */
+    INCOMPATIBLE_PROTOCOL_VERSION = -11,
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/ExplicitKeyDiceCertChain.cddl b/security/authgraph/aidl/android/hardware/security/authgraph/ExplicitKeyDiceCertChain.cddl
new file mode 100644
index 0000000..3de5617
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/ExplicitKeyDiceCertChain.cddl
@@ -0,0 +1,30 @@
+;
+; Copyright (C) 2023 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+ExplicitKeyDiceCertChain = [
+    1, ; version, hopefully will never change
+    DiceCertChainInitialPayload,
+    * DiceChainEntry
+]
+
+DiceCertChainInitialPayload = {
+    -4670552 : bstr .cbor PubKeyEd25519 /
+               bstr .cbor PubKeyECDSA256 /
+               bstr .cbor PubKeyECDSA384 ; subjectPublicKey
+}
+
+; INCLUDE generateCertificateRequestV2.cddl for: PubKeyEd25519, PubKeyECDSA256, PubKeyECDSA384,
+;                                                DiceChainEntry
+; from hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/IAuthGraphKeyExchange.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/IAuthGraphKeyExchange.aidl
new file mode 100644
index 0000000..6ceb09c
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/IAuthGraphKeyExchange.aidl
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+import android.hardware.security.authgraph.Arc;
+import android.hardware.security.authgraph.Identity;
+import android.hardware.security.authgraph.KeInitResult;
+import android.hardware.security.authgraph.Key;
+import android.hardware.security.authgraph.PubKey;
+import android.hardware.security.authgraph.SessionIdSignature;
+import android.hardware.security.authgraph.SessionInfo;
+import android.hardware.security.authgraph.SessionInitiationInfo;
+
+/**
+ * AuthGraph interface definition for authenticated key exchange between two parties: P1 (source)
+ * and P2 (sink).
+ * Pre-requisites: each participant should have a:
+ *     1. Persistent identity - e.g. a signing key pair with a self signed certificate or a DICE
+ *        certificate chain.
+ *     2. A symmetric encryption key kept in memory with per-boot life time of the participant
+ *        (a.k.a per-boot key)
+ *
+ * ErrorCodes are defined in android.hardware.security.authgraph.ErrorCode.aidl.
+ * @hide
+ */
+@VintfStability
+interface IAuthGraphKeyExchange {
+    /**
+     * This method is invoked on P1 (source).
+     * Create an ephermeral EC key pair on NIST curve P-256 and a nonce (of 16 bytes) for
+     * key exchange.
+     *
+     * @return SessionInitiationInfo including the `Key` containing the public key of the created
+     * key pair and an arc from the per-boot key to the private key, the nonce, the persistent
+     * identity and the latest protocol version (i.e. AIDL version) supported.
+     *
+     * Note: The arc from the per-boot key to the private key in `Key` of the return type:
+     * `SessionInitiationInfo` serves two purposes:
+     * i. A mapping to correlate `create` and `finish` calls to P1 in a particular instance of the
+     *    key exchange protocol.
+     * ii.A way to minimize the in-memory storage (P1 can include the nonce in the protected headers
+     *    of the arc).
+     * However, P1 should maintain some form of in-memory record to be able to verify that the input
+     * `Key` sent to `finish` is from an unfinished instance of a key exchange protocol, to prevent
+     * any replay attacks in `finish`.
+     */
+    SessionInitiationInfo create();
+
+    /**
+     * This method is invoked on P2 (sink).
+     * Perform the following steps for key exchange:
+     *     0. If either `peerPubKey`, `peerId`, `peerNonce` is not in the expected format, return
+     *        errors: INVALID_PEER_KE_KEY, INVALID_IDENTITY, INVALID_PEER_NONCE respectively.
+     *     1. Create an ephemeral EC key pair on NIST curve P-256.
+     *     2. Create a nonce (of 16 bytes).
+     *     3. Compute the diffie-hellman shared secret: Z.
+     *     4. Compute a salt = bstr .cbor [
+     *            source_version:    int,                    ; from input `peerVersion`
+     *            sink_pub_key:      bstr .cbor PlainPubKey, ; from step #1
+     *            source_pub_key:    bstr .cbor PlainPubKey, ; from input `peerPubKey`
+     *            sink_nonce:        bstr .size 16,          ; from step #2
+     *            source_nonce:      bstr .size 16,          ; from input `peerNonce`
+     *            sink_cert_chain:   bstr .cbor ExplicitKeyDiceCertChain, ; from own identity
+     *            source_cert_chain: bstr .cbor ExplicitKeyDiceCertChain, ; from input `peerId`
+     *        ]
+     *     5. Extract a cryptographic secret S from Z, using the salt from #4 above.
+     *     6. Derive two symmetric encryption keys of 256 bits with:
+     *        i. b"KE_ENCRYPTION_KEY_SOURCE_TO_SINK" as context for the key used to encrypt incoming
+     *           messages
+     *       ii. b"KE_ENCRYPTION_KEY_SINK_TO_SOURCE" as context for the key used to encrypt outgoing
+     *           messages
+     *     7. Create arcs from the per-boot key to each of the two shared keys from step #6 and
+     *        mark authentication_complete = false in arcs' protected headers.
+     *     8. Derive a MAC key with b"KE_HMAC_KEY" as the context.
+     *     9. Compute session_id_input = bstr .cbor [
+     *            sink_nonce:     bstr .size 16,
+     *            source_nonce:   bstr .size 16,
+     *        ],
+     *     10.Compute a session_id as a 256 bits HMAC over the session_id_input from step#9 with
+     *        the key from step #8.
+     *     11.Create a signature over the session_id from step #10, using the signing key which is
+     *        part of the party's identity.
+     *
+     * @param peerPubKey - the public key of the key pair created by the peer (P1) for key exchange
+     *
+     * @param peerId - the persistent identity of the peer
+     *
+     * @param peerNonce - nonce created by the peer
+     *
+     * @param peerVersion - an integer representing the latest protocol version (i.e. AIDL version)
+     *                      supported by the peer
+     *
+     * @return KeInitResult including the `Key` containing the public key of the created key pair,
+     * the nonce, the persistent identity, two shared key arcs from step #7, session id, signature
+     * over the session id and the negotiated protocol version. The negotiated protocol version
+     * should be less than or equal to the peer's version.
+     *
+     * Note: The two shared key arcs in the return type: `KeInitResult` serves two purposes:
+     * i. A mapping to correlate `init` and `authenticationComplete` calls to P2 in a particular
+     *    instance of the key exchange protocol.
+     * ii.A way to minimize the in-memory storage of P2 allocated for key exchange.
+     * However, P2 should maintain some in-memory record to be able to verify that the input
+     * `sharedkeys` sent to `authenticationComplete` and to any subsequent AuthGraph protocol
+     * methods are valid shared keys agreed with the party identified by `peerId`, to prevent
+     * any replay attacks in `authenticationComplete` and in any subsequent AuthGraph protocol
+     * methods which use the shared keys to encrypt the secret messages.
+     */
+    KeInitResult init(
+            in PubKey peerPubKey, in Identity peerId, in byte[] peerNonce, in int peerVersion);
+
+    /**
+     * This method is invoked on P1 (source).
+     * Perform the following steps:
+     *     0. If either `peerPubKey`, `peerId`, `peerNonce` is not in the expected format, return
+     *        errors: INVALID_PEER_KE_KEY, INVALID_IDENTITY, INVALID_PEER_NONCE respectively. If
+     *        `peerVersion` is greater than the version advertised in `create`, return error:
+     *        INCOMPATIBLE_PROTOCOL_VERSION.
+     *        If `ownKey` is not in the in-memory records for unfinished instances of a key
+     *        exchange protocol, return error: INVALID_KE_KEY. Similarly, if the public key or the
+     *        arc containing the private key in `ownKey` is invalid, return INVALID_PUB_KEY_IN_KEY
+     *        and INVALID_PRIV_KEY_ARC_IN_KEY respectively.
+     *     1. Compute the diffie-hellman shared secret: Z.
+     *     2. Compute a salt = bstr .cbor [
+     *            source_version:    int,                    ; the protocol version used in `create`
+     *            sink_pub_key:      bstr .cbor PlainPubKey, ; from input `peerPubKey`
+     *            source_pub_key:    bstr .cbor PlainPubKey, ; from the output of `create`
+     *            sink_nonce:        bstr .size 16,          ; from input `peerNonce`
+     *            source_nonce:      bstr .size 16,          ; from the output of `create`
+     *            sink_cert_chain:   bstr .cbor ExplicitKeyDiceCertChain, ; from input `peerId`
+     *            source_cert_chain: bstr .cbor ExplicitKeyDiceCertChain, ; from own identity
+     *        ]
+     *     3. Extract a cryptographic secret S from Z, using the salt from #2 above.
+     *     4. Derive two symmetric encryption keys of 256 bits with:
+     *        i. b"KE_ENCRYPTION_KEY_SOURCE_TO_SINK" as context for the key used to encrypt outgoing
+     *           messages
+     *       ii. b"KE_ENCRYPTION_KEY_SINK_TO_SOURCE" as context for the key used to encrypt incoming
+     *           messages
+     *     5. Derive a MAC key with b"KE_HMAC_KEY" as the context.
+     *     6. Compute session_id_input = bstr .cbor [
+     *            sink_nonce:     bstr .size 16,
+     *            source_nonce:   bstr .size 16,
+     *        ],
+     *     7. Compute a session_id as a 256 bits HMAC over the session_id_input from step #6 with
+     *        the key from step #5.
+     *     8. Verify the peer's signature over the session_id from step #7. If successful, proceed,
+     *        otherwise, return error: INVALID_SIGNATURE.
+     *     9. Create arcs from the per-boot key to each of the two shared keys from step #4 and
+     *        mark authentication_complete = true in arcs' protected headers.
+     *     10.Create a signature over the session_id from step #7, using the signing key which is
+     *        part of the party's identity.
+     *
+     * @param peerPubKey - the public key of the key pair created by the peer (P2) for key exchange
+     *
+     * @param peerId - the persistent identity of the peer
+     *
+     * @param peerSignature - the signature created by the peer over the session id computed by the
+     *                        peer
+     *
+     * @param peerNonce - nonce created by the peer
+     *
+     * @param peerVersion - an integer representing the protocol version (i.e. AIDL version)
+     *                      negotiated with the peer
+     *
+     * @param ownKey - the key created by P1 (source) in `create()` for key exchange
+     *
+     * @return SessionInfo including the two shared key arcs from step #9, session id and the
+     * signature over the session id.
+     *
+     * Note: The two shared key arcs in the return type: `SessionInfo` serves two purposes:
+     * i. A mapping to correlate the key exchange protocol taken place with a particular peer and
+     *    subsequent AuthGraph protocols execued with the same peer.
+     * ii.A way to minimize the in-memory storage for shared keys.
+     * However, P1 should maintain some in-memory record to be able to verify that the shared key
+     * arcs sent to any subsequent AuthGraph protocol methods are valid shared keys agreed with the
+     * party identified by `peerId`, to prevent any replay attacks.
+     */
+    SessionInfo finish(in PubKey peerPubKey, in Identity peerId,
+            in SessionIdSignature peerSignature, in byte[] peerNonce, in int peerVersion,
+            in Key ownKey);
+
+    /**
+     * This method is invoked on P2 (sink).
+     * Perform the following steps:
+     *   0. If input `sharedKeys` is invalid (i.e. they cannot be decrypted with P2's per-boot key
+     *      or they are not in P2's in-memory records as valid shared keys agreed with the party
+     *      identified by `peerId`), return error: INVALID_SHARED_KEY_ARCS.
+     *   1. Verify that both shared key arcs have the same session id and peer identity.
+     *   2. Verify the peer's signature over the session id attached to the shared key arcs'
+     *      headers. If successful, proceed, otherwise, return error: INVALID_SIGNATURE.
+     *   3. Mark authentication_complete = true in the shared key arcs' headers
+     *
+     * @param peerSignature - the signature created by the peer over the session id computed by the
+     *                        peer
+     *
+     * @param sharedKeys - two shared key arcs created by P2 in `init`. P2 obtains from the arcs'
+     *                     protected headers, the session id and the peer's identity to verify the
+     *                     peer's signature over the session id.
+     *
+     * @return Arc[] - an array of two updated shared key arcs
+     */
+    Arc[2] authenticationComplete(in SessionIdSignature peerSignature, in Arc[2] sharedKeys);
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/Identity.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/Identity.aidl
new file mode 100644
index 0000000..9e350e8
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/Identity.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+/**
+ * Persistent (versioned) identity of a participant of Authgraph key exchange.
+ * Identity consists of two main parts:
+ *     1. a certificate chain (e.g. a DICE certificate chain)
+ *     2. (optional) a policy specifying how to verify the certificate chain - if a policy is not
+ *        provided, a simple byte-to-byte comparison of the certificate chain is assumed.
+ *
+ * During identity verification, the certificate chain of the identity attached to the access
+ * request is compared against the policy of the identity attached to the persistent resources.
+ *
+ * The usage of policy based identity verification in Authgraph is three-fold:
+ *        1. Retain access to persistent resources for the newer versions of the party who
+ *           created them, even when parts of the certificate chain are updated in the new version.
+ *        2. Deny access to the new persistent resources for the older versions of the party
+ *           who created the new persistent resources.
+ *        3. Trigger rotation of critical keys encrypted in persistent arcs created by the previous
+ *           version of the party, by including an updated policy in the identity attached to the
+ *           access request.
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+parcelable Identity {
+    /* Data is CBOR-encoded according to the `Identity` CDDL definition in Identity.cddl */
+    byte[] identity;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/Identity.cddl b/security/authgraph/aidl/android/hardware/security/authgraph/Identity.cddl
new file mode 100644
index 0000000..0419421
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/Identity.cddl
@@ -0,0 +1,23 @@
+;
+; Copyright (C) 2023 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+Identity = [
+    1, ; Version
+    cert_chain: bstr .cbor ExplicitKeyDiceCertChain,
+    policy: bstr .cbor DicePolicy / nil,
+]
+
+; INCLUDE ExplicitKeyDiceCertChain.cddl for: ExplicitKeyDiceCertChain
+; INCLUDE DicePolicy.cddl for: DicePolicy
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/KeInitResult.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/KeInitResult.aidl
new file mode 100644
index 0000000..b4ae451
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/KeInitResult.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+import android.hardware.security.authgraph.SessionInfo;
+import android.hardware.security.authgraph.SessionInitiationInfo;
+
+/**
+ * The return type for the init() step of authenticated key exchange.
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+parcelable KeInitResult {
+    /**
+     * Session initiation information.
+     */
+    SessionInitiationInfo sessionInitiationInfo;
+
+    /**
+     * Session information.
+     */
+    SessionInfo sessionInfo;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/Key.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/Key.aidl
new file mode 100644
index 0000000..11fe174
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/Key.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+import android.hardware.security.authgraph.Arc;
+import android.hardware.security.authgraph.PubKey;
+
+/**
+ * The type that encapsulates a key. Key can be either a symmetric key or an asymmetric key.
+ * If it is an asymmetric key, it is used for key exchange.
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+parcelable Key {
+    /**
+     * If the Key is an asymmetric key, public key should be present.
+     */
+    @nullable PubKey pubKey;
+
+    /**
+     * Arc from the per-boot key to the payload key. The payload key is either the symmetric key
+     * or the private key of an asymmetric key, based on the type of the key being created.
+     * This is marked as optional because there are instances where only the public key is returned,
+     * e.g. `init` method in the key exchange protocol.
+     */
+    @nullable Arc arcFromPBK;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/PlainPubKey.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/PlainPubKey.aidl
new file mode 100644
index 0000000..5483ec5
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/PlainPubKey.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+/**
+ * One of the two enum variants of the enum type: `PubKey`. This represents the plain public key
+ * material encoded as a COSE_Key.
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+parcelable PlainPubKey {
+    /* Data is CBOR-encoded according to the `PlainPubKey` CDDL definition in PlainPubKey.cddl */
+    byte[] plainPubKey;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/PlainPubKey.cddl b/security/authgraph/aidl/android/hardware/security/authgraph/PlainPubKey.cddl
new file mode 100644
index 0000000..34b316b
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/PlainPubKey.cddl
@@ -0,0 +1,24 @@
+;
+; Copyright (C) 2023 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+
+; P-256 public key for key exchange.
+PlainPubKey = [ ; COSE_Key [RFC9052 s7]
+    1 : 2,                       ; Key type : EC2
+    3 : -27,                     ; Algorithm : ECDH-SS + HKDF-256
+    -1 : 1,                      ; Curve: P256
+    -2 : bstr,                   ; X coordinate, big-endian
+    -3 : bstr                    ; Y coordinate, big-endian
+]
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/PubKey.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/PubKey.aidl
new file mode 100644
index 0000000..8640871
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/PubKey.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+import android.hardware.security.authgraph.PlainPubKey;
+import android.hardware.security.authgraph.SignedPubKey;
+
+/**
+ * The enum type representing the public key of an asymmetric key pair.
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+union PubKey {
+    /**
+     * Plain public key material encoded as a COSE_Key.
+     */
+    PlainPubKey plainKey;
+
+    /**
+     * Public key signed with the long term signing key of the party.
+     */
+    SignedPubKey signedKey;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/SessionIdSignature.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/SessionIdSignature.aidl
new file mode 100644
index 0000000..2fa8b4c
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/SessionIdSignature.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+/**
+ * Signature computed by a party over the session id during authenticated key exchange.
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+parcelable SessionIdSignature {
+    /* Data is CBOR-encoded according to the `SessionIdSignature` CDDL definition in
+     * SessionIdSignature.cddl */
+    byte[] signature;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/SessionIdSignature.cddl b/security/authgraph/aidl/android/hardware/security/authgraph/SessionIdSignature.cddl
new file mode 100644
index 0000000..038a0f0
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/SessionIdSignature.cddl
@@ -0,0 +1,33 @@
+;
+; Copyright (C) 2023 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+SessionIdSignature = [ ; COSE_Sign1 (untagged) [RFC9052 s4.2]
+    protected: bstr .cbor SessionIdSignatureProtected,
+    unprotected: {},
+    payload: nil,       ; session ID payload to be transported separately
+    signature: bstr     ; PureEd25519(privateKey, SessionIdSignatureSigStruct) /
+                        ; ECDSA(privateKey, SessionIdSignatureSigStruct)
+]
+
+SessionIdSignatureProtected = {
+    1 : AlgorithmEdDSA / AlgorithmES256,
+}
+
+SessionIdSignatureSigStruct = [ ; Sig_structure for SessionIdSignature [ RFC9052 s4.4]
+    context: "Signature1",
+    protected: bstr SessionIdSignatureProtected,
+    external_aad: bstr .size 0,
+    payload: bstr,       ; session ID payload provided separately
+]
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/SessionInfo.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/SessionInfo.aidl
new file mode 100644
index 0000000..ef49a1a
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/SessionInfo.aidl
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+import android.hardware.security.authgraph.Arc;
+import android.hardware.security.authgraph.SessionIdSignature;
+
+/**
+ * Session information returned as part of authenticated key exchange.
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+parcelable SessionInfo {
+    /**
+     * The arcs that encrypt the two derived symmetric encryption keys (for two-way communication)
+     * from the party's per-boot key.
+     */
+    Arc[2] sharedKeys;
+
+    /**
+     * The value of the session id computed by the two parties during the authenticate key
+     * exchange. Apart from the usage of the session id by the two peers, session id is also useful
+     * to verify (by a third party) that the key exchange was successful.
+     */
+    byte[] sessionId;
+
+    /**
+     * The signature over the session id, created by the party who computed the session id.
+     *
+     * If there is one or more `DiceChainEntry` in the `ExplicitKeyDiceCertChain` of the party's
+     * identity, the signature is verified with the public key in the leaf of the chain of
+     * DiceChainEntries (i.e the public key in the last of the array of DiceChainEntries).
+     * Otherwise, the signature is verified with the `DiceCertChainInitialPayload`.
+     */
+    SessionIdSignature signature;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/SessionInitiationInfo.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/SessionInitiationInfo.aidl
new file mode 100644
index 0000000..c630d91
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/SessionInitiationInfo.aidl
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+import android.hardware.security.authgraph.Arc;
+import android.hardware.security.authgraph.Identity;
+import android.hardware.security.authgraph.Key;
+
+/**
+ * Session initiation information returned as part of authenticated key exchange.
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+parcelable SessionInitiationInfo {
+    /**
+     * An ephemeral EC key created for the ECDH process.
+     */
+    Key key;
+
+    /**
+     * The identity of the party who created the Diffie-Hellman key exchange key.
+     */
+    Identity identity;
+
+    /**
+     * Nonce value specific to this session. The nonce serves three purposes:
+     * 1. freshness of key exchange
+     * 2. creating a session id (a publicly known value related to the exchanged keys)
+     * 3. usage as salt into the HKDF-EXTRACT function during key derivation from the shared DH key
+     */
+    byte[] nonce;
+
+    /**
+     * The protocol version (i.e. AIDL version) - This is used to prevent version downgrade attacks
+     * as follows:
+     * 1. In `create`, the source advertises the latest protocol version supported by the source,
+     *    which is given as input to the `init` call on the sink in the input parameter:
+     *    `peerVersion`.
+     * 2. In `init`, the sink includes the `peerVersion` in the inputs to the derivation of the
+     *    shared keys. Then the sink returns the latest protocol version supported by the sink,
+     *    which is given as input to the `finish` call on the source in the input parameter:
+     *    `peerVersion`.
+     * 3. In `finish`, the source first checks whether the sink's version is equal or less than the
+     *    source's version and includes in the source's version in the inputs to the derivation of
+     *    the shared keys.
+     * Analysis: if an attacker-in-the-middle wanted the two parties to use an older (vulnerable)
+     * version of the protocol, they can invoke `init` with a version that is lower than the version
+     * advertised by the source in `create`. However, since both parties include the source's
+     * version in the inputs to the derivation of the shared keys, the two parties won't end up with
+     * the same shared keys in the presence of such an attack. This is detected when checking the
+     * signature on the session id in `finish`, at which point the protocol aborts. Therefore,
+     * an attacker cannot successfully launch a version downgrade attack on this protocol.
+     */
+    int version;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/SignedPubKey.aidl b/security/authgraph/aidl/android/hardware/security/authgraph/SignedPubKey.aidl
new file mode 100644
index 0000000..72ee219
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/SignedPubKey.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.authgraph;
+
+/**
+ * One of the two enum variants of the enum type: `PubKey`. This represents the public key signed
+ * with the long term signing key of the party.
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true)
+parcelable SignedPubKey {
+    /* Data is CBOR-encoded according to the `SignedPubKey` CDDL definition in SignedPubKey.cddl */
+    byte[] signedPubKey;
+}
diff --git a/security/authgraph/aidl/android/hardware/security/authgraph/SignedPubKey.cddl b/security/authgraph/aidl/android/hardware/security/authgraph/SignedPubKey.cddl
new file mode 100644
index 0000000..f23a492
--- /dev/null
+++ b/security/authgraph/aidl/android/hardware/security/authgraph/SignedPubKey.cddl
@@ -0,0 +1,41 @@
+;
+; Copyright (C) 2023 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+SignedPubKey = [ ; COSE_Sign1 (untagged) [RFC9052 s4.2]
+    protected: bstr .cbor SignedPubKeyProtected,
+    unprotected: {},
+    payload: bstr .cbor PlainPubKey,
+    signature: bstr     ; PureEd25519(privateKey, SignedPubKeySigStruct) /
+                        ; ECDSA(privateKey, SignedPubKeySigStruct)
+]
+
+SignedPubKeyProtected = {
+           1 : AlgorithmEdDSA / AlgorithmES256,
+    ? -70011 : Identity,        ; the party who performs the signing operation adds its own
+                                ; identity to the protected headers.
+}
+
+SignedPubKeySigStruct = [ ; Sig_structure for SignedPubKey [ RFC9052 s4.4]
+    context: "Signature1",
+    protected: bstr SignedPubKeyProtected,
+    external_aad: bstr .size 0,
+    payload: bstr .cbor PlainPubKey,
+]
+
+AlgorithmES256 = -7              ; [RFC9053 s2.1]
+AlgorithmEdDSA = -8              ; [RFC9053 s2.2]
+
+; INCLUDE PlainPubKey.cddl for: PlainPubKey
+; INCLUDE Identity.cddl for: Identity
\ No newline at end of file
diff --git a/security/authgraph/aidl/vts/functional/Android.bp b/security/authgraph/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..0e3480f
--- /dev/null
+++ b/security/authgraph/aidl/vts/functional/Android.bp
@@ -0,0 +1,81 @@
+//
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsAidlAuthGraphSessionTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "authgraph_use_latest_hal_aidl_ndk_static",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+    ],
+    srcs: [
+        "AuthGraphSessionTest.cpp",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+        "libcrypto",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
+
+rust_test {
+    name: "VtsAidlAuthGraphRoleTest",
+    srcs: ["role_test.rs"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+    defaults: [
+        "authgraph_use_latest_hal_aidl_rust",
+    ],
+    rustlibs: [
+        "libauthgraph_vts_test",
+        "libbinder_rs",
+    ],
+}
+
+rust_library {
+    name: "libauthgraph_vts_test",
+    crate_name: "authgraph_vts_test",
+    srcs: ["lib.rs"],
+    defaults: [
+        "authgraph_use_latest_hal_aidl_rust",
+    ],
+    rustlibs: [
+        "libauthgraph_boringssl",
+        "libauthgraph_core",
+        "libauthgraph_hal",
+        "libauthgraph_nonsecure",
+        "libbinder_rs",
+        "libcoset",
+    ],
+}
diff --git a/security/authgraph/aidl/vts/functional/AuthGraphSessionTest.cpp b/security/authgraph/aidl/vts/functional/AuthGraphSessionTest.cpp
new file mode 100644
index 0000000..d9dea77
--- /dev/null
+++ b/security/authgraph/aidl/vts/functional/AuthGraphSessionTest.cpp
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "authgraph_session_test"
+#include <android-base/logging.h>
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/security/authgraph/Error.h>
+#include <aidl/android/hardware/security/authgraph/IAuthGraphKeyExchange.h>
+#include <android/binder_manager.h>
+#include <binder/ProcessState.h>
+#include <gtest/gtest.h>
+#include <vector>
+
+namespace aidl::android::hardware::security::authgraph::test {
+using ::aidl::android::hardware::security::authgraph::Error;
+
+namespace {
+
+// Check that the signature in the encoded COSE_Sign1 data is correct, and that the payload matches.
+// TODO: maybe drop separate payload, and extract it from cose_sign1.payload (and return it).
+void CheckSignature(std::vector<uint8_t>& /*pub_cose_key*/, std::vector<uint8_t>& /*payload*/,
+                    std::vector<uint8_t>& /*cose_sign1*/) {
+    // TODO: implement me
+}
+
+void CheckSignature(std::vector<uint8_t>& pub_cose_key, std::vector<uint8_t>& payload,
+                    SessionIdSignature& signature) {
+    return CheckSignature(pub_cose_key, payload, signature.signature);
+}
+
+std::vector<uint8_t> SigningKeyFromIdentity(const Identity& identity) {
+    // TODO: This is a CBOR-encoded `Identity` which currently happens to be a COSE_Key with the
+    // pubkey This will change in future.
+    return identity.identity;
+}
+
+}  // namespace
+
+class AuthGraphSessionTest : public ::testing::TestWithParam<std::string> {
+  public:
+    enum ErrorType { AIDL_ERROR, BINDER_ERROR };
+
+    union ErrorValue {
+        Error aidl_error;
+        int32_t binder_error;
+    };
+
+    struct ReturnedError {
+        ErrorType err_type;
+        ErrorValue err_val;
+
+        friend bool operator==(const ReturnedError& lhs, const ReturnedError& rhs) {
+            return lhs.err_type == rhs.err_type;
+            switch (lhs.err_type) {
+                case ErrorType::AIDL_ERROR:
+                    return lhs.err_val.aidl_error == rhs.err_val.aidl_error;
+                case ErrorType::BINDER_ERROR:
+                    return lhs.err_val.binder_error == rhs.err_val.binder_error;
+            }
+        }
+    };
+
+    const ReturnedError OK = {.err_type = ErrorType::AIDL_ERROR, .err_val.aidl_error = Error::OK};
+
+    ReturnedError GetReturnError(const ::ndk::ScopedAStatus& result) {
+        if (result.isOk()) {
+            return OK;
+        }
+        int32_t exception_code = result.getExceptionCode();
+        int32_t error_code = result.getServiceSpecificError();
+        if (exception_code == EX_SERVICE_SPECIFIC && error_code != 0) {
+            ReturnedError re = {.err_type = ErrorType::AIDL_ERROR,
+                                .err_val.aidl_error = static_cast<Error>(error_code)};
+            return re;
+        }
+        ReturnedError re = {.err_type = ErrorType::BINDER_ERROR,
+                            .err_val.binder_error = exception_code};
+        return re;
+    }
+
+    // Build the parameters for the VTS test by enumerating the available HAL instances
+    static std::vector<std::string> build_params() {
+        auto params = ::android::getAidlHalInstanceNames(IAuthGraphKeyExchange::descriptor);
+        return params;
+    }
+
+    void SetUp() override {
+        ASSERT_TRUE(AServiceManager_isDeclared(GetParam().c_str()))
+                << "No instance declared for " << GetParam();
+        ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+        authNode_ = IAuthGraphKeyExchange::fromBinder(binder);
+        ASSERT_NE(authNode_, nullptr) << "Failed to get Binder reference for " << GetParam();
+    }
+
+    void TearDown() override {}
+
+  protected:
+    std::shared_ptr<IAuthGraphKeyExchange> authNode_;
+};
+
+TEST_P(AuthGraphSessionTest, Mainline) {
+    std::shared_ptr<IAuthGraphKeyExchange> source = authNode_;
+    std::shared_ptr<IAuthGraphKeyExchange> sink = authNode_;
+
+    // Step 1: create an ephemeral ECDH key at the source.
+    SessionInitiationInfo source_init_info;
+    ASSERT_EQ(OK, GetReturnError(source->create(&source_init_info)));
+    ASSERT_TRUE(source_init_info.key.pubKey.has_value());
+    ASSERT_TRUE(source_init_info.key.arcFromPBK.has_value());
+
+    // Step 2: pass the source's ECDH public key and other session info to the sink.
+    KeInitResult init_result;
+    ASSERT_EQ(OK, GetReturnError(sink->init(source_init_info.key.pubKey.value(),
+                                            source_init_info.identity, source_init_info.nonce,
+                                            source_init_info.version, &init_result)));
+    SessionInitiationInfo sink_init_info = init_result.sessionInitiationInfo;
+    ASSERT_TRUE(sink_init_info.key.pubKey.has_value());
+    // The sink_init_info.arcFromPBK need not be populated, as the ephemeral key agreement
+    // key is no longer needed.
+
+    SessionInfo sink_info = init_result.sessionInfo;
+    ASSERT_EQ((int)sink_info.sharedKeys.size(), 2) << "Expect two symmetric keys from init()";
+    ASSERT_GT((int)sink_info.sessionId.size(), 0) << "Expect non-empty session ID from sink";
+    std::vector<uint8_t> sink_signing_key = SigningKeyFromIdentity(sink_init_info.identity);
+    CheckSignature(sink_signing_key, sink_info.sessionId, sink_info.signature);
+
+    // Step 3: pass the sink's ECDH public key and other session info to the source, so it can
+    // calculate the same pair of symmetric keys.
+    SessionInfo source_info;
+    ASSERT_EQ(OK, GetReturnError(source->finish(sink_init_info.key.pubKey.value(),
+                                                sink_init_info.identity, sink_info.signature,
+                                                sink_init_info.nonce, sink_init_info.version,
+                                                source_init_info.key, &source_info)));
+    ASSERT_EQ((int)source_info.sharedKeys.size(), 2) << "Expect two symmetric keys from finsh()";
+    ASSERT_GT((int)source_info.sessionId.size(), 0) << "Expect non-empty session ID from source";
+    std::vector<uint8_t> source_signing_key = SigningKeyFromIdentity(source_init_info.identity);
+    CheckSignature(source_signing_key, source_info.sessionId, source_info.signature);
+
+    // Both ends should agree on the session ID.
+    ASSERT_EQ(source_info.sessionId, sink_info.sessionId);
+
+    // Step 4: pass the source's session ID info back to the sink, so it can check it and
+    // update the symmetric keys so they're marked as authentication complete.
+    std::array<Arc, 2> auth_complete_result;
+    ASSERT_EQ(OK, GetReturnError(sink->authenticationComplete(
+                          source_info.signature, sink_info.sharedKeys, &auth_complete_result)));
+    ASSERT_EQ((int)auth_complete_result.size(), 2)
+            << "Expect two symmetric keys from authComplete()";
+    sink_info.sharedKeys = auth_complete_result;
+
+    // At this point the sink and source have agreed on the same pair of symmetric keys,
+    // encoded as `sink_info.sharedKeys` and `source_info.sharedKeys`.
+}
+
+TEST_P(AuthGraphSessionTest, ParallelSink) {
+    std::shared_ptr<IAuthGraphKeyExchange> source = authNode_;
+    std::shared_ptr<IAuthGraphKeyExchange> sink1 = authNode_;
+    std::shared_ptr<IAuthGraphKeyExchange> sink2 = authNode_;
+
+    // Step 1: create ephemeral ECDH keys at the source.
+    SessionInitiationInfo source_init1_info;
+    ASSERT_EQ(OK, GetReturnError(source->create(&source_init1_info)));
+    ASSERT_TRUE(source_init1_info.key.pubKey.has_value());
+    ASSERT_TRUE(source_init1_info.key.arcFromPBK.has_value());
+    SessionInitiationInfo source_init2_info;
+    ASSERT_EQ(OK, GetReturnError(source->create(&source_init2_info)));
+    ASSERT_TRUE(source_init2_info.key.pubKey.has_value());
+    ASSERT_TRUE(source_init2_info.key.arcFromPBK.has_value());
+
+    // Step 2: pass the source's ECDH public keys and other session info to the sinks.
+    KeInitResult init1_result;
+    ASSERT_EQ(OK, GetReturnError(sink1->init(source_init1_info.key.pubKey.value(),
+                                             source_init1_info.identity, source_init1_info.nonce,
+                                             source_init1_info.version, &init1_result)));
+    SessionInitiationInfo sink1_init_info = init1_result.sessionInitiationInfo;
+    ASSERT_TRUE(sink1_init_info.key.pubKey.has_value());
+
+    SessionInfo sink1_info = init1_result.sessionInfo;
+    ASSERT_EQ((int)sink1_info.sharedKeys.size(), 2) << "Expect two symmetric keys from init()";
+    ASSERT_GT((int)sink1_info.sessionId.size(), 0) << "Expect non-empty session ID from sink";
+    std::vector<uint8_t> sink1_signing_key = SigningKeyFromIdentity(sink1_init_info.identity);
+    CheckSignature(sink1_signing_key, sink1_info.sessionId, sink1_info.signature);
+    KeInitResult init2_result;
+    ASSERT_EQ(OK, GetReturnError(sink2->init(source_init2_info.key.pubKey.value(),
+                                             source_init2_info.identity, source_init2_info.nonce,
+                                             source_init2_info.version, &init2_result)));
+    SessionInitiationInfo sink2_init_info = init2_result.sessionInitiationInfo;
+    ASSERT_TRUE(sink2_init_info.key.pubKey.has_value());
+
+    SessionInfo sink2_info = init2_result.sessionInfo;
+    ASSERT_EQ((int)sink2_info.sharedKeys.size(), 2) << "Expect two symmetric keys from init()";
+    ASSERT_GT((int)sink2_info.sessionId.size(), 0) << "Expect non-empty session ID from sink";
+    std::vector<uint8_t> sink2_signing_key = SigningKeyFromIdentity(sink2_init_info.identity);
+    CheckSignature(sink2_signing_key, sink2_info.sessionId, sink2_info.signature);
+
+    // Step 3: pass each sink's ECDH public key and other session info to the source, so it can
+    // calculate the same pair of symmetric keys.
+    SessionInfo source_info1;
+    ASSERT_EQ(OK, GetReturnError(source->finish(sink1_init_info.key.pubKey.value(),
+                                                sink1_init_info.identity, sink1_info.signature,
+                                                sink1_init_info.nonce, sink1_init_info.version,
+                                                source_init1_info.key, &source_info1)));
+    ASSERT_EQ((int)source_info1.sharedKeys.size(), 2) << "Expect two symmetric keys from finsh()";
+    ASSERT_GT((int)source_info1.sessionId.size(), 0) << "Expect non-empty session ID from source";
+    std::vector<uint8_t> source_signing_key1 = SigningKeyFromIdentity(source_init1_info.identity);
+    CheckSignature(source_signing_key1, source_info1.sessionId, source_info1.signature);
+    SessionInfo source_info2;
+    ASSERT_EQ(OK, GetReturnError(source->finish(sink2_init_info.key.pubKey.value(),
+                                                sink2_init_info.identity, sink2_info.signature,
+                                                sink2_init_info.nonce, sink2_init_info.version,
+                                                source_init2_info.key, &source_info2)));
+    ASSERT_EQ((int)source_info2.sharedKeys.size(), 2) << "Expect two symmetric keys from finsh()";
+    ASSERT_GT((int)source_info2.sessionId.size(), 0) << "Expect non-empty session ID from source";
+    std::vector<uint8_t> source_signing_key2 = SigningKeyFromIdentity(source_init2_info.identity);
+    CheckSignature(source_signing_key2, source_info2.sessionId, source_info2.signature);
+
+    // Both ends should agree on the session ID.
+    ASSERT_EQ(source_info1.sessionId, sink1_info.sessionId);
+    ASSERT_EQ(source_info2.sessionId, sink2_info.sessionId);
+
+    // Step 4: pass the source's session ID info back to the sink, so it can check it and
+    // update the symmetric keys so they're marked as authentication complete.
+    std::array<Arc, 2> auth_complete_result1;
+    ASSERT_EQ(OK, GetReturnError(sink1->authenticationComplete(
+                          source_info1.signature, sink1_info.sharedKeys, &auth_complete_result1)));
+    ASSERT_EQ((int)auth_complete_result1.size(), 2)
+            << "Expect two symmetric keys from authComplete()";
+    sink1_info.sharedKeys = auth_complete_result1;
+    std::array<Arc, 2> auth_complete_result2;
+    ASSERT_EQ(OK, GetReturnError(sink2->authenticationComplete(
+                          source_info2.signature, sink2_info.sharedKeys, &auth_complete_result2)));
+    ASSERT_EQ((int)auth_complete_result2.size(), 2)
+            << "Expect two symmetric keys from authComplete()";
+    sink2_info.sharedKeys = auth_complete_result2;
+}
+
+TEST_P(AuthGraphSessionTest, ParallelSource) {
+    std::shared_ptr<IAuthGraphKeyExchange> source1 = authNode_;
+    std::shared_ptr<IAuthGraphKeyExchange> source2 = authNode_;
+    std::shared_ptr<IAuthGraphKeyExchange> sink = authNode_;
+
+    // Step 1: create an ephemeral ECDH key at each of the sources.
+    SessionInitiationInfo source1_init_info;
+    ASSERT_EQ(OK, GetReturnError(source1->create(&source1_init_info)));
+    ASSERT_TRUE(source1_init_info.key.pubKey.has_value());
+    ASSERT_TRUE(source1_init_info.key.arcFromPBK.has_value());
+    SessionInitiationInfo source2_init_info;
+    ASSERT_EQ(OK, GetReturnError(source1->create(&source2_init_info)));
+    ASSERT_TRUE(source2_init_info.key.pubKey.has_value());
+    ASSERT_TRUE(source2_init_info.key.arcFromPBK.has_value());
+
+    // Step 2: pass each source's ECDH public key and other session info to the sink.
+    KeInitResult init1_result;
+    ASSERT_EQ(OK, GetReturnError(sink->init(source1_init_info.key.pubKey.value(),
+                                            source1_init_info.identity, source1_init_info.nonce,
+                                            source1_init_info.version, &init1_result)));
+    SessionInitiationInfo sink_init1_info = init1_result.sessionInitiationInfo;
+    ASSERT_TRUE(sink_init1_info.key.pubKey.has_value());
+
+    SessionInfo sink_info1 = init1_result.sessionInfo;
+    ASSERT_EQ((int)sink_info1.sharedKeys.size(), 2) << "Expect two symmetric keys from init()";
+    ASSERT_GT((int)sink_info1.sessionId.size(), 0) << "Expect non-empty session ID from sink";
+    std::vector<uint8_t> sink_signing_key1 = SigningKeyFromIdentity(sink_init1_info.identity);
+    CheckSignature(sink_signing_key1, sink_info1.sessionId, sink_info1.signature);
+
+    KeInitResult init2_result;
+    ASSERT_EQ(OK, GetReturnError(sink->init(source2_init_info.key.pubKey.value(),
+                                            source2_init_info.identity, source2_init_info.nonce,
+                                            source2_init_info.version, &init2_result)));
+    SessionInitiationInfo sink_init2_info = init2_result.sessionInitiationInfo;
+    ASSERT_TRUE(sink_init2_info.key.pubKey.has_value());
+
+    SessionInfo sink_info2 = init2_result.sessionInfo;
+    ASSERT_EQ((int)sink_info2.sharedKeys.size(), 2) << "Expect two symmetric keys from init()";
+    ASSERT_GT((int)sink_info2.sessionId.size(), 0) << "Expect non-empty session ID from sink";
+    std::vector<uint8_t> sink_signing_key2 = SigningKeyFromIdentity(sink_init2_info.identity);
+    CheckSignature(sink_signing_key2, sink_info2.sessionId, sink_info2.signature);
+
+    // Step 3: pass the sink's ECDH public keys and other session info to the each of the sources.
+    SessionInfo source1_info;
+    ASSERT_EQ(OK, GetReturnError(source1->finish(sink_init1_info.key.pubKey.value(),
+                                                 sink_init1_info.identity, sink_info1.signature,
+                                                 sink_init1_info.nonce, sink_init1_info.version,
+                                                 source1_init_info.key, &source1_info)));
+    ASSERT_EQ((int)source1_info.sharedKeys.size(), 2) << "Expect two symmetric keys from finsh()";
+    ASSERT_GT((int)source1_info.sessionId.size(), 0) << "Expect non-empty session ID from source";
+    std::vector<uint8_t> source1_signing_key = SigningKeyFromIdentity(source1_init_info.identity);
+    CheckSignature(source1_signing_key, source1_info.sessionId, source1_info.signature);
+
+    SessionInfo source2_info;
+    ASSERT_EQ(OK, GetReturnError(source2->finish(sink_init2_info.key.pubKey.value(),
+                                                 sink_init2_info.identity, sink_info2.signature,
+                                                 sink_init2_info.nonce, sink_init2_info.version,
+                                                 source2_init_info.key, &source2_info)));
+    ASSERT_EQ((int)source2_info.sharedKeys.size(), 2) << "Expect two symmetric keys from finsh()";
+    ASSERT_GT((int)source2_info.sessionId.size(), 0) << "Expect non-empty session ID from source";
+    std::vector<uint8_t> source2_signing_key = SigningKeyFromIdentity(source2_init_info.identity);
+    CheckSignature(source2_signing_key, source2_info.sessionId, source2_info.signature);
+
+    // Both ends should agree on the session ID.
+    ASSERT_EQ(source1_info.sessionId, sink_info1.sessionId);
+    ASSERT_EQ(source2_info.sessionId, sink_info2.sessionId);
+
+    // Step 4: pass the each source's session ID info back to the sink, so it can check it and
+    // update the symmetric keys so they're marked as authentication complete.
+    std::array<Arc, 2> auth_complete_result1;
+    ASSERT_EQ(OK, GetReturnError(sink->authenticationComplete(
+                          source1_info.signature, sink_info1.sharedKeys, &auth_complete_result1)));
+    ASSERT_EQ((int)auth_complete_result1.size(), 2)
+            << "Expect two symmetric keys from authComplete()";
+    sink_info1.sharedKeys = auth_complete_result1;
+    std::array<Arc, 2> auth_complete_result2;
+    ASSERT_EQ(OK, GetReturnError(sink->authenticationComplete(
+                          source2_info.signature, sink_info2.sharedKeys, &auth_complete_result2)));
+    ASSERT_EQ((int)auth_complete_result2.size(), 2)
+            << "Expect two symmetric keys from authComplete()";
+    sink_info2.sharedKeys = auth_complete_result2;
+}
+
+TEST_P(AuthGraphSessionTest, FreshNonces) {
+    std::shared_ptr<IAuthGraphKeyExchange> source = authNode_;
+    std::shared_ptr<IAuthGraphKeyExchange> sink = authNode_;
+
+    SessionInitiationInfo source_init_info1;
+    ASSERT_EQ(OK, GetReturnError(source->create(&source_init_info1)));
+    SessionInitiationInfo source_init_info2;
+    ASSERT_EQ(OK, GetReturnError(source->create(&source_init_info2)));
+
+    // Two calls to create() should result in the same identity but different nonce values.
+    ASSERT_EQ(source_init_info1.identity, source_init_info2.identity);
+    ASSERT_NE(source_init_info1.nonce, source_init_info2.nonce);
+    ASSERT_NE(source_init_info1.key.pubKey, source_init_info2.key.pubKey);
+    ASSERT_NE(source_init_info1.key.arcFromPBK, source_init_info2.key.arcFromPBK);
+
+    KeInitResult init_result1;
+    ASSERT_EQ(OK, GetReturnError(sink->init(source_init_info1.key.pubKey.value(),
+                                            source_init_info1.identity, source_init_info1.nonce,
+                                            source_init_info1.version, &init_result1)));
+    KeInitResult init_result2;
+    ASSERT_EQ(OK, GetReturnError(sink->init(source_init_info2.key.pubKey.value(),
+                                            source_init_info2.identity, source_init_info2.nonce,
+                                            source_init_info2.version, &init_result2)));
+
+    // Two calls to init() should result in the same identity buf different nonces and session IDs.
+    ASSERT_EQ(init_result1.sessionInitiationInfo.identity,
+              init_result2.sessionInitiationInfo.identity);
+    ASSERT_NE(init_result1.sessionInitiationInfo.nonce, init_result2.sessionInitiationInfo.nonce);
+    ASSERT_NE(init_result1.sessionInfo.sessionId, init_result2.sessionInfo.sessionId);
+}
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, AuthGraphSessionTest,
+                         testing::ValuesIn(AuthGraphSessionTest::build_params()),
+                         ::android::PrintInstanceNameToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AuthGraphSessionTest);
+
+}  // namespace aidl::android::hardware::security::authgraph::test
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/security/authgraph/aidl/vts/functional/lib.rs b/security/authgraph/aidl/vts/functional/lib.rs
new file mode 100644
index 0000000..da3fa1c
--- /dev/null
+++ b/security/authgraph/aidl/vts/functional/lib.rs
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! VTS test library for AuthGraph functionality.
+//!
+//! This test code is bundled as a library, not as `[cfg(test)]`, to allow it to be
+//! re-used inside the (Rust) VTS tests of components that use AuthGraph.
+
+use android_hardware_security_authgraph::aidl::android::hardware::security::authgraph::{
+    Error::Error, IAuthGraphKeyExchange::IAuthGraphKeyExchange, Identity::Identity,
+    PlainPubKey::PlainPubKey, PubKey::PubKey, SessionIdSignature::SessionIdSignature,
+};
+use authgraph_boringssl as boring;
+use authgraph_core::{error::Error as AgError, keyexchange as ke};
+use coset::CborSerializable;
+
+pub mod sink;
+pub mod source;
+
+/// Return an AuthGraphParticipant suitable for testing.
+pub fn test_ag_participant() -> Result<ke::AuthGraphParticipant, AgError> {
+    Ok(ke::AuthGraphParticipant::new(
+        boring::crypto_trait_impls(),
+        Box::<boring::test_device::AgDevice>::default(),
+        ke::MAX_OPENED_SESSIONS,
+    )?)
+}
+
+fn build_plain_pub_key(pub_key: &Option<Vec<u8>>) -> PubKey {
+    PubKey::PlainKey(PlainPubKey {
+        plainPubKey: pub_key.clone().unwrap(),
+    })
+}
+
+fn extract_plain_pub_key(pub_key: &Option<PubKey>) -> &PlainPubKey {
+    match pub_key {
+        Some(PubKey::PlainKey(pub_key)) => pub_key,
+        Some(PubKey::SignedKey(_)) => panic!("expect unsigned public key"),
+        None => panic!("expect pubKey to be populated"),
+    }
+}
+
+fn vec_to_identity(data: &[u8]) -> Identity {
+    Identity {
+        identity: data.to_vec(),
+    }
+}
+
+fn vec_to_signature(data: &[u8]) -> SessionIdSignature {
+    SessionIdSignature {
+        signature: data.to_vec(),
+    }
+}
diff --git a/security/authgraph/aidl/vts/functional/role_test.rs b/security/authgraph/aidl/vts/functional/role_test.rs
new file mode 100644
index 0000000..71a2fae
--- /dev/null
+++ b/security/authgraph/aidl/vts/functional/role_test.rs
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Tests of individual AuthGraph role (source or sink) functionality.
+
+#![cfg(test)]
+
+use authgraph_vts_test as vts;
+use android_hardware_security_authgraph::aidl::android::hardware::security::authgraph::{
+    IAuthGraphKeyExchange::IAuthGraphKeyExchange,
+};
+
+const AUTH_GRAPH_NONSECURE: &str =
+    "android.hardware.security.authgraph.IAuthGraphKeyExchange/nonsecure";
+
+/// Retrieve the /nonsecure instance of AuthGraph, which supports both sink and source roles.
+fn get_nonsecure() -> Option<binder::Strong<dyn IAuthGraphKeyExchange>> {
+    binder::get_interface(AUTH_GRAPH_NONSECURE).ok()
+}
+
+/// Macro to require availability of a /nonsecure instance of AuthGraph.
+///
+/// Note that this macro triggers `return` if not found.
+macro_rules! require_nonsecure {
+    {} => {
+        match get_nonsecure() {
+            Some(v) => v,
+            None => {
+                eprintln!("Skipping test as no /nonsecure impl found");
+                return;
+            }
+        }
+    }
+}
+
+#[test]
+fn test_nonsecure_source_mainline() {
+    let mut sink = vts::test_ag_participant().expect("failed to create a local sink");
+    vts::source::test_mainline(&mut sink, require_nonsecure!());
+}
+#[test]
+fn test_nonsecure_source_corrupt_sig() {
+    let mut sink = vts::test_ag_participant().expect("failed to create a local sink");
+    vts::source::test_corrupt_sig(&mut sink, require_nonsecure!());
+}
+#[test]
+fn test_nonsecure_source_corrupt_keys() {
+    let mut sink = vts::test_ag_participant().expect("failed to create a local sink");
+    vts::source::test_corrupt_key(&mut sink, require_nonsecure!());
+}
+#[test]
+fn test_nonsecure_sink_mainline() {
+    let mut source = vts::test_ag_participant().expect("failed to create a local source");
+    vts::sink::test_mainline(&mut source, require_nonsecure!());
+}
+#[test]
+fn test_nonsecure_sink_corrupt_sig() {
+    let mut source = vts::test_ag_participant().expect("failed to create a local source");
+    vts::sink::test_corrupt_sig(&mut source, require_nonsecure!());
+}
+#[test]
+fn test_nonsecure_sink_corrupt_keys() {
+    let mut source = vts::test_ag_participant().expect("failed to create a local source");
+    vts::sink::test_corrupt_keys(&mut source, require_nonsecure!());
+}
diff --git a/security/authgraph/aidl/vts/functional/sink.rs b/security/authgraph/aidl/vts/functional/sink.rs
new file mode 100644
index 0000000..bb357b8
--- /dev/null
+++ b/security/authgraph/aidl/vts/functional/sink.rs
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! VTS tests for sinks
+use super::*;
+use authgraph_core::{key, keyexchange as ke};
+
+/// Run AuthGraph tests against the provided sink, using a local test source implementation.
+pub fn test(
+    local_source: &mut ke::AuthGraphParticipant,
+    sink: binder::Strong<dyn IAuthGraphKeyExchange>,
+) {
+    test_mainline(local_source, sink.clone());
+    test_corrupt_sig(local_source, sink.clone());
+    test_corrupt_keys(local_source, sink);
+}
+
+/// Perform mainline AuthGraph key exchange with the provided sink and local implementation.
+/// Return the agreed AES keys in plaintext.
+pub fn test_mainline(
+    local_source: &mut ke::AuthGraphParticipant,
+    sink: binder::Strong<dyn IAuthGraphKeyExchange>,
+) -> [key::AesKey; 2] {
+    // Step 1: create an ephemeral ECDH key at the (local) source.
+    let source_init_info = local_source
+        .create()
+        .expect("failed to create() with local impl");
+
+    // Step 2: pass the source's ECDH public key and other session info to the (remote) sink.
+    let init_result = sink
+        .init(
+            &build_plain_pub_key(&source_init_info.ke_key.pub_key),
+            &vec_to_identity(&source_init_info.identity),
+            &source_init_info.nonce,
+            source_init_info.version,
+        )
+        .expect("failed to init() with remote impl");
+    let sink_init_info = init_result.sessionInitiationInfo;
+    let sink_pub_key = extract_plain_pub_key(&sink_init_info.key.pubKey);
+
+    let sink_info = init_result.sessionInfo;
+    assert!(!sink_info.sessionId.is_empty());
+
+    // The AuthGraph core library will verify the session ID signature, but do it here too.
+    let sink_verification_key = local_source
+        .peer_verification_key_from_identity(&sink_init_info.identity.identity)
+        .expect("failed to get peer verification from identity");
+    local_source
+        .verify_signature_on_session_id(
+            &sink_verification_key,
+            &sink_info.sessionId,
+            &sink_info.signature.signature,
+        )
+        .expect("failed verification of signed session ID");
+
+    // Step 3: pass the sink's ECDH public key and other session info to the (local) source, so it
+    // can calculate the same pair of symmetric keys.
+    let source_info = local_source
+        .finish(
+            &sink_pub_key.plainPubKey,
+            &sink_init_info.identity.identity,
+            &sink_info.signature.signature,
+            &sink_init_info.nonce,
+            sink_init_info.version,
+            source_init_info.ke_key,
+        )
+        .expect("failed to finish() with local impl");
+    assert!(!source_info.session_id.is_empty());
+
+    // The AuthGraph core library will verify the session ID signature, but do it here too.
+    let source_verification_key = key::Identity::from_slice(&source_init_info.identity)
+        .expect("invalid identity CBOR")
+        .cert_chain
+        .root_key;
+    local_source
+        .verify_signature_on_session_id(
+            &source_verification_key,
+            &source_info.session_id,
+            &source_info.session_id_signature,
+        )
+        .expect("failed verification of signed session ID");
+
+    // Both ends should agree on the session ID.
+    assert_eq!(source_info.session_id, sink_info.sessionId);
+
+    // Step 4: pass the (local) source's session ID signature back to the sink, so it can check it
+    // and update the symmetric keys so they're marked as authentication complete.
+    let _sink_arcs = sink
+        .authenticationComplete(
+            &vec_to_signature(&source_info.session_id_signature),
+            &sink_info.sharedKeys,
+        )
+        .expect("failed to authenticationComplete() with remote sink");
+    // Decrypt and return the session keys.
+    let decrypted_shared_keys = local_source
+        .decipher_shared_keys_from_arcs(&source_info.shared_keys)
+        .expect("failed to decrypt shared key arcs")
+        .try_into();
+    let decrypted_shared_keys_array = match decrypted_shared_keys {
+        Ok(array) => array,
+        Err(_) => panic!("wrong number of decrypted shared key arcs"),
+    };
+    decrypted_shared_keys_array
+}
+
+/// Perform mainline AuthGraph key exchange with the provided sink, but provide an invalid
+/// session ID signature.
+pub fn test_corrupt_sig(
+    local_source: &mut ke::AuthGraphParticipant,
+    sink: binder::Strong<dyn IAuthGraphKeyExchange>,
+) {
+    // Step 1: create an ephemeral ECDH key at the (local) source.
+    let source_init_info = local_source
+        .create()
+        .expect("failed to create() with local impl");
+
+    // Step 2: pass the source's ECDH public key and other session info to the (remote) sink.
+    let init_result = sink
+        .init(
+            &build_plain_pub_key(&source_init_info.ke_key.pub_key),
+            &vec_to_identity(&source_init_info.identity),
+            &source_init_info.nonce,
+            source_init_info.version,
+        )
+        .expect("failed to init() with remote impl");
+    let sink_init_info = init_result.sessionInitiationInfo;
+    let sink_pub_key = extract_plain_pub_key(&sink_init_info.key.pubKey);
+
+    let sink_info = init_result.sessionInfo;
+    assert!(!sink_info.sessionId.is_empty());
+
+    // Step 3: pass the sink's ECDH public key and other session info to the (local) source, so it
+    // can calculate the same pair of symmetric keys.
+    let source_info = local_source
+        .finish(
+            &sink_pub_key.plainPubKey,
+            &sink_init_info.identity.identity,
+            &sink_info.signature.signature,
+            &sink_init_info.nonce,
+            sink_init_info.version,
+            source_init_info.ke_key,
+        )
+        .expect("failed to finish() with local impl");
+    assert!(!source_info.session_id.is_empty());
+
+    // Build a corrupted version of the (local) source's session ID signature.
+    let mut corrupt_signature = source_info.session_id_signature.clone();
+    let sig_len = corrupt_signature.len();
+    corrupt_signature[sig_len - 1] ^= 0x01;
+
+    // Step 4: pass the (local) source's **invalid** session ID signature back to the sink,
+    // which should reject it.
+    let result =
+        sink.authenticationComplete(&vec_to_signature(&corrupt_signature), &sink_info.sharedKeys);
+    let err = result.expect_err("expect failure with corrupt signature");
+    assert_eq!(
+        err,
+        binder::Status::new_service_specific_error(Error::INVALID_SIGNATURE.0, None)
+    );
+}
+
+/// Perform mainline AuthGraph key exchange with the provided sink, but provide an invalid
+/// Arc for the sink's key.
+pub fn test_corrupt_keys(
+    local_source: &mut ke::AuthGraphParticipant,
+    sink: binder::Strong<dyn IAuthGraphKeyExchange>,
+) {
+    // Step 1: create an ephemeral ECDH key at the (local) source.
+    let source_init_info = local_source
+        .create()
+        .expect("failed to create() with local impl");
+
+    // Step 2: pass the source's ECDH public key and other session info to the (remote) sink.
+    let init_result = sink
+        .init(
+            &build_plain_pub_key(&source_init_info.ke_key.pub_key),
+            &vec_to_identity(&source_init_info.identity),
+            &source_init_info.nonce,
+            source_init_info.version,
+        )
+        .expect("failed to init() with remote impl");
+    let sink_init_info = init_result.sessionInitiationInfo;
+    let sink_pub_key = extract_plain_pub_key(&sink_init_info.key.pubKey);
+
+    let sink_info = init_result.sessionInfo;
+    assert!(!sink_info.sessionId.is_empty());
+
+    // Step 3: pass the sink's ECDH public key and other session info to the (local) source, so it
+    // can calculate the same pair of symmetric keys.
+    let source_info = local_source
+        .finish(
+            &sink_pub_key.plainPubKey,
+            &sink_init_info.identity.identity,
+            &sink_info.signature.signature,
+            &sink_init_info.nonce,
+            sink_init_info.version,
+            source_init_info.ke_key,
+        )
+        .expect("failed to finish() with local impl");
+    assert!(!source_info.session_id.is_empty());
+
+    // Deliberately corrupt the sink's shared key Arcs before returning them
+    let mut corrupt_keys = sink_info.sharedKeys.clone();
+    let len0 = corrupt_keys[0].arc.len();
+    let len1 = corrupt_keys[1].arc.len();
+    corrupt_keys[0].arc[len0 - 1] ^= 0x01;
+    corrupt_keys[1].arc[len1 - 1] ^= 0x01;
+
+    // Step 4: pass the (local) source's session ID signature back to the sink, but with corrupted
+    // keys, which should be rejected.
+    let result = sink.authenticationComplete(
+        &vec_to_signature(&source_info.session_id_signature),
+        &corrupt_keys,
+    );
+    let err = result.expect_err("expect failure with corrupt keys");
+    assert_eq!(
+        err,
+        binder::Status::new_service_specific_error(Error::INVALID_SHARED_KEY_ARCS.0, None)
+    );
+}
diff --git a/security/authgraph/aidl/vts/functional/source.rs b/security/authgraph/aidl/vts/functional/source.rs
new file mode 100644
index 0000000..4178a99
--- /dev/null
+++ b/security/authgraph/aidl/vts/functional/source.rs
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! VTS tests for sources
+use super::*;
+use authgraph_core::{key, keyexchange as ke};
+
+/// Run AuthGraph tests against the provided source, using a local test sink implementation.
+pub fn test(
+    local_sink: &mut ke::AuthGraphParticipant,
+    source: binder::Strong<dyn IAuthGraphKeyExchange>,
+) {
+    test_mainline(local_sink, source.clone());
+    test_corrupt_sig(local_sink, source.clone());
+    test_corrupt_key(local_sink, source);
+}
+
+/// Perform mainline AuthGraph key exchange with the provided source.
+/// Return the agreed AES keys in plaintext.
+pub fn test_mainline(
+    local_sink: &mut ke::AuthGraphParticipant,
+    source: binder::Strong<dyn IAuthGraphKeyExchange>,
+) -> [key::AesKey; 2] {
+    // Step 1: create an ephemeral ECDH key at the (remote) source.
+    let source_init_info = source
+        .create()
+        .expect("failed to create() with remote impl");
+    assert!(source_init_info.key.pubKey.is_some());
+    assert!(source_init_info.key.arcFromPBK.is_some());
+    let source_pub_key = extract_plain_pub_key(&source_init_info.key.pubKey);
+
+    // Step 2: pass the source's ECDH public key and other session info to the (local) sink.
+    let init_result = local_sink
+        .init(
+            &source_pub_key.plainPubKey,
+            &source_init_info.identity.identity,
+            &source_init_info.nonce,
+            source_init_info.version,
+        )
+        .expect("failed to init() with local impl");
+    let sink_init_info = init_result.session_init_info;
+    let sink_pub_key = sink_init_info
+        .ke_key
+        .pub_key
+        .expect("expect pub_key to be populated");
+
+    let sink_info = init_result.session_info;
+    assert!(!sink_info.session_id.is_empty());
+
+    // The AuthGraph core library will verify the session ID signature, but do it here too.
+    let sink_verification_key = key::Identity::from_slice(&sink_init_info.identity)
+        .expect("invalid identity CBOR")
+        .cert_chain
+        .root_key;
+    local_sink
+        .verify_signature_on_session_id(
+            &sink_verification_key,
+            &sink_info.session_id,
+            &sink_info.session_id_signature,
+        )
+        .expect("failed verification of signed session ID");
+
+    // Step 3: pass the sink's ECDH public key and other session info to the (remote) source, so it
+    // can calculate the same pair of symmetric keys.
+    let source_info = source
+        .finish(
+            &PubKey::PlainKey(PlainPubKey {
+                plainPubKey: sink_pub_key,
+            }),
+            &Identity {
+                identity: sink_init_info.identity,
+            },
+            &vec_to_signature(&sink_info.session_id_signature),
+            &sink_init_info.nonce,
+            sink_init_info.version,
+            &source_init_info.key,
+        )
+        .expect("failed to finish() with remote impl");
+    assert!(!source_info.sessionId.is_empty());
+
+    // The AuthGraph core library will verify the session ID signature, but do it here too.
+    let source_verification_key = local_sink
+        .peer_verification_key_from_identity(&source_init_info.identity.identity)
+        .expect("failed to get peer verification from identity");
+    local_sink
+        .verify_signature_on_session_id(
+            &source_verification_key,
+            &source_info.sessionId,
+            &source_info.signature.signature,
+        )
+        .expect("failed verification of signed session ID");
+
+    // Both ends should agree on the session ID.
+    assert_eq!(source_info.sessionId, sink_info.session_id);
+
+    // Step 4: pass the (remote) source's session ID signature back to the sink, so it can check it
+    // and update the symmetric keys so they're marked as authentication complete.
+    let sink_arcs = local_sink
+        .authentication_complete(&source_info.signature.signature, sink_info.shared_keys)
+        .expect("failed to authenticationComplete() with local sink");
+    // Decrypt and return the session keys.
+    let decrypted_shared_keys = local_sink
+        .decipher_shared_keys_from_arcs(&sink_arcs)
+        .expect("failed to decrypt shared key arcs")
+        .try_into();
+    let decrypted_shared_keys_array = match decrypted_shared_keys {
+        Ok(array) => array,
+        Err(_) => panic!("wrong number of decrypted shared key arcs"),
+    };
+    decrypted_shared_keys_array
+}
+
+/// Perform mainline AuthGraph key exchange with the provided source, but provide an invalid session
+/// ID signature.
+pub fn test_corrupt_sig(
+    local_sink: &mut ke::AuthGraphParticipant,
+    source: binder::Strong<dyn IAuthGraphKeyExchange>,
+) {
+    // Step 1: create an ephemeral ECDH key at the (remote) source.
+    let source_init_info = source
+        .create()
+        .expect("failed to create() with remote impl");
+    assert!(source_init_info.key.pubKey.is_some());
+    assert!(source_init_info.key.arcFromPBK.is_some());
+    let source_pub_key = extract_plain_pub_key(&source_init_info.key.pubKey);
+
+    // Step 2: pass the source's ECDH public key and other session info to the (local) sink.
+    let init_result = local_sink
+        .init(
+            &source_pub_key.plainPubKey,
+            &source_init_info.identity.identity,
+            &source_init_info.nonce,
+            source_init_info.version,
+        )
+        .expect("failed to init() with local impl");
+    let sink_init_info = init_result.session_init_info;
+    let sink_pub_key = sink_init_info
+        .ke_key
+        .pub_key
+        .expect("expect pub_key to be populated");
+    let sink_info = init_result.session_info;
+    assert!(!sink_info.session_id.is_empty());
+
+    // Deliberately corrupt the sink's session ID signature.
+    let mut corrupt_signature = sink_info.session_id_signature.clone();
+    let sig_len = corrupt_signature.len();
+    corrupt_signature[sig_len - 1] ^= 0x01;
+
+    // Step 3: pass the sink's ECDH public key and other session info to the (remote) source, so it
+    // can calculate the same pair of symmetric keys.
+    let result = source.finish(
+        &PubKey::PlainKey(PlainPubKey {
+            plainPubKey: sink_pub_key,
+        }),
+        &Identity {
+            identity: sink_init_info.identity,
+        },
+        &vec_to_signature(&corrupt_signature),
+        &sink_init_info.nonce,
+        sink_init_info.version,
+        &source_init_info.key,
+    );
+    let err = result.expect_err("expect failure with corrupt signature");
+    assert_eq!(
+        err,
+        binder::Status::new_service_specific_error(Error::INVALID_SIGNATURE.0, None)
+    );
+}
+
+/// Perform mainline AuthGraph key exchange with the provided source, but give it back
+/// a corrupted key.
+pub fn test_corrupt_key(
+    local_sink: &mut ke::AuthGraphParticipant,
+    source: binder::Strong<dyn IAuthGraphKeyExchange>,
+) {
+    // Step 1: create an ephemeral ECDH key at the (remote) source.
+    let source_init_info = source
+        .create()
+        .expect("failed to create() with remote impl");
+    assert!(source_init_info.key.pubKey.is_some());
+    assert!(source_init_info.key.arcFromPBK.is_some());
+    let source_pub_key = extract_plain_pub_key(&source_init_info.key.pubKey);
+
+    // Step 2: pass the source's ECDH public key and other session info to the (local) sink.
+    let init_result = local_sink
+        .init(
+            &source_pub_key.plainPubKey,
+            &source_init_info.identity.identity,
+            &source_init_info.nonce,
+            source_init_info.version,
+        )
+        .expect("failed to init() with local impl");
+    let sink_init_info = init_result.session_init_info;
+    let sink_pub_key = sink_init_info
+        .ke_key
+        .pub_key
+        .expect("expect pub_key to be populated");
+
+    let sink_info = init_result.session_info;
+    assert!(!sink_info.session_id.is_empty());
+
+    // The AuthGraph core library will verify the session ID signature, but do it here too.
+    let sink_verification_key = key::Identity::from_slice(&sink_init_info.identity)
+        .expect("invalid identity CBOR")
+        .cert_chain
+        .root_key;
+    local_sink
+        .verify_signature_on_session_id(
+            &sink_verification_key,
+            &sink_info.session_id,
+            &sink_info.session_id_signature,
+        )
+        .expect("failed verification of signed session ID");
+
+    // Deliberately corrupt the source's encrypted key.
+    let mut corrupt_key = source_init_info.key.clone();
+    match &mut corrupt_key.arcFromPBK {
+        Some(a) => {
+            let len = a.arc.len();
+            a.arc[len - 1] ^= 0x01;
+        }
+        None => panic!("no arc data"),
+    }
+
+    // Step 3: pass the sink's ECDH public key and other session info to the (remote) source, but
+    // give it back a corrupted version of its own key.
+    let result = source.finish(
+        &PubKey::PlainKey(PlainPubKey {
+            plainPubKey: sink_pub_key,
+        }),
+        &Identity {
+            identity: sink_init_info.identity,
+        },
+        &vec_to_signature(&sink_info.session_id_signature),
+        &sink_init_info.nonce,
+        sink_init_info.version,
+        &corrupt_key,
+    );
+
+    let err = result.expect_err("expect failure with corrupt signature");
+    assert_eq!(
+        err,
+        binder::Status::new_service_specific_error(Error::INVALID_PRIV_KEY_ARC_IN_KEY.0, None)
+    );
+}
diff --git a/security/authgraph/default/Android.bp b/security/authgraph/default/Android.bp
new file mode 100644
index 0000000..c481075
--- /dev/null
+++ b/security/authgraph/default/Android.bp
@@ -0,0 +1,82 @@
+//
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+rust_library {
+    name: "libauthgraph_nonsecure",
+    crate_name: "authgraph_nonsecure",
+    defaults: [
+        "authgraph_use_latest_hal_aidl_rust",
+    ],
+    vendor_available: true,
+    rustlibs: [
+        "libandroid_logger",
+        "libauthgraph_boringssl",
+        "libauthgraph_core",
+        "libauthgraph_hal",
+        "libbinder_rs",
+        "liblibc",
+        "liblog_rust",
+    ],
+    srcs: ["src/lib.rs"],
+
+}
+
+rust_binary {
+    name: "android.hardware.security.authgraph-service.nonsecure",
+    relative_install_path: "hw",
+    vendor: true,
+    init_rc: ["authgraph.rc"],
+    vintf_fragments: ["authgraph.xml"],
+    defaults: [
+        "authgraph_use_latest_hal_aidl_rust",
+    ],
+    rustlibs: [
+        "libandroid_logger",
+        "libauthgraph_hal",
+        "libauthgraph_nonsecure",
+        "libbinder_rs",
+        "liblibc",
+        "liblog_rust",
+    ],
+    srcs: [
+        "src/main.rs",
+    ],
+}
+
+rust_fuzz {
+    name: "android.hardware.authgraph-service.nonsecure_fuzzer",
+    rustlibs: [
+        "libauthgraph_hal",
+        "libauthgraph_nonsecure",
+        "libbinder_random_parcel_rs",
+        "libbinder_rs",
+    ],
+    srcs: ["src/fuzzer.rs"],
+    fuzz_config: {
+        cc: [
+            "drysdale@google.com",
+            "hasinitg@google.com",
+        ],
+    },
+}
diff --git a/security/authgraph/default/authgraph.rc b/security/authgraph/default/authgraph.rc
new file mode 100644
index 0000000..0222994
--- /dev/null
+++ b/security/authgraph/default/authgraph.rc
@@ -0,0 +1,5 @@
+service vendor.authgraph /vendor/bin/hw/android.hardware.security.authgraph-service.nonsecure
+    interface aidl android.hardware.security.authgraph.IAuthGraph/nonsecure
+    class hal
+    user nobody
+    group nobody
diff --git a/security/authgraph/default/authgraph.xml b/security/authgraph/default/authgraph.xml
new file mode 100644
index 0000000..9529a0a
--- /dev/null
+++ b/security/authgraph/default/authgraph.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.security.authgraph</name>
+        <version>1</version>
+        <interface>
+            <name>IAuthGraphKeyExchange</name>
+            <instance>nonsecure</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/security/authgraph/default/src/fuzzer.rs b/security/authgraph/default/src/fuzzer.rs
new file mode 100644
index 0000000..d401777
--- /dev/null
+++ b/security/authgraph/default/src/fuzzer.rs
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#![allow(missing_docs)]
+#![no_main]
+extern crate libfuzzer_sys;
+
+use authgraph_hal::service::AuthGraphService;
+use authgraph_nonsecure::LocalTa;
+use binder_random_parcel_rs::fuzz_service;
+use libfuzzer_sys::fuzz_target;
+use std::sync::{Arc, Mutex};
+
+fuzz_target!(|data: &[u8]| {
+    let local_ta = LocalTa::new().expect("Failed to create an AuthGraph local TA.");
+    let service = AuthGraphService::new_as_binder(Arc::new(Mutex::new(local_ta)));
+    fuzz_service(&mut service.as_binder(), data);
+});
diff --git a/security/authgraph/default/src/lib.rs b/security/authgraph/default/src/lib.rs
new file mode 100644
index 0000000..43d037c
--- /dev/null
+++ b/security/authgraph/default/src/lib.rs
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Common functionality for non-secure/testing instance of AuthGraph.
+
+use authgraph_boringssl as boring;
+use authgraph_core::{
+    error,
+    key::MillisecondsSinceEpoch,
+    keyexchange,
+    ta::{AuthGraphTa, Role},
+    traits,
+};
+use authgraph_hal::channel::SerializedChannel;
+use std::sync::{Arc, Mutex};
+use std::time::Instant;
+
+/// Monotonic clock with an epoch that starts at the point of construction.
+/// (This makes it unsuitable for use outside of testing, because the epoch
+/// will not match that of any other component.)
+pub struct StdClock(Instant);
+
+impl Default for StdClock {
+    fn default() -> Self {
+        Self(Instant::now())
+    }
+}
+
+impl traits::MonotonicClock for StdClock {
+    fn now(&self) -> MillisecondsSinceEpoch {
+        let millis: i64 = self
+            .0
+            .elapsed()
+            .as_millis()
+            .try_into()
+            .expect("failed to fit timestamp in i64");
+        MillisecondsSinceEpoch(millis)
+    }
+}
+
+/// Implementation of the AuthGraph TA that runs locally in-process (and which is therefore
+/// insecure).
+pub struct LocalTa {
+    ta: Arc<Mutex<AuthGraphTa>>,
+}
+
+impl LocalTa {
+    /// Create a new instance.
+    pub fn new() -> Result<Self, error::Error> {
+        Ok(Self {
+            ta: Arc::new(Mutex::new(AuthGraphTa::new(
+                keyexchange::AuthGraphParticipant::new(
+                    boring::crypto_trait_impls(),
+                    Box::<boring::test_device::AgDevice>::default(),
+                    keyexchange::MAX_OPENED_SESSIONS,
+                )?,
+                Role::Both,
+            ))),
+        })
+    }
+}
+
+/// Pretend to be a serialized channel to the TA, but actually just directly invoke the TA with
+/// incoming requests.
+impl SerializedChannel for LocalTa {
+    const MAX_SIZE: usize = usize::MAX;
+
+    fn execute(&mut self, req_data: &[u8]) -> binder::Result<Vec<u8>> {
+        Ok(self.ta.lock().unwrap().process(req_data))
+    }
+}
diff --git a/security/authgraph/default/src/main.rs b/security/authgraph/default/src/main.rs
new file mode 100644
index 0000000..81f2dd6
--- /dev/null
+++ b/security/authgraph/default/src/main.rs
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Default implementation of the AuthGraph key exchange HAL.
+//!
+//! This implementation of the HAL is only intended to allow testing and policy compliance.  A real
+//! implementation of the AuthGraph HAL would be implemented in a secure environment, and would not
+//! be independently registered with service manager (a secure component that uses AuthGraph would
+//! expose an entrypoint that allowed retrieval of the specific IAuthGraphKeyExchange instance that
+//! is correlated with the component).
+
+use authgraph_hal::service;
+use authgraph_nonsecure::LocalTa;
+use log::{error, info};
+use std::sync::{Arc, Mutex};
+
+static SERVICE_NAME: &str = "android.hardware.security.authgraph.IAuthGraphKeyExchange";
+static SERVICE_INSTANCE: &str = "nonsecure";
+
+/// Local error type for failures in the HAL service.
+#[derive(Debug, Clone)]
+struct HalServiceError(String);
+
+impl From<String> for HalServiceError {
+    fn from(s: String) -> Self {
+        Self(s)
+    }
+}
+
+fn main() {
+    if let Err(e) = inner_main() {
+        panic!("HAL service failed: {:?}", e);
+    }
+}
+
+fn inner_main() -> Result<(), HalServiceError> {
+    // Initialize Android logging.
+    android_logger::init_once(
+        android_logger::Config::default()
+            .with_tag("authgraph-hal-nonsecure")
+            .with_min_level(log::Level::Info)
+            .with_log_id(android_logger::LogId::System),
+    );
+    // Redirect panic messages to logcat.
+    std::panic::set_hook(Box::new(|panic_info| {
+        error!("{}", panic_info);
+    }));
+
+    info!("Insecure AuthGraph key exchange HAL service is starting.");
+
+    info!("Starting thread pool now.");
+    binder::ProcessState::start_thread_pool();
+
+    // Register the service
+    let local_ta =
+        LocalTa::new().map_err(|e| format!("Failed to create the TA because: {e:?}"))?;
+    let service = service::AuthGraphService::new_as_binder(Arc::new(Mutex::new(local_ta)));
+    let service_name = format!("{}/{}", SERVICE_NAME, SERVICE_INSTANCE);
+    binder::add_service(&service_name, service.as_binder()).map_err(|e| {
+        format!(
+            "Failed to register service {} because of {:?}.",
+            service_name, e
+        )
+    })?;
+
+    info!("Successfully registered AuthGraph HAL services.");
+    binder::ProcessState::join_thread_pool();
+    info!("AuthGraph HAL service is terminating."); // should not reach here
+    Ok(())
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
index 36f0106..aa7bf28 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
@@ -643,6 +643,8 @@
      * Tag::ATTESTATION_CHALLENGE is used to deliver a "challenge" value to the attested key
      * generation/import methods, which must place the value in the KeyDescription SEQUENCE of the
      * attestation extension.
+     * The challenge value may be up to 128 bytes. If the caller provides a bigger challenge,
+     * INVALID_INPUT_LENGTH error should be returned.
      *
      * Must never appear in KeyCharacteristics.
      */
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 5f8ec0e..822770d 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -79,7 +79,8 @@
 typedef KeyMintAidlTestBase::KeyData KeyData;
 // Predicate for testing basic characteristics validity in generation or import.
 bool KeyCharacteristicsBasicallyValid(SecurityLevel secLevel,
-                                      const vector<KeyCharacteristics>& key_characteristics) {
+                                      const vector<KeyCharacteristics>& key_characteristics,
+                                      int32_t aidl_version) {
     if (key_characteristics.empty()) return false;
 
     std::unordered_set<SecurityLevel> levels_seen;
@@ -89,7 +90,12 @@
             return false;
         }
 
-        EXPECT_EQ(count_tag_invalid_entries(entry.authorizations), 0);
+        // There was no test to assert that INVALID tag should not present in authorization list
+        // before Keymint V3, so there are some Keymint implementations where asserting for INVALID
+        // tag fails(b/297306437), hence skipping for Keymint < 3.
+        if (aidl_version >= 3) {
+            EXPECT_EQ(count_tag_invalid_entries(entry.authorizations), 0);
+        }
 
         // Just ignore the SecurityLevel::KEYSTORE as the KM won't do any enforcement on this.
         if (entry.securityLevel == SecurityLevel::KEYSTORE) continue;
@@ -264,7 +270,7 @@
     vendor_patch_level_ = getVendorPatchlevel();
 }
 
-int32_t KeyMintAidlTestBase::AidlVersion() {
+int32_t KeyMintAidlTestBase::AidlVersion() const {
     int32_t version = 0;
     auto status = keymint_->getInterfaceVersion(&version);
     if (!status.isOk()) {
@@ -294,8 +300,8 @@
     KeyCreationResult creationResult;
     Status result = keymint_->generateKey(key_desc.vector_data(), attest_key, &creationResult);
     if (result.isOk()) {
-        EXPECT_PRED2(KeyCharacteristicsBasicallyValid, SecLevel(),
-                     creationResult.keyCharacteristics);
+        EXPECT_PRED3(KeyCharacteristicsBasicallyValid, SecLevel(),
+                     creationResult.keyCharacteristics, AidlVersion());
         EXPECT_GT(creationResult.keyBlob.size(), 0);
         *key_blob = std::move(creationResult.keyBlob);
         *key_characteristics = std::move(creationResult.keyCharacteristics);
@@ -367,8 +373,8 @@
                                  {} /* attestationSigningKeyBlob */, &creationResult);
 
     if (result.isOk()) {
-        EXPECT_PRED2(KeyCharacteristicsBasicallyValid, SecLevel(),
-                     creationResult.keyCharacteristics);
+        EXPECT_PRED3(KeyCharacteristicsBasicallyValid, SecLevel(),
+                     creationResult.keyCharacteristics, AidlVersion());
         EXPECT_GT(creationResult.keyBlob.size(), 0);
 
         *key_blob = std::move(creationResult.keyBlob);
@@ -411,8 +417,8 @@
             unwrapping_params.vector_data(), password_sid, biometric_sid, &creationResult);
 
     if (result.isOk()) {
-        EXPECT_PRED2(KeyCharacteristicsBasicallyValid, SecLevel(),
-                     creationResult.keyCharacteristics);
+        EXPECT_PRED3(KeyCharacteristicsBasicallyValid, SecLevel(),
+                     creationResult.keyCharacteristics, AidlVersion());
         EXPECT_GT(creationResult.keyBlob.size(), 0);
 
         key_blob_ = std::move(creationResult.keyBlob);
@@ -841,7 +847,7 @@
         int vendor_api_level = property_get_int32("ro.vendor.api_level", 0);
         if (SecLevel() == SecurityLevel::STRONGBOX) {
             // This is known to be broken on older vendor implementations.
-            if (vendor_api_level < __ANDROID_API_T__) {
+            if (vendor_api_level <= __ANDROID_API_U__) {
                 compare_output = false;
             } else {
                 additional_information = " (b/194134359) ";
@@ -2068,27 +2074,36 @@
     return retval;
 }
 
-void assert_mgf_digests_present_in_key_characteristics(
+void KeyMintAidlTestBase::assert_mgf_digests_present_or_not_in_key_characteristics(
+        std::vector<android::hardware::security::keymint::Digest>& expected_mgf_digests,
+        bool is_mgf_digest_expected) const {
+    assert_mgf_digests_present_or_not_in_key_characteristics(
+            key_characteristics_, expected_mgf_digests, is_mgf_digest_expected);
+}
+
+void KeyMintAidlTestBase::assert_mgf_digests_present_or_not_in_key_characteristics(
         const vector<KeyCharacteristics>& key_characteristics,
-        std::vector<android::hardware::security::keymint::Digest>& expected_mgf_digests) {
+        std::vector<android::hardware::security::keymint::Digest>& expected_mgf_digests,
+        bool is_mgf_digest_expected) const {
+    // There was no test to assert that MGF1 digest was present in generated/imported key
+    // characteristics before Keymint V3, so there are some Keymint implementations where
+    // asserting for MGF1 digest fails(b/297306437), hence skipping for Keymint < 3.
+    if (AidlVersion() < 3) {
+        return;
+    }
     AuthorizationSet auths;
     for (auto& entry : key_characteristics) {
         auths.push_back(AuthorizationSet(entry.authorizations));
     }
     for (auto digest : expected_mgf_digests) {
-        ASSERT_TRUE(auths.Contains(TAG_RSA_OAEP_MGF_DIGEST, digest));
+        if (is_mgf_digest_expected) {
+            ASSERT_TRUE(auths.Contains(TAG_RSA_OAEP_MGF_DIGEST, digest));
+        } else {
+            ASSERT_FALSE(auths.Contains(TAG_RSA_OAEP_MGF_DIGEST, digest));
+        }
     }
 }
 
-bool is_mgf_digest_present(const vector<KeyCharacteristics>& key_characteristics,
-                           android::hardware::security::keymint::Digest expected_mgf_digest) {
-    AuthorizationSet auths;
-    for (auto& entry : key_characteristics) {
-        auths.push_back(AuthorizationSet(entry.authorizations));
-    }
-    return auths.Contains(TAG_RSA_OAEP_MGF_DIGEST, expected_mgf_digest);
-}
-
 namespace {
 
 void check_cose_key(const vector<uint8_t>& data, bool testMode) {
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index 9778b3d..4fb711c 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -95,7 +95,7 @@
 
     void InitializeKeyMint(std::shared_ptr<IKeyMintDevice> keyMint);
     IKeyMintDevice& keyMint() { return *keymint_; }
-    int32_t AidlVersion();
+    int32_t AidlVersion() const;
     uint32_t os_version() { return os_version_; }
     uint32_t os_patch_level() { return os_patch_level_; }
     uint32_t vendor_patch_level() { return vendor_patch_level_; }
@@ -373,6 +373,15 @@
     bool shouldSkipAttestKeyTest(void) const;
     void skipAttestKeyTest(void) const;
 
+    void assert_mgf_digests_present_or_not_in_key_characteristics(
+            const vector<KeyCharacteristics>& key_characteristics,
+            std::vector<android::hardware::security::keymint::Digest>& expected_mgf_digests,
+            bool is_mgf_digest_expected) const;
+
+    void assert_mgf_digests_present_or_not_in_key_characteristics(
+            std::vector<android::hardware::security::keymint::Digest>& expected_mgf_digests,
+            bool is_mgf_digest_expected) const;
+
   protected:
     std::shared_ptr<IKeyMintDevice> keymint_;
     uint32_t os_version_;
@@ -430,11 +439,6 @@
 X509_Ptr parse_cert_blob(const vector<uint8_t>& blob);
 ASN1_OCTET_STRING* get_attestation_record(X509* certificate);
 vector<uint8_t> make_name_from_str(const string& name);
-void assert_mgf_digests_present_in_key_characteristics(
-        const vector<KeyCharacteristics>& key_characteristics,
-        std::vector<android::hardware::security::keymint::Digest>& expected_mgf_digests);
-bool is_mgf_digest_present(const vector<KeyCharacteristics>& key_characteristics,
-                           android::hardware::security::keymint::Digest expected_mgf_digest);
 void check_maced_pubkey(const MacedPublicKey& macedPubKey, bool testMode,
                         vector<uint8_t>* payload_value);
 void p256_pub_key(const vector<uint8_t>& coseKeyData, EVP_PKEY_Ptr* signingKey);
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index de563c4..a8f17dd 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -4743,6 +4743,12 @@
  * should have the correct characteristics.
  */
 TEST_P(ImportKeyTest, RsaOaepMGFDigestSuccess) {
+    // There was no test to assert that MGF1 digest was present in generated/imported key
+    // characteristics before Keymint V3, so there are some Keymint implementations where
+    // this test case fails(b/297306437), hence this test is skipped for Keymint < 3.
+    if (AidlVersion() < 3) {
+        GTEST_SKIP() << "Test not applicable to Keymint < V3";
+    }
     auto mgf_digests = ValidDigests(false /* withNone */, true /* withMD5 */);
     size_t key_size = 2048;
 
@@ -4763,7 +4769,7 @@
     CheckOrigin();
 
     // Make sure explicitly specified mgf-digests exist in key characteristics.
-    assert_mgf_digests_present_in_key_characteristics(key_characteristics_, mgf_digests);
+    assert_mgf_digests_present_or_not_in_key_characteristics(mgf_digests, true);
 
     string message = "Hello";
 
@@ -4827,8 +4833,9 @@
     CheckCryptoParam(TAG_PADDING, PaddingMode::RSA_OAEP);
     CheckOrigin();
 
+    vector defaultDigest = {Digest::SHA1};
     // Make sure default mgf-digest (SHA1) is not included in Key characteristics.
-    ASSERT_FALSE(is_mgf_digest_present(key_characteristics_, Digest::SHA1));
+    assert_mgf_digests_present_or_not_in_key_characteristics(defaultDigest, false);
 }
 
 INSTANTIATE_KEYMINT_AIDL_TEST(ImportKeyTest);
@@ -5256,7 +5263,7 @@
  */
 TEST_P(EncryptionOperationsTest, RsaOaepSuccess) {
     auto digests = ValidDigests(false /* withNone */, true /* withMD5 */);
-    auto mgf_digest = Digest::SHA1;
+    auto mgf_digest = vector{Digest::SHA1};
 
     size_t key_size = 2048;  // Need largish key for SHA-512 test.
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
@@ -5264,11 +5271,11 @@
                                                  .RsaEncryptionKey(key_size, 65537)
                                                  .Padding(PaddingMode::RSA_OAEP)
                                                  .Digest(digests)
-                                                 .Authorization(TAG_RSA_OAEP_MGF_DIGEST, mgf_digest)
+                                                 .OaepMGFDigest(mgf_digest)
                                                  .SetDefaultValidity()));
 
     // Make sure explicitly specified mgf-digest exist in key characteristics.
-    ASSERT_TRUE(is_mgf_digest_present(key_characteristics_, mgf_digest));
+    assert_mgf_digests_present_or_not_in_key_characteristics(mgf_digest, true);
 
     string message = "Hello";
 
@@ -5393,21 +5400,21 @@
                                                  .Padding(PaddingMode::RSA_OAEP)
                                                  .Digest(Digest::SHA_2_256)
                                                  .SetDefaultValidity()));
+    if (AidlVersion() >= 3) {
+        std::vector<Digest> mgf1DigestsInAuths;
+        mgf1DigestsInAuths.reserve(digests.size());
+        const auto& hw_auths = SecLevelAuthorizations(key_characteristics_);
+        std::for_each(hw_auths.begin(), hw_auths.end(), [&](auto& param) {
+            if (param.tag == Tag::RSA_OAEP_MGF_DIGEST) {
+                KeyParameterValue value = param.value;
+                mgf1DigestsInAuths.push_back(param.value.template get<KeyParameterValue::digest>());
+            }
+        });
 
-    std::vector<Digest> mgf1DigestsInAuths;
-    mgf1DigestsInAuths.reserve(digests.size());
-    const auto& hw_auths = SecLevelAuthorizations(key_characteristics_);
-    std::for_each(hw_auths.begin(), hw_auths.end(), [&](auto& param) {
-        if (param.tag == Tag::RSA_OAEP_MGF_DIGEST) {
-            KeyParameterValue value = param.value;
-            mgf1DigestsInAuths.push_back(param.value.template get<KeyParameterValue::digest>());
-        }
-    });
-
-    std::sort(digests.begin(), digests.end());
-    std::sort(mgf1DigestsInAuths.begin(), mgf1DigestsInAuths.end());
-    EXPECT_EQ(digests, mgf1DigestsInAuths);
-
+        std::sort(digests.begin(), digests.end());
+        std::sort(mgf1DigestsInAuths.begin(), mgf1DigestsInAuths.end());
+        EXPECT_EQ(digests, mgf1DigestsInAuths);
+    }
     string message = "Hello";
 
     for (auto digest : digests) {
@@ -5462,8 +5469,9 @@
                                                  .Digest(Digest::SHA_2_256)
                                                  .SetDefaultValidity()));
 
+    vector defaultDigest = vector{Digest::SHA1};
     // Make sure default mgf-digest (SHA1) is not included in Key characteristics.
-    ASSERT_FALSE(is_mgf_digest_present(key_characteristics_, Digest::SHA1));
+    assert_mgf_digests_present_or_not_in_key_characteristics(defaultDigest, false);
 
     // Do local RSA encryption using the default MGF digest of SHA-1.
     string message = "Hello";
@@ -5499,19 +5507,20 @@
  */
 TEST_P(EncryptionOperationsTest, RsaOaepMGFDigestDefaultFail) {
     size_t key_size = 2048;
-    auto mgf_digest = Digest::SHA_2_256;
+    auto mgf_digest = vector{Digest::SHA_2_256};
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
-                                                 .Authorization(TAG_RSA_OAEP_MGF_DIGEST, mgf_digest)
+                                                 .OaepMGFDigest(mgf_digest)
                                                  .RsaEncryptionKey(key_size, 65537)
                                                  .Padding(PaddingMode::RSA_OAEP)
                                                  .Digest(Digest::SHA_2_256)
                                                  .SetDefaultValidity()));
 
     // Make sure explicitly specified mgf-digest exist in key characteristics.
-    ASSERT_TRUE(is_mgf_digest_present(key_characteristics_, mgf_digest));
+    assert_mgf_digests_present_or_not_in_key_characteristics(mgf_digest, true);
+    vector defaultDigest = vector{Digest::SHA1};
     // Make sure default mgf-digest is not included in key characteristics.
-    ASSERT_FALSE(is_mgf_digest_present(key_characteristics_, Digest::SHA1));
+    assert_mgf_digests_present_or_not_in_key_characteristics(defaultDigest, false);
 
     // Do local RSA encryption using the default MGF digest of SHA-1.
     string message = "Hello";
@@ -5535,16 +5544,17 @@
  * with incompatible MGF digest.
  */
 TEST_P(EncryptionOperationsTest, RsaOaepWithMGFIncompatibleDigest) {
-    auto mgf_digest = Digest::SHA_2_256;
+    auto mgf_digest = vector{Digest::SHA_2_256};
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                                 .Authorization(TAG_RSA_OAEP_MGF_DIGEST, mgf_digest)
+                                                 .OaepMGFDigest(mgf_digest)
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .RsaEncryptionKey(2048, 65537)
                                                  .Padding(PaddingMode::RSA_OAEP)
                                                  .Digest(Digest::SHA_2_256)
                                                  .SetDefaultValidity()));
+
     // Make sure explicitly specified mgf-digest exist in key characteristics.
-    ASSERT_TRUE(is_mgf_digest_present(key_characteristics_, mgf_digest));
+    assert_mgf_digests_present_or_not_in_key_characteristics(mgf_digest, true);
 
     string message = "Hello World!";
 
@@ -5562,16 +5572,17 @@
  * with unsupported MGF digest.
  */
 TEST_P(EncryptionOperationsTest, RsaOaepWithMGFUnsupportedDigest) {
-    auto mgf_digest = Digest::SHA_2_256;
+    auto mgf_digest = vector{Digest::SHA_2_256};
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
-                                                 .Authorization(TAG_RSA_OAEP_MGF_DIGEST, mgf_digest)
+                                                 .OaepMGFDigest(mgf_digest)
                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                                  .RsaEncryptionKey(2048, 65537)
                                                  .Padding(PaddingMode::RSA_OAEP)
                                                  .Digest(Digest::SHA_2_256)
                                                  .SetDefaultValidity()));
+
     // Make sure explicitly specified mgf-digest exist in key characteristics.
-    ASSERT_TRUE(is_mgf_digest_present(key_characteristics_, mgf_digest));
+    assert_mgf_digests_present_or_not_in_key_characteristics(mgf_digest, true);
 
     string message = "Hello World!";
 
diff --git a/security/rkp/aidl/android/hardware/security/keymint/generateCertificateRequestV2.cddl b/security/rkp/aidl/android/hardware/security/keymint/generateCertificateRequestV2.cddl
index 15b0442..61404d4 100644
--- a/security/rkp/aidl/android/hardware/security/keymint/generateCertificateRequestV2.cddl
+++ b/security/rkp/aidl/android/hardware/security/keymint/generateCertificateRequestV2.cddl
@@ -101,17 +101,19 @@
     -4670545 : bstr,                         ; Code Hash
     ? -4670546 : bstr,                       ; Code Descriptor
     -4670547 : bstr,                         ; Configuration Hash
-    -4670548 : bstr .cbor {                  ; Configuration Descriptor
-        ? -70002 : tstr,                         ; Component name
-        ? -70003 : int / tstr,                   ; Component version
-        ? -70004 : null,                         ; Resettable
-        ? -70005 : uint,                         ; Security version
-    },
+    -4670548 : bstr .cbor ConfigurationDescriptor,
     -4670549 : bstr,                         ; Authority Hash
     ? -4670550 : bstr,                       ; Authority Descriptor
     -4670551 : bstr,                         ; Mode
 }
 
+ConfigurationDescriptor = {                  ; Configuration Descriptor
+    ? -70002 : tstr,                         ; Component name
+    ? -70003 : int / tstr,                   ; Component version
+    ? -70004 : null,                         ; Resettable
+    ? -70005 : uint,                         ; Security version
+}
+
 ; Each entry in the DICE chain is a DiceChainEntryPayload signed by the key from the previous
 ; entry in the DICE chain array.
 DiceChainEntry = [                            ; COSE_Sign1 (untagged), [RFC9052 s4.2]
diff --git a/sensors/aidl/default/Android.bp b/sensors/aidl/default/Android.bp
index 16b4d35..384ee97 100644
--- a/sensors/aidl/default/Android.bp
+++ b/sensors/aidl/default/Android.bp
@@ -28,9 +28,11 @@
     srcs: ["sensors-default.rc"],
 }
 
-filegroup {
+prebuilt_etc {
     name: "sensors-default.xml",
-    srcs: ["sensors-default.xml"],
+    src: "sensors-default.xml",
+    sub_dir: "vintf",
+    installable: false,
 }
 
 cc_library_static {
diff --git a/sensors/aidl/default/apex/Android.bp b/sensors/aidl/default/apex/Android.bp
index ceb428b..5482086 100644
--- a/sensors/aidl/default/apex/Android.bp
+++ b/sensors/aidl/default/apex/Android.bp
@@ -2,17 +2,6 @@
     default_applicable_licenses: ["hardware_interfaces_license"],
 }
 
-apex_key {
-    name: "com.android.hardware.sensors.key",
-    public_key: "com.android.hardware.sensors.avbpubkey",
-    private_key: "com.android.hardware.sensors.pem",
-}
-
-android_app_certificate {
-    name: "com.android.hardware.sensors.certificate",
-    certificate: "com.android.hardware.sensors",
-}
-
 genrule {
     name: "com.android.hardware.sensors.rc-gen",
     srcs: [":sensors-default.rc"],
@@ -31,16 +20,16 @@
 apex {
     name: "com.android.hardware.sensors",
     manifest: "apex_manifest.json",
-    key: "com.android.hardware.sensors.key",
-    certificate: ":com.android.hardware.sensors.certificate",
+    key: "com.android.hardware.key",
+    certificate: ":com.android.hardware.certificate",
     file_contexts: "file_contexts",
-    use_vndk_as_stable: true,
     updatable: false,
-    // Install the apex in /vendor/apex
-    soc_specific: true,
+    vendor: true,
+
     binaries: ["android.hardware.sensors-service.example"],
     prebuilts: [
-        "com.android.hardware.sensors.rc",
+        "com.android.hardware.sensors.rc", // init rc
+        "sensors-default.xml", // vintf fragment
         "android.hardware.sensor.ambient_temperature.prebuilt.xml",
         "android.hardware.sensor.barometer.prebuilt.xml",
         "android.hardware.sensor.gyroscope.prebuilt.xml",
@@ -49,5 +38,4 @@
         "android.hardware.sensor.proximity.prebuilt.xml",
         "android.hardware.sensor.relative_humidity.prebuilt.xml",
     ],
-    vintf_fragments: [":sensors-default.xml"],
 }
diff --git a/sensors/aidl/default/apex/file_contexts b/sensors/aidl/default/apex/file_contexts
index 27be16b..6d231f8 100644
--- a/sensors/aidl/default/apex/file_contexts
+++ b/sensors/aidl/default/apex/file_contexts
@@ -1,5 +1,3 @@
-(/.*)?							u:object_r:vendor_file:s0
-# Permission XMLs
-/etc/permissions(/.*)?					u:object_r:vendor_configs_file:s0
-# Service binary
-/bin/hw/android\.hardware\.sensors-service\.example	u:object_r:hal_sensors_default_exec:s0
\ No newline at end of file
+(/.*)?                                                u:object_r:vendor_file:s0
+/etc(/.*)?                                            u:object_r:vendor_configs_file:s0
+/bin/hw/android\.hardware\.sensors-service\.example   u:object_r:hal_sensors_default_exec:s0
\ No newline at end of file
diff --git a/sensors/aidl/vts/SensorsAidlTestSharedMemory.h b/sensors/aidl/vts/SensorsAidlTestSharedMemory.h
index 4b5916a..200f26f 100644
--- a/sensors/aidl/vts/SensorsAidlTestSharedMemory.h
+++ b/sensors/aidl/vts/SensorsAidlTestSharedMemory.h
@@ -17,7 +17,11 @@
 #ifndef ANDROID_SENSORS_AIDL_TEST_SHARED_MEMORY_H
 #define ANDROID_SENSORS_AIDL_TEST_SHARED_MEMORY_H
 
-#include "sensors-vts-utils/GrallocWrapper.h"
+#include <aidl/android/hardware/graphics/common/BufferUsage.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/GraphicBufferMapper.h>
 
 #include <aidlcommonsupport/NativeHandle.h>
 #include <android-base/macros.h>
@@ -28,6 +32,8 @@
 
 #include <cutils/ashmem.h>
 
+using ::aidl::android::hardware::graphics::common::BufferUsage;
+using ::aidl::android::hardware::graphics::common::PixelFormat;
 using ::aidl::android::hardware::sensors::BnSensors;
 using ::aidl::android::hardware::sensors::Event;
 using ::aidl::android::hardware::sensors::ISensors;
@@ -53,12 +59,22 @@
     }
 
     ISensors::SharedMemInfo getSharedMemInfo() const {
-        ISensors::SharedMemInfo mem = {
-                .type = mType,
-                .format = ISensors::SharedMemInfo::SharedMemFormat::SENSORS_EVENT,
-                .size = static_cast<int32_t>(mSize),
-                .memoryHandle = android::dupToAidl(mNativeHandle)};
-        return mem;
+        if (mType == ISensors::SharedMemInfo::SharedMemType::GRALLOC) {
+            ISensors::SharedMemInfo mem = {
+                    .type = mType,
+                    .format = ISensors::SharedMemInfo::SharedMemFormat::SENSORS_EVENT,
+                    .size = static_cast<int32_t>(mSize),
+                    .memoryHandle = android::dupToAidl(mBufferHandle)};
+            return mem;
+
+        } else {
+            ISensors::SharedMemInfo mem = {
+                    .type = mType,
+                    .format = ISensors::SharedMemInfo::SharedMemFormat::SENSORS_EVENT,
+                    .size = static_cast<int32_t>(mSize),
+                    .memoryHandle = android::dupToAidl(mNativeHandle)};
+            return mem;
+        }
     }
     char* getBuffer() const { return mBuffer; }
     size_t getSize() const { return mSize; }
@@ -141,17 +157,26 @@
             }
             case ISensors::SharedMemInfo::SharedMemType::GRALLOC: {
                 if (mSize != 0) {
-                    mGrallocWrapper->freeBuffer(mNativeHandle);
-                    mNativeHandle = nullptr;
+                    android::status_t status =
+                            android::GraphicBufferAllocator::get().free(mBufferHandle);
+                    if (status != android::OK) {
+                        ALOGE("SensorsAidlTestSharedMemory Gralloc failed to free buffer. Status: "
+                              "%s",
+                              android::statusToString(status).c_str());
+                    }
+                    mBufferHandle = nullptr;
+                    mBuffer = nullptr;
                     mSize = 0;
                 }
                 break;
             }
             default: {
-                if (mNativeHandle != nullptr || mSize != 0 || mBuffer != nullptr) {
+                if (mNativeHandle != nullptr || mSize != 0 || mBuffer != nullptr ||
+                    mBufferHandle != nullptr) {
                     ALOGE("SensorsAidlTestSharedMemory %p not properly destructed: "
-                          "type %d, native handle %p, size %zu, buffer %p",
-                          this, static_cast<int>(mType), mNativeHandle, mSize, mBuffer);
+                          "type %d, native handle %p, size %zu, buffer %p, buffer handle %p",
+                          this, static_cast<int>(mType), mNativeHandle, mSize, mBuffer,
+                          mBufferHandle);
                 }
                 break;
             }
@@ -185,14 +210,33 @@
                 break;
             }
             case ISensors::SharedMemInfo::SharedMemType::GRALLOC: {
-                mGrallocWrapper = std::make_unique<::android::GrallocWrapper>();
-                if (!mGrallocWrapper->isInitialized()) {
+                static constexpr uint64_t kBufferUsage =
+                        static_cast<uint64_t>(BufferUsage::SENSOR_DIRECT_DATA) |
+                        static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN) |
+                        static_cast<uint64_t>(BufferUsage::CPU_WRITE_RARELY);
+
+                uint32_t stride = 0;
+                buffer_handle_t bufferHandle;
+                android::status_t status = android::GraphicBufferAllocator::get().allocate(
+                        size, 1, static_cast<int>(PixelFormat::BLOB), 1, kBufferUsage,
+                        &bufferHandle, &stride, "SensorVts");
+                if (status != android::OK) {
+                    ALOGE("SensorsAidlTestSharedMemory failed to allocate memory. Status: %s",
+                          android::statusToString(status).c_str());
                     break;
                 }
-
-                std::pair<native_handle_t*, void*> buf = mGrallocWrapper->allocate(size);
-                handle = buf.first;
-                buffer = static_cast<char*>(buf.second);
+                // Per the HAL, all-zeros Rect means the entire buffer
+                android::Rect rect = {0, 0, 0, 0};
+                void* ret;
+                status = android::GraphicBufferMapper::get().lock(bufferHandle, kBufferUsage, rect,
+                                                                  &ret);
+                if (status != android::OK) {
+                    ALOGE("SensorsAidlTestSharedMemory failed to import buffer: Status: %s",
+                          android::statusToString(status).c_str());
+                } else {
+                    buffer = static_cast<char*>(ret);
+                    mBufferHandle = bufferHandle;
+                }
                 break;
             }
             default:
@@ -208,9 +252,9 @@
 
     ISensors::SharedMemInfo::SharedMemType mType;
     native_handle_t* mNativeHandle;
+    buffer_handle_t mBufferHandle;
     size_t mSize;
     char* mBuffer;
-    std::unique_ptr<::android::GrallocWrapper> mGrallocWrapper;
 
     DISALLOW_COPY_AND_ASSIGN(SensorsAidlTestSharedMemory);
 };
diff --git a/sensors/aidl/vts/VtsAidlHalSensorsTargetTest.cpp b/sensors/aidl/vts/VtsAidlHalSensorsTargetTest.cpp
index 456aee7..0b15d12 100644
--- a/sensors/aidl/vts/VtsAidlHalSensorsTargetTest.cpp
+++ b/sensors/aidl/vts/VtsAidlHalSensorsTargetTest.cpp
@@ -41,6 +41,7 @@
 using aidl::android::hardware::sensors::SensorInfo;
 using aidl::android::hardware::sensors::SensorStatus;
 using aidl::android::hardware::sensors::SensorType;
+using aidl::android::hardware::sensors::AdditionalInfo;
 using android::ProcessState;
 using std::chrono::duration_cast;
 
@@ -629,6 +630,15 @@
     Event additionalInfoEvent;
     additionalInfoEvent.sensorType = SensorType::ADDITIONAL_INFO;
     additionalInfoEvent.timestamp = android::elapsedRealtimeNano();
+    AdditionalInfo info;
+    info.type = AdditionalInfo::AdditionalInfoType::AINFO_BEGIN;
+    info.serial = 1;
+    AdditionalInfo::AdditionalInfoPayload::Int32Values infoData;
+    for (int32_t i = 0; i < 14; i++) {
+        infoData.values[i] = i;
+    }
+    info.payload.set<AdditionalInfo::AdditionalInfoPayload::Tag::dataInt32>(infoData);
+    additionalInfoEvent.payload.set<Event::EventPayload::Tag::additional>(info);
 
     Event injectedEvent;
     injectedEvent.timestamp = android::elapsedRealtimeNano();
diff --git a/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h b/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
index b381580..becc93c 100644
--- a/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
+++ b/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
@@ -52,8 +52,6 @@
 using ::android::hardware::sensors::V1_0::Vec3;
 using ::android::hardware::sensors::V2_1::implementation::convertToOldSensorInfos;
 using std::chrono::duration_cast;
-using std::chrono::microseconds;
-using std::chrono::milliseconds;
 using std::chrono::nanoseconds;
 
 using EventV1_0 = ::android::hardware::sensors::V1_0::Event;
@@ -91,7 +89,7 @@
     }
 
     void waitForFlushEvents(const std::vector<SensorInfoType>& sensorsToWaitFor,
-                            int32_t numCallsToFlush, milliseconds timeout) {
+                            int32_t numCallsToFlush, std::chrono::milliseconds timeout) {
         std::unique_lock<std::recursive_mutex> lock(mFlushMutex);
         mFlushCV.wait_for(lock, timeout,
                           [&] { return flushesReceived(sensorsToWaitFor, numCallsToFlush); });
@@ -102,7 +100,8 @@
         return mEventMap[sensorHandle];
     }
 
-    void waitForEvents(const std::vector<SensorInfoType>& sensorsToWaitFor, milliseconds timeout) {
+    void waitForEvents(const std::vector<SensorInfoType>& sensorsToWaitFor,
+                       std::chrono::milliseconds timeout) {
         std::unique_lock<std::recursive_mutex> lock(mEventMutex);
         mEventCV.wait_for(lock, timeout, [&] { return eventsReceived(sensorsToWaitFor); });
     }
@@ -472,7 +471,7 @@
     }
 
     // Wait for events to be written back to the Event FMQ
-    callback.waitForEvents(sensors, milliseconds(1000) /* timeout */);
+    callback.waitForEvents(sensors, std::chrono::milliseconds(1000) /* timeout */);
     getEnvironment()->unregisterCallback();
 
     for (const auto& s : sensors) {
@@ -623,7 +622,7 @@
     }
 
     // Wait up to one second for the flush events
-    callback.waitForFlushEvents(sensors, flushCalls, milliseconds(1000) /* timeout */);
+    callback.waitForFlushEvents(sensors, flushCalls, std::chrono::milliseconds(1000) /* timeout */);
 
     // Deactivate all sensors after waiting for flush events so pending flush events are not
     // abandoned by the HAL.
@@ -748,8 +747,8 @@
 }
 
 TEST_P(SensorsHidlTest, NoStaleEvents) {
-    constexpr milliseconds kFiveHundredMs(500);
-    constexpr milliseconds kOneSecond(1000);
+    constexpr std::chrono::milliseconds kFiveHundredMs(500);
+    constexpr std::chrono::milliseconds kOneSecond(1000);
 
     // Register the callback to receive sensor events
     EventCallback callback;
@@ -757,10 +756,11 @@
 
     // This test is not valid for one-shot, on-change or special-report-mode sensors
     const std::vector<SensorInfoType> sensors = getNonOneShotAndNonOnChangeAndNonSpecialSensors();
-    milliseconds maxMinDelay(0);
+    std::chrono::milliseconds maxMinDelay(0);
     for (const SensorInfoType& sensor : sensors) {
-        milliseconds minDelay = duration_cast<milliseconds>(microseconds(sensor.minDelay));
-        maxMinDelay = milliseconds(std::max(maxMinDelay.count(), minDelay.count()));
+        std::chrono::milliseconds minDelay = duration_cast<std::chrono::milliseconds>(
+                std::chrono::microseconds(sensor.minDelay));
+        maxMinDelay = std::chrono::milliseconds(std::max(maxMinDelay.count(), minDelay.count()));
     }
 
     // Activate the sensors so that they start generating events
@@ -787,7 +787,7 @@
     }
 
     // Allow some time to pass, reset the callback, then reactivate the sensors
-    usleep(duration_cast<microseconds>(kOneSecond + (5 * maxMinDelay)).count());
+    usleep(duration_cast<std::chrono::microseconds>(kOneSecond + (5 * maxMinDelay)).count());
     callback.reset();
     activateAllSensors(true);
     callback.waitForEvents(sensors, kFiveHundredMs + (5 * maxMinDelay));
@@ -815,9 +815,10 @@
         // Ensure that the first event received is not stale by ensuring that its timestamp is
         // sufficiently different from the previous event
         const EventType newEvent = events.front();
-        milliseconds delta = duration_cast<milliseconds>(
+        std::chrono::milliseconds delta = duration_cast<std::chrono::milliseconds>(
                 nanoseconds(newEvent.timestamp - lastEventTimestampMap[sensor.sensorHandle]));
-        milliseconds sensorMinDelay = duration_cast<milliseconds>(microseconds(sensor.minDelay));
+        std::chrono::milliseconds sensorMinDelay = duration_cast<std::chrono::milliseconds>(
+                std::chrono::microseconds(sensor.minDelay));
         ASSERT_GE(delta, kFiveHundredMs + (3 * sensorMinDelay));
     }
 }
diff --git a/sensors/common/vts/utils/Android.bp b/sensors/common/vts/utils/Android.bp
index b35280a..ab3984c 100644
--- a/sensors/common/vts/utils/Android.bp
+++ b/sensors/common/vts/utils/Android.bp
@@ -35,6 +35,7 @@
         "libbinder_ndk",
         "libutils",
         "libvndksupport",
+        "libui",
     ],
     static_libs: [
         "libaidlcommonsupport",
@@ -50,9 +51,6 @@
         "android.hardware.graphics.common-ndk_shared",
     ],
     cflags: ["-DLOG_TAG=\"sensors_hidl_hal_test\""],
-    srcs: [
-        "GrallocWrapper.cpp",
-    ],
     export_include_dirs: [
         "include",
     ],
@@ -64,6 +62,7 @@
         "libbinder_ndk",
         "libutils",
         "libvndksupport",
+        "libui",
     ],
     static_libs: [
         "android.hardware.sensors@1.0",
@@ -71,13 +70,4 @@
         "android.hardware.sensors@2.1",
         "libaidlcommonsupport",
     ],
-    whole_static_libs: [
-        "android.hardware.graphics.allocator@2.0",
-        "android.hardware.graphics.allocator@3.0",
-        "android.hardware.graphics.allocator@4.0",
-        "android.hardware.graphics.mapper@2.0",
-        "android.hardware.graphics.mapper@2.1",
-        "android.hardware.graphics.mapper@3.0",
-        "android.hardware.graphics.mapper@4.0",
-    ],
 }
diff --git a/sensors/common/vts/utils/GrallocWrapper.cpp b/sensors/common/vts/utils/GrallocWrapper.cpp
deleted file mode 100644
index a15e7fe..0000000
--- a/sensors/common/vts/utils/GrallocWrapper.cpp
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "GrallocWrapper.h"
-
-#include <aidl/android/hardware/graphics/allocator/IAllocator.h>
-#include <aidlcommonsupport/NativeHandle.h>
-#include <android/binder_manager.h>
-#include <android/hardware/graphics/allocator/2.0/IAllocator.h>
-#include <android/hardware/graphics/allocator/3.0/IAllocator.h>
-#include <android/hardware/graphics/allocator/4.0/IAllocator.h>
-#include <android/hardware/graphics/mapper/2.0/IMapper.h>
-#include <android/hardware/graphics/mapper/2.1/IMapper.h>
-#include <android/hardware/graphics/mapper/3.0/IMapper.h>
-#include <android/hardware/graphics/mapper/4.0/IMapper.h>
-
-#include <utils/Log.h>
-
-#include <cinttypes>
-#include <memory>
-#include <type_traits>
-
-using IAllocatorAidl = ::aidl::android::hardware::graphics::allocator::IAllocator;
-using IAllocator2 = ::android::hardware::graphics::allocator::V2_0::IAllocator;
-using IAllocator3 = ::android::hardware::graphics::allocator::V3_0::IAllocator;
-using IAllocator4 = ::android::hardware::graphics::allocator::V4_0::IAllocator;
-using IMapper2 = ::android::hardware::graphics::mapper::V2_0::IMapper;
-using IMapper2_1 = ::android::hardware::graphics::mapper::V2_1::IMapper;
-using IMapper3 = ::android::hardware::graphics::mapper::V3_0::IMapper;
-using IMapper4 = ::android::hardware::graphics::mapper::V4_0::IMapper;
-
-using Error2 = ::android::hardware::graphics::mapper::V2_0::Error;
-using Error3 = ::android::hardware::graphics::mapper::V3_0::Error;
-using Error4 = ::android::hardware::graphics::mapper::V4_0::Error;
-
-using ::aidl::android::hardware::common::NativeHandle;
-using ::aidl::android::hardware::graphics::allocator::AllocationResult;
-
-using ::android::hardware::graphics::common::V1_0::BufferUsage;
-using ::android::hardware::graphics::common::V1_0::PixelFormat;
-
-using ::android::hardware::hidl_handle;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-
-namespace android {
-
-// Since we use the same APIs across allocator/mapper HALs but they have major
-// version differences (meaning they are not related through inheritance), we
-// create a common interface abstraction for the IAllocator + IMapper combination
-// (major versions need to match in the current HALs, e.g. IAllocator 3.0 needs to
-// be paired with IMapper 3.0, so these are tied together)
-class IGrallocHalWrapper {
-  public:
-    virtual ~IGrallocHalWrapper() = default;
-
-    // IAllocator
-    virtual native_handle_t* allocate(uint32_t size) = 0;
-    virtual void freeBuffer(native_handle_t* bufferHandle) = 0;
-
-    // IMapper
-    virtual void* lock(native_handle_t* bufferHandle) = 0;
-    virtual void unlock(native_handle_t* bufferHandle) = 0;
-};
-
-namespace {
-
-bool failed(Error2 error) {
-    return (error != Error2::NONE);
-}
-bool failed(Error3 error) {
-    return (error != Error3::NONE);
-}
-bool failed(Error4 error) {
-    return (error != Error4::NONE);
-}
-
-template <typename>
-struct FirstArg;
-
-// Template specialization for pointer to a non-static member function, which exposes
-// the type of the first argument given to said function
-template <typename ReturnType, typename ClassT, typename Arg1, typename... OtherArgs>
-struct FirstArg<ReturnType (ClassT::*)(Arg1, OtherArgs...)> {
-    using type = Arg1;
-};
-
-// Alias to FirstArg which also removes any reference type and const associated
-template <typename T>
-using BaseTypeOfFirstArg = typename std::remove_const<
-        typename std::remove_reference<typename FirstArg<T>::type>::type>::type;
-
-// Since all the type and function names are the same for the things we use across the major HAL
-// versions, we use template magic to avoid repeating ourselves.
-template <typename AllocatorT, typename MapperT,
-          template <typename> typename AllocatorWrapperT = sp>
-class GrallocHalWrapper : public IGrallocHalWrapper {
-  public:
-    GrallocHalWrapper(const AllocatorWrapperT<AllocatorT>& allocator, const sp<MapperT>& mapper)
-        : mAllocator(allocator), mMapper(mapper) {
-        if (mapper->isRemote()) {
-            ALOGE("Mapper is in passthrough mode");
-        }
-    }
-
-    virtual native_handle_t* allocate(uint32_t size) override;
-    virtual void freeBuffer(native_handle_t* bufferHandle) override;
-
-    virtual void* lock(native_handle_t* bufferHandle) override;
-    virtual void unlock(native_handle_t* bufferHandle) override;
-
-  private:
-    static constexpr uint64_t kBufferUsage =
-            static_cast<uint64_t>(BufferUsage::SENSOR_DIRECT_DATA | BufferUsage::CPU_READ_OFTEN |
-                                  BufferUsage::CPU_WRITE_RARELY);
-    AllocatorWrapperT<AllocatorT> mAllocator;
-    sp<MapperT> mMapper;
-
-    // v2.0 and v3.0 use vec<uint32_t> for BufferDescriptor, but v4.0 uses vec<uint8_t>, so use
-    // some template magic to deduce the right type based off of the first argument to allocate(),
-    // which is always the version-specific BufferDescriptor type
-    typedef BaseTypeOfFirstArg<decltype(&AllocatorT::allocate)> BufferDescriptorT;
-
-    BufferDescriptorT getDescriptor(uint32_t size);
-    native_handle_t* importBuffer(const hidl_handle& rawHandle);
-};
-
-template <>
-native_handle_t* GrallocHalWrapper<IAllocatorAidl, IMapper4, std::shared_ptr>::allocate(
-        uint32_t size) {
-    constexpr uint32_t kBufferCount = 1;
-    BufferDescriptorT descriptor = getDescriptor(size);
-    native_handle_t* bufferHandle = nullptr;
-
-    AllocationResult result;
-    auto status = mAllocator->allocate(descriptor, kBufferCount, &result);
-    if (!status.isOk()) {
-        status_t error = status.getExceptionCode();
-        ALOGE("Failed to allocate buffer: %" PRId32, static_cast<int32_t>(error));
-    } else if (result.buffers.size() != kBufferCount) {
-        ALOGE("Invalid buffer array size (got %zu, expected %" PRIu32 ")", result.buffers.size(),
-              kBufferCount);
-    } else {
-        // Convert from AIDL NativeHandle to native_handle_t to hidl_handle
-        hidl_handle hidlHandle;
-        hidlHandle.setTo(dupFromAidl(result.buffers[0]), /*shouldOwn*/ true);
-        bufferHandle = importBuffer(hidlHandle);
-    }
-
-    return bufferHandle;
-}
-
-template <typename AllocatorT, typename MapperT, template <typename> typename AllocatorWrapperT>
-native_handle_t* GrallocHalWrapper<AllocatorT, MapperT, AllocatorWrapperT>::allocate(
-        uint32_t size) {
-    constexpr uint32_t kBufferCount = 1;
-    BufferDescriptorT descriptor = getDescriptor(size);
-    native_handle_t* bufferHandle = nullptr;
-
-    auto callback = [&](auto error, uint32_t /*stride*/, const hidl_vec<hidl_handle>& buffers) {
-        if (failed(error)) {
-            ALOGE("Failed to allocate buffer: %" PRId32, static_cast<int32_t>(error));
-        } else if (buffers.size() != kBufferCount) {
-            ALOGE("Invalid buffer array size (got %zu, expected %" PRIu32 ")", buffers.size(),
-                  kBufferCount);
-        } else {
-            bufferHandle = importBuffer(buffers[0]);
-        }
-    };
-
-    mAllocator->allocate(descriptor, kBufferCount, callback);
-    return bufferHandle;
-}
-
-template <typename AllocatorT, typename MapperT, template <typename> typename AllocatorWrapperT>
-void GrallocHalWrapper<AllocatorT, MapperT, AllocatorWrapperT>::freeBuffer(
-        native_handle_t* bufferHandle) {
-    auto error = mMapper->freeBuffer(bufferHandle);
-    if (!error.isOk() || failed(error)) {
-        ALOGE("Failed to free buffer %p", bufferHandle);
-    }
-}
-
-template <typename AllocatorT, typename MapperT, template <typename> typename AllocatorWrapperT>
-typename GrallocHalWrapper<AllocatorT, MapperT, AllocatorWrapperT>::BufferDescriptorT
-GrallocHalWrapper<AllocatorT, MapperT, AllocatorWrapperT>::getDescriptor(uint32_t size) {
-    typename MapperT::BufferDescriptorInfo descriptorInfo = {
-            .width = size,
-            .height = 1,
-            .layerCount = 1,
-            .format = static_cast<decltype(descriptorInfo.format)>(PixelFormat::BLOB),
-            .usage = kBufferUsage,
-    };
-
-    BufferDescriptorT descriptor;
-    auto callback = [&](auto error, const BufferDescriptorT& tmpDescriptor) {
-        if (failed(error)) {
-            ALOGE("Failed to create descriptor: %" PRId32, static_cast<int32_t>(error));
-        } else {
-            descriptor = tmpDescriptor;
-        }
-    };
-
-    mMapper->createDescriptor(descriptorInfo, callback);
-    return descriptor;
-}
-
-template <typename AllocatorT, typename MapperT, template <typename> typename AllocatorWrapperT>
-native_handle_t* GrallocHalWrapper<AllocatorT, MapperT, AllocatorWrapperT>::importBuffer(
-        const hidl_handle& rawHandle) {
-    native_handle_t* bufferHandle = nullptr;
-
-    mMapper->importBuffer(rawHandle, [&](auto error, void* tmpBuffer) {
-        if (failed(error)) {
-            ALOGE("Failed to import buffer %p: %" PRId32, rawHandle.getNativeHandle(),
-                  static_cast<int32_t>(error));
-        } else {
-            bufferHandle = static_cast<native_handle_t*>(tmpBuffer);
-        }
-    });
-
-    return bufferHandle;
-}
-
-template <typename AllocatorT, typename MapperT, template <typename> typename AllocatorWrapperT>
-void* GrallocHalWrapper<AllocatorT, MapperT, AllocatorWrapperT>::lock(
-        native_handle_t* bufferHandle) {
-    // Per the HAL, all-zeros Rect means the entire buffer
-    typename MapperT::Rect accessRegion = {};
-    hidl_handle acquireFenceHandle;  // No fence needed, already safe to lock
-
-    void* data = nullptr;
-    mMapper->lock(bufferHandle, kBufferUsage, accessRegion, acquireFenceHandle,
-                  [&](auto error, void* tmpData, ...) {  // V3/4 pass extra args we don't use
-                      if (failed(error)) {
-                          ALOGE("Failed to lock buffer %p: %" PRId32, bufferHandle,
-                                static_cast<int32_t>(error));
-                      } else {
-                          data = tmpData;
-                      }
-                  });
-
-    return data;
-}
-
-template <typename AllocatorT, typename MapperT, template <typename> typename AllocatorWrapperT>
-void GrallocHalWrapper<AllocatorT, MapperT, AllocatorWrapperT>::unlock(
-        native_handle_t* bufferHandle) {
-    mMapper->unlock(bufferHandle, [&](auto error, const hidl_handle& /*releaseFence*/) {
-        if (failed(error)) {
-            ALOGE("Failed to unlock buffer %p: %" PRId32, bufferHandle,
-                  static_cast<int32_t>(error));
-        }
-    });
-}
-
-}  // anonymous namespace
-
-GrallocWrapper::GrallocWrapper() {
-    sp<IAllocator4> allocator4 = IAllocator4::getService();
-    sp<IMapper4> mapper4 = IMapper4::getService();
-
-    const auto kAllocatorSvc = std::string(IAllocatorAidl::descriptor) + "/default";
-    std::shared_ptr<IAllocatorAidl> allocatorAidl;
-    if (AServiceManager_isDeclared(kAllocatorSvc.c_str())) {
-        allocatorAidl = IAllocatorAidl::fromBinder(
-                ndk::SpAIBinder(AServiceManager_checkService(kAllocatorSvc.c_str())));
-    }
-
-    // As of T, AIDL Allocator is supported only with HIDL Mapper4
-    // (ref: VtsHalGraphicsAllocatorAidl_TargetTest.cpp)
-    if (allocatorAidl != nullptr && mapper4 != nullptr) {
-        ALOGD("Using AIDL IAllocator + HIDL IMapper v4.0");
-        mGrallocHal = std::unique_ptr<IGrallocHalWrapper>(
-                new GrallocHalWrapper<IAllocatorAidl, IMapper4, std::shared_ptr>(allocatorAidl,
-                                                                                 mapper4));
-    } else if (allocator4 != nullptr && mapper4 != nullptr) {
-        ALOGD("AIDL IAllocator not found, using HIDL IAllocator/IMapper v4.0");
-        mGrallocHal = std::unique_ptr<IGrallocHalWrapper>(
-                new GrallocHalWrapper<IAllocator4, IMapper4>(allocator4, mapper4));
-    } else {
-        ALOGD("Graphics HALs 4.0 not found (allocator %d mapper %d), falling back to 3.0",
-              (allocator4 != nullptr), (mapper4 != nullptr));
-
-        sp<IAllocator3> allocator3 = IAllocator3::getService();
-        sp<IMapper3> mapper3 = IMapper3::getService();
-
-        if (allocator3 != nullptr && mapper3 != nullptr) {
-            mGrallocHal = std::unique_ptr<IGrallocHalWrapper>(
-                    new GrallocHalWrapper<IAllocator3, IMapper3>(allocator3, mapper3));
-        } else {
-            ALOGD("Graphics HALs 3.0 not found (allocator %d mapper %d), falling back to 2.x",
-                  (allocator3 != nullptr), (mapper3 != nullptr));
-
-            sp<IAllocator2> allocator2 = IAllocator2::getService();
-            sp<IMapper2> mapper2 = IMapper2_1::getService();
-            if (mapper2 == nullptr) {
-                mapper2 = IMapper2::getService();
-            }
-
-            if (allocator2 != nullptr && mapper2 != nullptr) {
-                mGrallocHal = std::unique_ptr<IGrallocHalWrapper>(
-                        new GrallocHalWrapper<IAllocator2, IMapper2>(allocator2, mapper2));
-            } else {
-                ALOGE("Couldn't open graphics HALs (2.x allocator %d mapper %d)",
-                      (allocator2 != nullptr), (mapper2 != nullptr));
-            }
-        }
-    }
-}
-
-GrallocWrapper::~GrallocWrapper() {
-    for (auto bufferHandle : mAllocatedBuffers) {
-        mGrallocHal->unlock(bufferHandle);
-        mGrallocHal->freeBuffer(bufferHandle);
-    }
-    mAllocatedBuffers.clear();
-}
-
-std::pair<native_handle_t*, void*> GrallocWrapper::allocate(uint32_t size) {
-    native_handle_t* bufferHandle = mGrallocHal->allocate(size);
-    void* buffer = nullptr;
-    if (bufferHandle) {
-        buffer = mGrallocHal->lock(bufferHandle);
-        if (buffer) {
-            mAllocatedBuffers.insert(bufferHandle);
-        } else {
-            mGrallocHal->freeBuffer(bufferHandle);
-            bufferHandle = nullptr;
-        }
-    }
-    return std::make_pair<>(bufferHandle, buffer);
-}
-
-void GrallocWrapper::freeBuffer(native_handle_t* bufferHandle) {
-    if (mAllocatedBuffers.erase(bufferHandle)) {
-        mGrallocHal->unlock(bufferHandle);
-        mGrallocHal->freeBuffer(bufferHandle);
-    }
-}
-
-}  // namespace android
diff --git a/sensors/common/vts/utils/include/sensors-vts-utils/GrallocWrapper.h b/sensors/common/vts/utils/include/sensors-vts-utils/GrallocWrapper.h
deleted file mode 100644
index ebbcb2c..0000000
--- a/sensors/common/vts/utils/include/sensors-vts-utils/GrallocWrapper.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <utils/NativeHandle.h>
-
-#include <memory>
-#include <string>
-#include <unordered_set>
-#include <utility>
-
-namespace android {
-
-class IGrallocHalWrapper;
-
-// Reference: hardware/interfaces/graphics/mapper/2.0/vts/functional/
-class GrallocWrapper {
-   public:
-    GrallocWrapper();
-    ~GrallocWrapper();
-
-    // After constructing this object, this function must be called to check the result. If it
-    // returns false, other methods are not safe to call.
-    bool isInitialized() const { return (mGrallocHal != nullptr); };
-
-    // Allocates a gralloc buffer suitable for direct channel sensors usage with the given size.
-    // The buffer should be freed using freeBuffer when it's not needed anymore; otherwise it'll
-    // be freed when this object is destroyed.
-    // Returns a handle to the buffer, and a CPU-accessible pointer for reading. On failure, both
-    // will be set to nullptr.
-    std::pair<native_handle_t*, void*> allocate(uint32_t size);
-
-    // Releases a gralloc buffer previously returned by allocate()
-    void freeBuffer(native_handle_t* bufferHandle);
-
-  private:
-    std::unique_ptr<IGrallocHalWrapper> mGrallocHal;
-
-    // Keep track of all cloned and imported handles.  When a test fails with
-    // ASSERT_*, the destructor will free the handles for the test.
-    std::unordered_set<native_handle_t*> mAllocatedBuffers;
-};
-
-}  // namespace android
diff --git a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsTestSharedMemory.h b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsTestSharedMemory.h
index 39084a4..b96adb3 100644
--- a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsTestSharedMemory.h
+++ b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsTestSharedMemory.h
@@ -17,7 +17,11 @@
 #ifndef ANDROID_SENSORS_TEST_SHARED_MEMORY_H
 #define ANDROID_SENSORS_TEST_SHARED_MEMORY_H
 
-#include "GrallocWrapper.h"
+#include <aidl/android/hardware/graphics/common/BufferUsage.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/GraphicBufferMapper.h>
 
 #include <android-base/macros.h>
 #include <log/log.h>
@@ -28,6 +32,8 @@
 #include <cutils/ashmem.h>
 
 using namespace ::android::hardware::sensors::V1_0;
+using ::aidl::android::hardware::graphics::common::BufferUsage;
+using ::aidl::android::hardware::graphics::common::PixelFormat;
 
 template <class SensorTypeVersion, class EventType>
 class SensorsTestSharedMemory {
@@ -48,11 +54,20 @@
     }
 
     SharedMemInfo getSharedMemInfo() const {
-        SharedMemInfo mem = {.type = mType,
-                             .format = SharedMemFormat::SENSORS_EVENT,
-                             .size = static_cast<uint32_t>(mSize),
-                             .memoryHandle = mNativeHandle};
-        return mem;
+        if (mType == SharedMemType::GRALLOC) {
+            SharedMemInfo mem = {.type = mType,
+                                 .format = SharedMemFormat::SENSORS_EVENT,
+                                 .size = static_cast<uint32_t>(mSize),
+                                 .memoryHandle = mBufferHandle};
+            return mem;
+
+        } else {
+            SharedMemInfo mem = {.type = mType,
+                                 .format = SharedMemFormat::SENSORS_EVENT,
+                                 .size = static_cast<uint32_t>(mSize),
+                                 .memoryHandle = mNativeHandle};
+            return mem;
+        }
     }
     char* getBuffer() const { return mBuffer; }
     size_t getSize() const { return mSize; }
@@ -128,17 +143,26 @@
             }
             case SharedMemType::GRALLOC: {
                 if (mSize != 0) {
-                    mGrallocWrapper->freeBuffer(mNativeHandle);
-                    mNativeHandle = nullptr;
+                    android::status_t status =
+                            android::GraphicBufferAllocator::get().free(mBufferHandle);
+                    if (status != android::OK) {
+                        ALOGE("SensorsAidlTestSharedMemory Gralloc failed to free buffer. Status: "
+                              "%s",
+                              android::statusToString(status).c_str());
+                    }
+                    mBufferHandle = nullptr;
+                    mBuffer = nullptr;
                     mSize = 0;
                 }
                 break;
             }
             default: {
-                if (mNativeHandle != nullptr || mSize != 0 || mBuffer != nullptr) {
-                    ALOGE("SensorsTestSharedMemory %p not properly destructed: "
-                          "type %d, native handle %p, size %zu, buffer %p",
-                          this, static_cast<int>(mType), mNativeHandle, mSize, mBuffer);
+                if (mNativeHandle != nullptr || mSize != 0 || mBuffer != nullptr ||
+                    mBufferHandle != nullptr) {
+                    ALOGE("SensorsAidlTestSharedMemory %p not properly destructed: "
+                          "type %d, native handle %p, size %zu, buffer %p, buffer handle %p",
+                          this, static_cast<int>(mType), mNativeHandle, mSize, mBuffer,
+                          mBufferHandle);
                 }
                 break;
             }
@@ -171,14 +195,33 @@
                 break;
             }
             case SharedMemType::GRALLOC: {
-                mGrallocWrapper = std::make_unique<::android::GrallocWrapper>();
-                if (!mGrallocWrapper->isInitialized()) {
+                static constexpr uint64_t kBufferUsage =
+                        static_cast<uint64_t>(BufferUsage::SENSOR_DIRECT_DATA) |
+                        static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN) |
+                        static_cast<uint64_t>(BufferUsage::CPU_WRITE_RARELY);
+
+                uint32_t stride = 0;
+                buffer_handle_t bufferHandle;
+                android::status_t status = android::GraphicBufferAllocator::get().allocate(
+                        size, 1, static_cast<int>(PixelFormat::BLOB), 1, kBufferUsage,
+                        &bufferHandle, &stride, "SensorVts");
+                if (status != android::OK) {
+                    ALOGE("SensorsAidlTestSharedMemory failed to allocate memory. Status: %s",
+                          android::statusToString(status).c_str());
                     break;
                 }
-
-                std::pair<native_handle_t*, void*> buf = mGrallocWrapper->allocate(size);
-                handle = buf.first;
-                buffer = static_cast<char*>(buf.second);
+                // Per the HAL, all-zeros Rect means the entire buffer
+                android::Rect rect = {0, 0, 0, 0};
+                void* ret;
+                status = android::GraphicBufferMapper::get().lock(bufferHandle, kBufferUsage, rect,
+                                                                  &ret);
+                if (status != android::OK) {
+                    ALOGE("SensorsAidlTestSharedMemory failed to import buffer: Status: %s",
+                          android::statusToString(status).c_str());
+                } else {
+                    buffer = static_cast<char*>(ret);
+                    mBufferHandle = bufferHandle;
+                }
                 break;
             }
             default:
@@ -194,9 +237,9 @@
 
     SharedMemType mType;
     native_handle_t* mNativeHandle;
+    buffer_handle_t mBufferHandle;
     size_t mSize;
     char* mBuffer;
-    std::unique_ptr<::android::GrallocWrapper> mGrallocWrapper;
 
     DISALLOW_COPY_AND_ASSIGN(SensorsTestSharedMemory);
 };
diff --git a/soundtrigger/aidl/Android.bp b/soundtrigger/aidl/Android.bp
index 27d43d3..aa400c1 100644
--- a/soundtrigger/aidl/Android.bp
+++ b/soundtrigger/aidl/Android.bp
@@ -22,8 +22,8 @@
         "android/hardware/soundtrigger3/ISoundTriggerHwGlobalCallback.aidl",
     ],
     stability: "vintf",
-    imports: [
-        "android.media.soundtrigger.types-V1",
+    defaults: [
+        "latest_android_media_soundtrigger_types_import_interface",
     ],
     backend: {
         cpp: {
@@ -34,6 +34,7 @@
             sdk_version: "module_current",
         },
     },
+    frozen: false,
     versions_with_info: [
         {
             version: "1",
@@ -45,7 +46,7 @@
 }
 
 // Note: This should always be one version ahead of the last frozen version
-latest_android_hardware_soundtrigger3 = "android.hardware.soundtrigger3-V1"
+latest_android_hardware_soundtrigger3 = "android.hardware.soundtrigger3-V2"
 
 // Modules that depend on android.hardware.soundtrigger3 directly can include
 // the following java_defaults to avoid explicitly managing dependency versions
@@ -56,3 +57,10 @@
         latest_android_hardware_soundtrigger3 + "-java",
     ],
 }
+
+cc_defaults {
+    name: "latest_android_hardware_soundtrigger3_ndk_shared",
+    shared_libs: [
+        latest_android_hardware_soundtrigger3 + "-ndk",
+    ],
+}
diff --git a/tests/multithread/1.0/default/Multithread.h b/tests/multithread/1.0/default/Multithread.h
index 0d4a007..7df75cc 100644
--- a/tests/multithread/1.0/default/Multithread.h
+++ b/tests/multithread/1.0/default/Multithread.h
@@ -33,7 +33,7 @@
     std::condition_variable mCv;
     std::mutex mCvMutex;
 
-    static constexpr auto kTimeoutDuration = 100ms;
+    static constexpr auto kTimeoutDuration = 1000ms;
 };
 
 extern "C" IMultithread* HIDL_FETCH_IMultithread(const char* name);
diff --git a/thermal/2.0/default/Android.bp b/thermal/2.0/default/Android.bp
index f743ade..83afa97 100644
--- a/thermal/2.0/default/Android.bp
+++ b/thermal/2.0/default/Android.bp
@@ -22,23 +22,13 @@
     default_applicable_licenses: ["hardware_interfaces_license"],
 }
 
-filegroup {
-    name: "android.hardware.thermal@2.0-service.xml",
-    srcs: ["android.hardware.thermal@2.0-service.xml"],
-}
-
-filegroup {
-    name: "android.hardware.thermal@2.0-service.rc",
-    srcs: ["android.hardware.thermal@2.0-service.rc"],
-}
-
 cc_binary {
     name: "android.hardware.thermal@2.0-service.mock",
     defaults: ["hidl_defaults"],
     relative_install_path: "hw",
     vendor: true,
-    init_rc: [":android.hardware.thermal@2.0-service.rc"],
-    vintf_fragments: [":android.hardware.thermal@2.0-service.xml"],
+    init_rc: ["android.hardware.thermal@2.0-service.rc"],
+    vintf_fragments: ["android.hardware.thermal@2.0-service.xml"],
     srcs: [
         "Thermal.cpp",
         "service.cpp",
diff --git a/thermal/2.0/default/apex/Android.bp b/thermal/2.0/default/apex/Android.bp
deleted file mode 100644
index 914a3a8..0000000
--- a/thermal/2.0/default/apex/Android.bp
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (C) 2022 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
-    default_applicable_licenses: ["hardware_interfaces_license"],
-}
-
-apex_key {
-    name: "com.android.hardware.thermal.key",
-    public_key: "com.android.hardware.thermal.avbpubkey",
-    private_key: "com.android.hardware.thermal.pem",
-}
-
-android_app_certificate {
-    name: "com.android.hardware.thermal.certificate",
-    certificate: "com.android.hardware.thermal",
-}
-
-genrule {
-    name: "com.android.hardware.thermal.rc-gen",
-    srcs: [":android.hardware.thermal@2.0-service.rc"],
-    out: ["com.android.hardware.thermal.rc"],
-    cmd: "sed -E 's/\\/vendor/\\/apex\\/com.android.hardware.thermal.mock/' $(in) > $(out)",
-}
-
-prebuilt_etc {
-    name: "com.android.hardware.thermal.rc",
-    src: ":com.android.hardware.thermal.rc-gen",
-    installable: false,
-}
-
-apex {
-    name: "com.android.hardware.thermal.mock",
-    manifest: "manifest.json",
-    file_contexts: "file_contexts",
-    key: "com.android.hardware.thermal.key",
-    certificate: ":com.android.hardware.thermal.certificate",
-    use_vndk_as_stable: true,
-    updatable: false,
-    soc_specific: true,
-    binaries: ["android.hardware.thermal@2.0-service.mock"],
-    prebuilts: ["com.android.hardware.thermal.rc"],
-    vintf_fragments: [":android.hardware.thermal@2.0-service.xml"],
-}
diff --git a/thermal/2.0/default/apex/com.android.hardware.thermal.avbpubkey b/thermal/2.0/default/apex/com.android.hardware.thermal.avbpubkey
deleted file mode 100644
index 8f7cf72..0000000
--- a/thermal/2.0/default/apex/com.android.hardware.thermal.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/thermal/2.0/default/apex/com.android.hardware.thermal.pem b/thermal/2.0/default/apex/com.android.hardware.thermal.pem
deleted file mode 100644
index 4ea6e85..0000000
--- a/thermal/2.0/default/apex/com.android.hardware.thermal.pem
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJKQIBAAKCAgEApXL2prEKqnU/xHlYeZB55x5EOrVAsfBuyHN5xOgUY877UuDC
-xoX+DOYAkCDSwMPmBj/Q9E70lId1PXMeI3y0obgYpdFubqRsoGNcEq2zG811tr6J
-BAE/7mAI0a4VVFWtauBNTiP5G31hsRpZNTE/dyZxs20GEVQqdvtBlEAuebCt7VIb
-fCwXnPCXSiYQ9WyjU0dwHoZPuy02qBuq3v9NYt5J6lYiPDqTWmpYBFm+SIZvth/j
-HUcaU3p+BlUad0qLhA8HYTPPBwJq2uHbTXeFrWXngt+UPCdzYubyUjG4WmgHIfee
-zSvoTjud2Gaofnd4/PcE5jFTMYUJuz0D4E+8StZArw+xegfxGcvqFZ4gfKAO1Vha
-eQBhw94P2eieUfh9metQEyKZ+A24sxG0awtAUOgcwr/WNv2TgJoN8+56DhuPXOuO
-U2qeIZuIrmE5xmyzrzJsCx+5vRQA7A3kpxRo1ZCgbJlzMgdAQaRTe2rqxyyoeFQR
-HbxUdsM7ZwDLE62UlaTTZFoJ1wzvZmsIJdkdRg97zcc3R3kYnVehCxAZ1KdAKzq6
-bhrw/kNAVA0zUHhlhHWGDnJQ/rkGfjQ9GGhS51IJYgKEoQc5M9hSgZD3idic8hso
-63r/P2Wn4S78gwLigfpzsGD7RrusTbMIRkebwpHwAYpB8qIykrtUKfunLtMCAwEA
-AQKCAgEAjiKbv0ytaw9bfwD4f0cdUu5vkzgPok55/f8mh4ERs0UoKGUrL74BKTeX
-GDr6k9w4CvpcGuaRu+A7WlVBeR8zVxN/KUUo6CidoZR6jxlmm+YA0MQTlbs1Hyal
-rO0vKcqJNx4Hi6/f3Dv0519JcCck7Mm8OHbbFZwG9zyXdDNHOggNA6rcLer7Rjpy
-3qKhQxbXoT3oFnEwog8Pu5A5VWZjJyLswULKGo//81cU0nf+vvOvmPj/9jEVbs32
-4p3OJNmHziXTIzCNFOqAvhX2fzDFSNgY8hf9k0gZGshpOS+5vwFLz2SZqo2j/0G8
-MyLOcgdVi4zzSobpf8tZNuAOKnCVwxteV3Ddl0o+qAf4cQCAXtuEkJvFWpAxrX4m
-J7nDDuvcTjKMlnchlm6XxmP27YNTKA2w5uNfrJQV5LR3IDs77PFpNiKOwERka6MU
-WE1LnjAFTCDxujT29NfIjC9z0iOY45TYut4okesaK7woe6pfK2H0ouvE6mZ+Lb7Y
-qZphPb4e4DZZZAVoELVvWflm/VC/xmBMA9vtzpjuvRXD+iYjgxbHaiG2UANLWmTd
-vADLqBadlbp10AvyrjKxHUhAiZnJgaVKRXtw5qk8YrwZOwypEHCQjY41fJuRScWF
-pD58/PYOLtSdwewzTijXSVwwPeqL1JMdJ+KWFk5zI410DtmHwoECggEBANM5dHEj
-L4ZOCcgHVG7aCLah7f/0Za7BBiPsZFD0uRIzWFTd77st3v8ulMw1JQiO3+xmpL7e
-iOP+onuSEMKgNQqeX9WCnv6pL+mjg0of2IEcGnvCpmfUZRA2x/MlyJRrQ1vHGqkV
-oBIIWgCGmIAWuFbHPmkNYx7mAJymAfzShkVtMw29oiGYyubqx9cTjD0odeyScbhZ
-LuMt72O5QeNsPREX4SsSRAL+vZbz1+8iRI8Fon89csZym3hos3DA4QSml+FNHnLe
-YFD6AfU7pdn79YhhNkn4jE0BxLaEkXhMs8RRTU2Q3o990ZxrAZGQz4zqOm5sOIQT
-JaXFXxGvOwEysrMCggEBAMiFaTzkhJSLEmLEu0T3gNEC2d3jE9kt4Olv4mQmzD00
-AzDtkrkArLQCeG3CMn55oFcJRwr8gAEk7/f2mvJSoZQnw0J0tRI+QiHJG4zwdsQC
-UszqN89X8ef0yoobwVa31ZoWxK4/NvLO4GZQ6UUd9uni10jfYVWmwEwKBU61ihvT
-ntcZIXvROR6khxzLeXtglnsznnYcdYMPJUeN+cfK9/rL3V3jk1jes8y8XwYfxkUa
-99nEe1NkRCUznzKxvkRwnKunlMCEkZ5z4nNMlqEqiOlVEYgwKwNCP+qM7ZKUJg7t
-uOL91bqWFPj2qdzyUh0uP5V/dg2odnu0xSblKWhxI2ECggEBAKbZttJ8Idlsoath
-pt+d2c4yoadTLlNZ5HjSDfgpKFxpNLhtTCbGuGVJLX8V5/gXrGi4OCER9n5rMXx9
-SEIFfYCy1C77bI7rpI5hfJ88ArESOxVSEFLqYx7otw+p5AThqibAY533GCfGcxoB
-OEvOJrVd1D31tju9IfSb6ewFfM0w0mhjSMRTRswb39pUda4F3QkQMUaXJEOOkJBs
-0dBNOvvaqiJ03kajZa3tVsBuiEuV/uOV7ak29Pqrcjt6EQW0dzsgyRGh+eFda9iE
-0qEbt7uQVusdq+5UnEg09hhaNpK4SmEgM76Te9WcbXPIOTst9xQs5oPmABIvk8aL
-bgenPaMCggEAX9EAHIzFnYVm37NKGQZ7k2RdXt2nGlwF4QYJk/nGFmjILZUYSza7
-T7jueuQU5MKRj4VrYSCOuf1AfahlGe3KL9VgRF0oOPNu/l3uwEYXOkox7qDs0jMf
-8MrUDXJ9zEZD10GR8gFa7GNWbw2yqchLuC8g2D2FcTwhHzSanKW6vNk+SWJE0bmE
-JdRQi73e6smYnn5n9eBbdqjCE5MQDBw8qqbHvJmGSyz/lZFdhrugLl1YmcJ9e7ep
-qG0mYT71wBZfhtapCeVO//w39Qhf4dtFWNnBauY5Z3E8wYNd8nDATtnhQvYwLtyQ
-YPbc7CsOecsjrvgdHSGmnC4hFxjh1HpbgQKCAQBzEr35CFQpUfGsu06SqRrxp7mb
-BfQzLmqnt+ZLu21H/YjVbamMXNjbJFSdtFLxAK/s9vFGQzEWWEYia2pRZejlCXhQ
-RO+TREJ4rm4Erfdh+zS9z8MZlxe2ybIMs260XxjZ0gEltRtJ6P14zLBlChi/rQEY
-tGrOpfjvLc1Lryaucwj7snDLtMfyqvemAUmuJNn/6IEMuG4DcdtJ3Whh7t3xKfO0
-Hc71Oh7F30okQQ6Ctiwp1T77Y1EXlRJrUtpf1sLxeItP6/r3WXLNaiFdKqGJxiCM
-Gw11oUcBA9bvfAzeJIpVVbLtGPNOp4J1XpkgFcWzsTM+ZpxWwbj4yJL6rSN2
------END RSA PRIVATE KEY-----
diff --git a/thermal/2.0/default/apex/com.android.hardware.thermal.pk8 b/thermal/2.0/default/apex/com.android.hardware.thermal.pk8
deleted file mode 100644
index 3e5bf69..0000000
--- a/thermal/2.0/default/apex/com.android.hardware.thermal.pk8
+++ /dev/null
Binary files differ
diff --git a/thermal/2.0/default/apex/com.android.hardware.thermal.x509.pem b/thermal/2.0/default/apex/com.android.hardware.thermal.x509.pem
deleted file mode 100644
index 048e69a..0000000
--- a/thermal/2.0/default/apex/com.android.hardware.thermal.x509.pem
+++ /dev/null
@@ -1,34 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIF3TCCA8UCFFWeg2KJX/fyAqZPcKaFS61/a3GiMA0GCSqGSIb3DQEBCwUAMIGp
-MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
-bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEi
-MCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTElMCMGA1UEAwwcY29t
-LmFuZHJvaWQuaGFyZHdhcmUudGhlcm1hbDAgFw0yMTExMTcxODE3MjRaGA80NzU5
-MTAxNDE4MTcyNFowgakxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh
-MRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYD
-VQQLDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29t
-MSUwIwYDVQQDDBxjb20uYW5kcm9pZC5oYXJkd2FyZS50aGVybWFsMIICIjANBgkq
-hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2W8QhCKX0WoS5jhIWbWBoKulD6XhKASA
-CL0oU6Uci0U6id2m++ou/T0aHSlliS9kT1NEABNwbLeTDa9h1qg1lbajRzXbvzEz
-EvZYT+dlgYFNZ9zaVCIlMapoiN+nrM/Rs24UBjJrZu1B39+IcE5ciQz69PgrLKWF
-vFEdYzxWI246azOVr3fvdelQg+CyPgiGObVAGOHhAidvsjg4FssKnADzn+JNlTt6
-b9P65xUQErut8hz9YdLtfZ096iwe8eEpsMOQwCNdKNXVCcNjQWkOwRaU+0awHoH1
-4mArvF7yyvJxqzkNaR03BPqXAHPIAPu6xdfA19+HVIiz6bLEZ1mfCM6edXByCWZu
-K8o54OZph71r15ncv4DVd+cnBjNvBvfdlJSeGjSreFkUo5NkfAwqdmLfZx6CedHI
-sLx7X8vPYolQnR4UEvaX6yeNN0gs8Dd6ePMqOEiwWFVASD+cbpcUrp09uzlDStGV
-1DPx/9J2kw8eXPMqOSGThkLWuUMUFojyh7bNlL16oYmEeZW6/OOXCOXzAQgJ7NIs
-DA1/H2w1HMHWgSfF4y+Es2CiqcgVFOIU/07b31Edw4v56pjx1CUpRpJZjDA1JJNo
-A4YCnpA6JWTxzUTmv21fEYyY+poA3CuzvCfZ80z9h/UFW98oM9GawGvK0i2pLudS
-RaZXWeil08cCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAj2gpn/IpAHQLDlI52WwL
-IDc5uzlf1BGseLZ8M8uuMiwvN00nOjunQQZEIbhH7+03GYyMzRhTTI3nWwmX4Fnq
-vC5sR68yNlu9gOAMXaAHo/Rw73ak7sv8xGbb2FeQsHaMKDQ2nqxP17SWdQ0xWa1u
-5qNurPOdAPOw77noZcT7yYX7lcrOKxPIekPJxyeOlp4bQSmabSRgYr70B7GybPlv
-K1gkgCBxl5RiVjVxLo+7ESHSAaGLy0S7F2v6PJ9oj15TovWQ0py2iBKZ6HReB7s/
-umnQqkwXDLudlNmoXIsgIYn8vKFTYpy1GSNJqhRSLfvLR6NtuU0iCTvg/X7oPmS1
-dWUdjVeO7ZVvIpO3gLLEe4maBEYF2sOlt3bRmzIHydORJtkfTt5tiZ4wR9RfJbkj
-iBiDbNiX010bZPTAXmgMbuJzQXZEXCBy0qKeS1cHwf9M8FDoiLajXepfZIp+6syt
-D4lZk4eAV141JL5PfABfdZWwT3cTgFIqCYpqrMQJIu+GHEjUwD2yNPi0I1/ydFCW
-CPDnzWjYCi6yVB8hss3ZFbvhfyJzdm3LivNVbLGK0+TG0EOAz6d2aQ0UjESgMD1A
-U8hLzQt7MiFSG0bt2cVx6SgCHeYUqMntbFELEAURWrhAfPLMJtAvFgKbEiZEAkkW
-pdDVh603aIp4LjVCfTYp/mQ=
------END CERTIFICATE-----
diff --git a/thermal/2.0/default/apex/file_contexts b/thermal/2.0/default/apex/file_contexts
deleted file mode 100644
index e0d87c7..0000000
--- a/thermal/2.0/default/apex/file_contexts
+++ /dev/null
@@ -1,5 +0,0 @@
-(/.*)?                                                    u:object_r:vendor_file:s0
-# Permission XMLs
-/etc/permissions(/.*)?                                    u:object_r:vendor_configs_file:s0
-# binary
-/bin/hw/android\.hardware\.thermal@2\.0-service\.mock     u:object_r:hal_thermal_default_exec:s0
diff --git a/thermal/2.0/default/apex/manifest.json b/thermal/2.0/default/apex/manifest.json
deleted file mode 100644
index ee44dc1..0000000
--- a/thermal/2.0/default/apex/manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "name": "com.android.hardware.thermal.mock",
-  "version": 1
-}
diff --git a/threadnetwork/aidl/default/thread_chip.cpp b/threadnetwork/aidl/default/thread_chip.cpp
index 9358eba..ed34e63 100644
--- a/threadnetwork/aidl/default/thread_chip.cpp
+++ b/threadnetwork/aidl/default/thread_chip.cpp
@@ -32,23 +32,19 @@
 namespace threadnetwork {
 
 ThreadChip::ThreadChip(char* url) : mUrl(), mRxFrameBuffer(), mCallback(nullptr) {
-    static const char kHdlcProtocol[] = "spinel+hdlc";
-    static const char kSpiProtocol[] = "spinel+spi";
-    const char* protocol;
+    const char* interfaceName;
 
     CHECK_EQ(mUrl.Init(url), 0);
 
-    protocol = mUrl.GetProtocol();
-    CHECK_NE(protocol, nullptr);
+    interfaceName = mUrl.GetProtocol();
+    CHECK_NE(interfaceName, nullptr);
 
-    if (memcmp(protocol, kSpiProtocol, strlen(kSpiProtocol)) == 0) {
-        mSpinelInterface = std::make_shared<ot::Posix::SpiInterface>(handleReceivedFrameJump, this,
-                                                                     mRxFrameBuffer);
-    } else if (memcmp(protocol, kHdlcProtocol, strlen(kHdlcProtocol)) == 0) {
-        mSpinelInterface = std::make_shared<ot::Posix::HdlcInterface>(handleReceivedFrameJump, this,
-                                                                      mRxFrameBuffer);
+    if (ot::Posix::SpiInterface::IsInterfaceNameMatch(interfaceName)) {
+        mSpinelInterface = std::make_shared<ot::Posix::SpiInterface>(mUrl);
+    } else if (ot::Posix::HdlcInterface::IsInterfaceNameMatch(interfaceName)) {
+        mSpinelInterface = std::make_shared<ot::Posix::HdlcInterface>(mUrl);
     } else {
-        ALOGE("The protocol \"%s\" is not supported", protocol);
+        ALOGE("The interface \"%s\" is not supported", interfaceName);
         exit(EXIT_FAILURE);
     }
 
@@ -106,7 +102,8 @@
     if (in_callback == nullptr) {
         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
     } else if (mCallback == nullptr) {
-        if (mSpinelInterface->Init(mUrl) != OT_ERROR_NONE) {
+        if (mSpinelInterface->Init(handleReceivedFrameJump, this, mRxFrameBuffer) !=
+            OT_ERROR_NONE) {
             return errorStatus(ERROR_FAILED, "Failed to initialize the interface");
         }
 
diff --git a/threadnetwork/aidl/default/thread_chip.hpp b/threadnetwork/aidl/default/thread_chip.hpp
index 680580a..30046ef 100644
--- a/threadnetwork/aidl/default/thread_chip.hpp
+++ b/threadnetwork/aidl/default/thread_chip.hpp
@@ -20,6 +20,7 @@
 #include <aidl/android/hardware/threadnetwork/IThreadChipCallback.h>
 
 #include "lib/spinel/spinel_interface.hpp"
+#include "lib/url/url.hpp"
 #include "mainloop.hpp"
 
 #include <android/binder_auto_utils.h>
diff --git a/tv/OWNERS b/tv/OWNERS
index ee7f272..e051b28 100644
--- a/tv/OWNERS
+++ b/tv/OWNERS
@@ -9,4 +9,5 @@
 quxiangfang@google.com
 shubang@google.com
 yixiaoluo@google.com
+qingxun@google.com
 
diff --git a/tv/input/OWNERS b/tv/input/OWNERS
index e69de29..1252e4e 100644
--- a/tv/input/OWNERS
+++ b/tv/input/OWNERS
@@ -0,0 +1 @@
+# Bug component: 826094
diff --git a/tv/input/aidl/Android.bp b/tv/input/aidl/Android.bp
index 35f510a..cd69130 100644
--- a/tv/input/aidl/Android.bp
+++ b/tv/input/aidl/Android.bp
@@ -35,6 +35,5 @@
             ],
         },
     ],
-    frozen: true,
-
+    frozen: false,
 }
diff --git a/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/TvMessageEvent.aidl b/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/TvMessageEvent.aidl
index 94fe665..3c1cb74 100644
--- a/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/TvMessageEvent.aidl
+++ b/tv/input/aidl/aidl_api/android.hardware.tv.input/current/android/hardware/tv/input/TvMessageEvent.aidl
@@ -37,4 +37,5 @@
   android.hardware.tv.input.TvMessageEventType type;
   int streamId;
   android.hardware.tv.input.TvMessage[] messages;
+  int deviceId;
 }
diff --git a/tv/input/aidl/android/hardware/tv/input/ITvInputCallback.aidl b/tv/input/aidl/android/hardware/tv/input/ITvInputCallback.aidl
index a3afd41..4121fc7 100644
--- a/tv/input/aidl/android/hardware/tv/input/ITvInputCallback.aidl
+++ b/tv/input/aidl/android/hardware/tv/input/ITvInputCallback.aidl
@@ -32,17 +32,20 @@
      * Notifies the client that an TV message event has occurred. For possible event types,
      * check {@link android.hardware.tv.input.TvMessageEventType}.
      *
-     * The first message in a list of messages contained in a
+     * <p> For implementations of version 1, The first message in a list of messages contained in a
      * {@link android.hardware.tv.input.TvMessageEvent} should always have a
      * {@link android.hardware.tv.input.TvMessage#subType} of "device_id",
      * otherwise the event is discarded. When the subType of a message is "device_id", the ID of
      * the device that sent the message should be contained in
-     * {@link android.hardware.tv.input.TvMessage#groupId}
+     * {@link android.hardware.tv.input.TvMessage#groupId}.
      *
-     * Invoking this callback for the first time immediately triggers
+     * <p> For version 2 and beyond, the device ID should be contained in
+     * {@link android.hardware.tv.input.TvMessageEvent#deviceId}.
+     *
+     * <p> Invoking this callback for the first time immediately triggers
      * {@link android.hardware.tv.input.ITvInput#getTvMessageQueueDesc}. It is
-     * expected for the queue to be ready with
-     * the relevant messages for the event before this callback is called.
+     * expected for the queue to be ready with the relevant messages for the event before this
+     * callback is called.
      *
      * @param event Event passed to the client.
      */
diff --git a/tv/input/aidl/android/hardware/tv/input/TvMessageEvent.aidl b/tv/input/aidl/android/hardware/tv/input/TvMessageEvent.aidl
index 74a078a..e04a725 100644
--- a/tv/input/aidl/android/hardware/tv/input/TvMessageEvent.aidl
+++ b/tv/input/aidl/android/hardware/tv/input/TvMessageEvent.aidl
@@ -25,4 +25,5 @@
 
     int streamId;
     TvMessage[] messages;
+    int deviceId;
 }
diff --git a/tv/input/aidl/default/TvInput.cpp b/tv/input/aidl/default/TvInput.cpp
index 2ee8bcf..f6a64c4 100644
--- a/tv/input/aidl/default/TvInput.cpp
+++ b/tv/input/aidl/default/TvInput.cpp
@@ -43,6 +43,9 @@
                                       new TvStreamConfigWrapper(11, 360, 480, false))}};
     mStreamConfigs[3] = {{5, shared_ptr<TvStreamConfigWrapper>(
                                      new TvStreamConfigWrapper(5, 1080, 1920, false))}};
+
+    mQueue = shared_ptr<AidlMessageQueue<int8_t, SynchronizedReadWrite>>(
+            new (std::nothrow) AidlMessageQueue<int8_t, SynchronizedReadWrite>(8));
 }
 
 ::ndk::ScopedAStatus TvInput::setCallback(const shared_ptr<ITvInputCallback>& in_callback) {
@@ -74,7 +77,9 @@
         return ::ndk::ScopedAStatus::fromServiceSpecificError(STATUS_INVALID_ARGUMENTS);
     }
 
+    // When calling notifyTvMessage, make sure to verify against this map.
     mTvMessageEventEnabled[deviceId][streamId][in_type] = enabled;
+
     return ::ndk::ScopedAStatus::ok();
 }
 
@@ -82,11 +87,17 @@
         MQDescriptor<int8_t, SynchronizedReadWrite>* out_queue, int32_t in_deviceId,
         int32_t in_streamId) {
     ALOGV("%s", __FUNCTION__);
+    ::ndk::ScopedAStatus status = ::ndk::ScopedAStatus::ok();
     if (mStreamConfigs.count(in_deviceId) == 0) {
         ALOGW("Device with id %d isn't available", in_deviceId);
-        return ::ndk::ScopedAStatus::fromServiceSpecificError(STATUS_INVALID_ARGUMENTS);
+        status = ::ndk::ScopedAStatus::fromServiceSpecificError(STATUS_INVALID_ARGUMENTS);
+    } else if (!mQueue->isValid()) {
+        ALOGE("Tv Message Queue was not properly initialized");
+        status = ::ndk::ScopedAStatus::fromServiceSpecificError(STATUS_INVALID_STATE);
+    } else {
+        *out_queue = mQueue->dupeDesc();
     }
-    return ::ndk::ScopedAStatus::ok();
+    return status;
 }
 
 ::ndk::ScopedAStatus TvInput::getStreamConfigurations(int32_t in_deviceId,
diff --git a/tv/input/aidl/default/TvInput.h b/tv/input/aidl/default/TvInput.h
index 5776961..595f017 100644
--- a/tv/input/aidl/default/TvInput.h
+++ b/tv/input/aidl/default/TvInput.h
@@ -66,6 +66,7 @@
     map<int32_t, shared_ptr<TvInputDeviceInfoWrapper>> mDeviceInfos;
     map<int32_t, map<int32_t, shared_ptr<TvStreamConfigWrapper>>> mStreamConfigs;
     TvMessageEnabledMap mTvMessageEventEnabled;
+    shared_ptr<AidlMessageQueue<int8_t, SynchronizedReadWrite>> mQueue;
 };
 
 }  // namespace input
diff --git a/tv/input/aidl/vts/functional/Android.bp b/tv/input/aidl/vts/functional/Android.bp
index 22487ea..930c5a8 100644
--- a/tv/input/aidl/vts/functional/Android.bp
+++ b/tv/input/aidl/vts/functional/Android.bp
@@ -32,7 +32,7 @@
         "libvndksupport",
         "libfmq",
         "android.hardware.common.fmq-V1-ndk",
-        "android.hardware.tv.input-V1-ndk",
+        "android.hardware.tv.input-V2-ndk",
     ],
     require_root: true,
 }
diff --git a/tv/input/aidl/vts/functional/VtsHalTvInputTargetTest.cpp b/tv/input/aidl/vts/functional/VtsHalTvInputTargetTest.cpp
index 8d3395b..746ae1e 100644
--- a/tv/input/aidl/vts/functional/VtsHalTvInputTargetTest.cpp
+++ b/tv/input/aidl/vts/functional/VtsHalTvInputTargetTest.cpp
@@ -355,8 +355,12 @@
     }
     int32_t stream_id = streamConfigs[0].streamId;
     ALOGD("GetTvMessageQueueTest: device_id=%d, stream_id=%d", device_id, stream_id);
-    MQDescriptor<int8_t, SynchronizedReadWrite> queue;
-    tv_input_->getTvMessageQueueDesc(&queue, device_id, stream_id);
+    MQDescriptor<int8_t, SynchronizedReadWrite> queueDescriptor;
+    AidlMessageQueue<int8_t, SynchronizedReadWrite>* queue;
+    tv_input_->getTvMessageQueueDesc(&queueDescriptor, device_id, stream_id);
+    queue = new (std::nothrow) AidlMessageQueue<int8_t, SynchronizedReadWrite>(queueDescriptor);
+    ASSERT_TRUE(queue->isValid());
+    delete queue;
 }
 
 INSTANTIATE_TEST_SUITE_P(PerInstance, TvInputAidlTest,
diff --git a/tv/tuner/aidl/default/Android.bp b/tv/tuner/aidl/default/Android.bp
index 65fa821..ed97d9c 100644
--- a/tv/tuner/aidl/default/Android.bp
+++ b/tv/tuner/aidl/default/Android.bp
@@ -23,6 +23,7 @@
         "TimeFilter.cpp",
         "Tuner.cpp",
         "service.cpp",
+        "dtv_plugin.cpp",
     ],
     static_libs: [
         "libaidlcommonsupport",
diff --git a/tv/tuner/aidl/default/Demux.cpp b/tv/tuner/aidl/default/Demux.cpp
index 11e7131..de94467 100644
--- a/tv/tuner/aidl/default/Demux.cpp
+++ b/tv/tuner/aidl/default/Demux.cpp
@@ -20,7 +20,9 @@
 #include <aidl/android/hardware/tv/tuner/DemuxQueueNotifyBits.h>
 #include <aidl/android/hardware/tv/tuner/Result.h>
 
+#include <fmq/AidlMessageQueue.h>
 #include <utils/Log.h>
+#include <thread>
 #include "Demux.h"
 
 namespace aidl {
@@ -29,6 +31,15 @@
 namespace tv {
 namespace tuner {
 
+using ::aidl::android::hardware::common::fmq::MQDescriptor;
+using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+using ::android::AidlMessageQueue;
+using ::android::hardware::EventFlag;
+
+using FilterMQ = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
+using AidlMQ = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
+using AidlMQDesc = MQDescriptor<int8_t, SynchronizedReadWrite>;
+
 #define WAIT_TIMEOUT 3000000000
 
 Demux::Demux(int32_t demuxId, uint32_t filterTypes) {
@@ -42,9 +53,128 @@
 
 Demux::~Demux() {
     ALOGV("%s", __FUNCTION__);
+    if (mDemuxIptvReadThread.joinable()) {
+        mDemuxIptvReadThread.join();
+    }
     close();
 }
 
+::ndk::ScopedAStatus Demux::openDvr(DvrType in_type, int32_t in_bufferSize,
+                                    const std::shared_ptr<IDvrCallback>& in_cb,
+                                    std::shared_ptr<IDvr>* _aidl_return) {
+    ALOGV("%s", __FUNCTION__);
+
+    if (in_cb == nullptr) {
+        ALOGW("[Demux] DVR callback can't be null");
+        *_aidl_return = nullptr;
+        return ::ndk::ScopedAStatus::fromServiceSpecificError(
+                static_cast<int32_t>(Result::INVALID_ARGUMENT));
+    }
+
+    set<int64_t>::iterator it;
+    switch (in_type) {
+        case DvrType::PLAYBACK:
+            mDvrPlayback = ndk::SharedRefBase::make<Dvr>(in_type, in_bufferSize, in_cb,
+                                                         this->ref<Demux>());
+            if (!mDvrPlayback->createDvrMQ()) {
+                ALOGE("[Demux] cannot create dvr message queue");
+                mDvrPlayback = nullptr;
+                *_aidl_return = mDvrPlayback;
+                return ::ndk::ScopedAStatus::fromServiceSpecificError(
+                        static_cast<int32_t>(Result::UNKNOWN_ERROR));
+            }
+
+            for (it = mPlaybackFilterIds.begin(); it != mPlaybackFilterIds.end(); it++) {
+                if (!mDvrPlayback->addPlaybackFilter(*it, mFilters[*it])) {
+                    ALOGE("[Demux] Can't get filter info for DVR playback");
+                    mDvrPlayback = nullptr;
+                    *_aidl_return = mDvrPlayback;
+                    return ::ndk::ScopedAStatus::fromServiceSpecificError(
+                            static_cast<int32_t>(Result::UNKNOWN_ERROR));
+                }
+            }
+
+            ALOGI("Playback normal case");
+
+            *_aidl_return = mDvrPlayback;
+            return ::ndk::ScopedAStatus::ok();
+        case DvrType::RECORD:
+            mDvrRecord = ndk::SharedRefBase::make<Dvr>(in_type, in_bufferSize, in_cb,
+                                                       this->ref<Demux>());
+            if (!mDvrRecord->createDvrMQ()) {
+                mDvrRecord = nullptr;
+                *_aidl_return = mDvrRecord;
+                return ::ndk::ScopedAStatus::fromServiceSpecificError(
+                        static_cast<int32_t>(Result::UNKNOWN_ERROR));
+            }
+
+            *_aidl_return = mDvrRecord;
+            return ::ndk::ScopedAStatus::ok();
+        default:
+            *_aidl_return = nullptr;
+            return ::ndk::ScopedAStatus::fromServiceSpecificError(
+                    static_cast<int32_t>(Result::INVALID_ARGUMENT));
+    }
+}
+
+void Demux::setIptvThreadRunning(bool isIptvThreadRunning) {
+    std::unique_lock<std::mutex> lock(mIsIptvThreadRunningMutex);
+    mIsIptvReadThreadRunning = isIptvThreadRunning;
+    mIsIptvThreadRunningCv.notify_all();
+}
+
+void Demux::readIptvThreadLoop(dtv_plugin* interface, dtv_streamer* streamer, size_t buf_size,
+                               int timeout_ms, int buffer_timeout) {
+    Timer *timer, *fullBufferTimer;
+    while (true) {
+        std::unique_lock<std::mutex> lock(mIsIptvThreadRunningMutex);
+        mIsIptvThreadRunningCv.wait(lock, [this] { return mIsIptvReadThreadRunning; });
+        if (mIsIptvDvrFMQFull && fullBufferTimer->get_elapsed_time_ms() > buffer_timeout) {
+            ALOGE("DVR FMQ has not been flushed within timeout of %d ms", buffer_timeout);
+            delete fullBufferTimer;
+            break;
+        }
+        timer = new Timer();
+        void* buf = malloc(sizeof(char) * IPTV_BUFFER_SIZE);
+        if (buf == nullptr) ALOGI("Buffer allocation failed");
+        ssize_t bytes_read = interface->read_stream(streamer, buf, buf_size, timeout_ms);
+        if (bytes_read == 0) {
+            double elapsed_time = timer->get_elapsed_time_ms();
+            if (elapsed_time > timeout_ms) {
+                ALOGE("[Demux] timeout reached - elapsed_time: %f, timeout: %d", elapsed_time,
+                      timeout_ms);
+            }
+            ALOGE("[Demux] Cannot read data from the socket");
+            delete timer;
+            break;
+        }
+
+        delete timer;
+        ALOGI("Number of bytes read: %zd", bytes_read);
+        int result = mDvrPlayback->writePlaybackFMQ(buf, bytes_read);
+
+        switch (result) {
+            case DVR_WRITE_FAILURE_REASON_FMQ_FULL:
+                if (!mIsIptvDvrFMQFull) {
+                    mIsIptvDvrFMQFull = true;
+                    fullBufferTimer = new Timer();
+                }
+                ALOGI("Waiting for client to flush DVR FMQ.");
+                break;
+            case DVR_WRITE_FAILURE_REASON_UNKNOWN:
+                ALOGE("Failed to write data into DVR FMQ for unknown reason");
+                break;
+            case DVR_WRITE_SUCCESS:
+                ALOGI("Wrote %zd bytes to DVR FMQ", bytes_read);
+                break;
+            default:
+                ALOGI("Invalid DVR Status");
+        }
+
+        free(buf);
+    }
+}
+
 ::ndk::ScopedAStatus Demux::setFrontendDataSource(int32_t in_frontendId) {
     ALOGV("%s", __FUNCTION__);
 
@@ -52,7 +182,6 @@
         return ::ndk::ScopedAStatus::fromServiceSpecificError(
                 static_cast<int32_t>(Result::NOT_INITIALIZED));
     }
-
     mFrontend = mTuner->getFrontendById(in_frontendId);
     if (mFrontend == nullptr) {
         return ::ndk::ScopedAStatus::fromServiceSpecificError(
@@ -61,6 +190,49 @@
 
     mTuner->setFrontendAsDemuxSource(in_frontendId, mDemuxId);
 
+    // if mFrontend is an IPTV frontend, create streamer to read TS data from socket
+    if (mFrontend->getFrontendType() == FrontendType::IPTV) {
+        // create a DVR instance on the demux
+        shared_ptr<IDvr> iptvDvr;
+
+        std::shared_ptr<IDvrCallback> dvrPlaybackCallback =
+                ::ndk::SharedRefBase::make<DvrPlaybackCallback>();
+
+        ::ndk::ScopedAStatus status =
+                openDvr(DvrType::PLAYBACK, IPTV_BUFFER_SIZE, dvrPlaybackCallback, &iptvDvr);
+        if (status.isOk()) {
+            ALOGI("DVR instance created");
+        }
+
+        // get plugin interface from frontend
+        dtv_plugin* interface = mFrontend->getIptvPluginInterface();
+        if (interface == nullptr) {
+            ALOGE("[Demux] getIptvPluginInterface(): plugin interface is null");
+            return ::ndk::ScopedAStatus::fromServiceSpecificError(
+                    static_cast<int32_t>(Result::INVALID_STATE));
+        }
+        ALOGI("[Demux] getIptvPluginInterface(): plugin interface is not null");
+
+        // get streamer object from Frontend instance
+        dtv_streamer* streamer = mFrontend->getIptvPluginStreamer();
+        if (streamer == nullptr) {
+            ALOGE("[Demux] getIptvPluginStreamer(): streamer is null");
+            return ::ndk::ScopedAStatus::fromServiceSpecificError(
+                    static_cast<int32_t>(Result::INVALID_STATE));
+        }
+        ALOGI("[Demux] getIptvPluginStreamer(): streamer is not null");
+
+        // get transport description from frontend
+        string transport_desc = mFrontend->getIptvTransportDescription();
+        ALOGI("[Demux] getIptvTransportDescription(): transport_desc: %s", transport_desc.c_str());
+
+        // call read_stream on the socket to populate the buffer with TS data
+        // while thread is alive, keep reading data
+        int timeout_ms = 20;
+        int buffer_timeout = 10000;  // 10s
+        mDemuxIptvReadThread = std::thread(&Demux::readIptvThreadLoop, this, interface, streamer,
+                                           IPTV_BUFFER_SIZE, timeout_ms, buffer_timeout);
+    }
     return ::ndk::ScopedAStatus::ok();
 }
 
@@ -193,61 +365,6 @@
     return ::ndk::ScopedAStatus::ok();
 }
 
-::ndk::ScopedAStatus Demux::openDvr(DvrType in_type, int32_t in_bufferSize,
-                                    const std::shared_ptr<IDvrCallback>& in_cb,
-                                    std::shared_ptr<IDvr>* _aidl_return) {
-    ALOGV("%s", __FUNCTION__);
-
-    if (in_cb == nullptr) {
-        ALOGW("[Demux] DVR callback can't be null");
-        *_aidl_return = nullptr;
-        return ::ndk::ScopedAStatus::fromServiceSpecificError(
-                static_cast<int32_t>(Result::INVALID_ARGUMENT));
-    }
-
-    set<int64_t>::iterator it;
-    switch (in_type) {
-        case DvrType::PLAYBACK:
-            mDvrPlayback = ndk::SharedRefBase::make<Dvr>(in_type, in_bufferSize, in_cb,
-                                                         this->ref<Demux>());
-            if (!mDvrPlayback->createDvrMQ()) {
-                mDvrPlayback = nullptr;
-                *_aidl_return = mDvrPlayback;
-                return ::ndk::ScopedAStatus::fromServiceSpecificError(
-                        static_cast<int32_t>(Result::UNKNOWN_ERROR));
-            }
-
-            for (it = mPlaybackFilterIds.begin(); it != mPlaybackFilterIds.end(); it++) {
-                if (!mDvrPlayback->addPlaybackFilter(*it, mFilters[*it])) {
-                    ALOGE("[Demux] Can't get filter info for DVR playback");
-                    mDvrPlayback = nullptr;
-                    *_aidl_return = mDvrPlayback;
-                    return ::ndk::ScopedAStatus::fromServiceSpecificError(
-                            static_cast<int32_t>(Result::UNKNOWN_ERROR));
-                }
-            }
-
-            *_aidl_return = mDvrPlayback;
-            return ::ndk::ScopedAStatus::ok();
-        case DvrType::RECORD:
-            mDvrRecord = ndk::SharedRefBase::make<Dvr>(in_type, in_bufferSize, in_cb,
-                                                       this->ref<Demux>());
-            if (!mDvrRecord->createDvrMQ()) {
-                mDvrRecord = nullptr;
-                *_aidl_return = mDvrRecord;
-                return ::ndk::ScopedAStatus::fromServiceSpecificError(
-                        static_cast<int32_t>(Result::UNKNOWN_ERROR));
-            }
-
-            *_aidl_return = mDvrRecord;
-            return ::ndk::ScopedAStatus::ok();
-        default:
-            *_aidl_return = nullptr;
-            return ::ndk::ScopedAStatus::fromServiceSpecificError(
-                    static_cast<int32_t>(Result::INVALID_ARGUMENT));
-    }
-}
-
 ::ndk::ScopedAStatus Demux::connectCiCam(int32_t in_ciCamId) {
     ALOGV("%s", __FUNCTION__);
 
diff --git a/tv/tuner/aidl/default/Demux.h b/tv/tuner/aidl/default/Demux.h
index 7d7aee4..ad7b7a7 100644
--- a/tv/tuner/aidl/default/Demux.h
+++ b/tv/tuner/aidl/default/Demux.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <aidl/android/hardware/tv/tuner/BnDemux.h>
+#include <aidl/android/hardware/tv/tuner/BnDvrCallback.h>
 
 #include <fmq/AidlMessageQueue.h>
 #include <math.h>
@@ -28,7 +29,9 @@
 #include "Filter.h"
 #include "Frontend.h"
 #include "TimeFilter.h"
+#include "Timer.h"
 #include "Tuner.h"
+#include "dtv_plugin.h"
 
 using namespace std;
 
@@ -44,6 +47,8 @@
 using ::android::hardware::EventFlag;
 
 using FilterMQ = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
+using AidlMQ = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
+using AidlMQDesc = MQDescriptor<int8_t, SynchronizedReadWrite>;
 
 class Dvr;
 class Filter;
@@ -51,6 +56,19 @@
 class TimeFilter;
 class Tuner;
 
+class DvrPlaybackCallback : public BnDvrCallback {
+  public:
+    virtual ::ndk::ScopedAStatus onPlaybackStatus(PlaybackStatus status) override {
+        ALOGD("demux.h: playback status %d", status);
+        return ndk::ScopedAStatus::ok();
+    }
+
+    virtual ::ndk::ScopedAStatus onRecordStatus(RecordStatus status) override {
+        ALOGD("Record Status %hhd", status);
+        return ndk::ScopedAStatus::ok();
+    }
+};
+
 class Demux : public BnDemux {
   public:
     Demux(int32_t demuxId, uint32_t filterTypes);
@@ -85,6 +103,8 @@
     void setIsRecording(bool isRecording);
     bool isRecording();
     void startFrontendInputLoop();
+    void readIptvThreadLoop(dtv_plugin* interface, dtv_streamer* streamer, size_t size,
+                            int timeout_ms, int buffer_timeout);
 
     /**
      * A dispatcher to read and dispatch input data to all the started filters.
@@ -104,6 +124,11 @@
     void setInUse(bool inUse);
     void setTunerService(std::shared_ptr<Tuner> tuner);
 
+    /**
+     * Setter for IPTV Reading thread
+     */
+    void setIptvThreadRunning(bool isIptvThreadRunning);
+
   private:
     // Tuner service
     std::shared_ptr<Tuner> mTuner;
@@ -167,6 +192,10 @@
 
     // Thread handlers
     std::thread mFrontendInputThread;
+    std::thread mDemuxIptvReadThread;
+
+    // track whether the DVR FMQ for IPTV Playback is full
+    bool mIsIptvDvrFMQFull = false;
 
     /**
      * If a specific filter's writing loop is still running
@@ -175,6 +204,13 @@
     std::atomic<bool> mKeepFetchingDataFromFrontend;
 
     /**
+     * Controls IPTV reading thread status
+     */
+    bool mIsIptvReadThreadRunning;
+    std::mutex mIsIptvThreadRunningMutex;
+    std::condition_variable mIsIptvThreadRunningCv;
+
+    /**
      * If the dvr recording is running.
      */
     bool mIsRecording = false;
diff --git a/tv/tuner/aidl/default/Dvr.cpp b/tv/tuner/aidl/default/Dvr.cpp
index c046ae3..393200c 100644
--- a/tv/tuner/aidl/default/Dvr.cpp
+++ b/tv/tuner/aidl/default/Dvr.cpp
@@ -236,6 +236,25 @@
     ALOGD("[Dvr] playback thread ended.");
 }
 
+void Dvr::maySendIptvPlaybackStatusCallback() {
+    lock_guard<mutex> lock(mPlaybackStatusLock);
+    int availableToRead = mDvrMQ->availableToRead();
+    int availableToWrite = mDvrMQ->availableToWrite();
+
+    PlaybackStatus newStatus = checkPlaybackStatusChange(availableToWrite, availableToRead,
+                                                         IPTV_PLAYBACK_STATUS_THRESHOLD_HIGH,
+                                                         IPTV_PLAYBACK_STATUS_THRESHOLD_LOW);
+    if (mPlaybackStatus != newStatus) {
+        map<int64_t, std::shared_ptr<Filter>>::iterator it;
+        for (it = mFilters.begin(); it != mFilters.end(); it++) {
+            std::shared_ptr<Filter> currentFilter = it->second;
+            currentFilter->setIptvDvrPlaybackStatus(newStatus);
+        }
+        mCallback->onPlaybackStatus(newStatus);
+        mPlaybackStatus = newStatus;
+    }
+}
+
 void Dvr::maySendPlaybackStatusCallback() {
     lock_guard<mutex> lock(mPlaybackStatusLock);
     int availableToRead = mDvrMQ->availableToRead();
@@ -379,7 +398,7 @@
 
     // Read es raw data from the FMQ per meta data built previously
     vector<int8_t> frameData;
-    map<int64_t, std::shared_ptr<IFilter>>::iterator it;
+    map<int64_t, std::shared_ptr<Filter>>::iterator it;
     int pid = 0;
     for (int i = 0; i < totalFrames; i++) {
         frameData.resize(esMeta[i].len);
@@ -411,7 +430,7 @@
 }
 
 void Dvr::startTpidFilter(vector<int8_t> data) {
-    map<int64_t, std::shared_ptr<IFilter>>::iterator it;
+    map<int64_t, std::shared_ptr<Filter>>::iterator it;
     for (it = mFilters.begin(); it != mFilters.end(); it++) {
         uint16_t pid = ((data[1] & 0x1f) << 8) | ((data[2] & 0xff));
         if (DEBUG_DVR) {
@@ -432,7 +451,7 @@
         }
     }
 
-    map<int64_t, std::shared_ptr<IFilter>>::iterator it;
+    map<int64_t, std::shared_ptr<Filter>>::iterator it;
     // Handle the output data per filter type
     for (it = mFilters.begin(); it != mFilters.end(); it++) {
         if (!mDemux->startFilterHandler(it->first).isOk()) {
@@ -443,6 +462,24 @@
     return true;
 }
 
+int Dvr::writePlaybackFMQ(void* buf, size_t size) {
+    lock_guard<mutex> lock(mWriteLock);
+    ALOGI("Playback status: %d", mPlaybackStatus);
+    if (mPlaybackStatus == PlaybackStatus::SPACE_FULL) {
+        ALOGW("[Dvr] stops writing and wait for the client side flushing.");
+        return DVR_WRITE_FAILURE_REASON_FMQ_FULL;
+    }
+    ALOGI("availableToWrite before: %zu", mDvrMQ->availableToWrite());
+    if (mDvrMQ->write((int8_t*)buf, size)) {
+        mDvrEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
+        ALOGI("availableToWrite: %zu", mDvrMQ->availableToWrite());
+        maySendIptvPlaybackStatusCallback();
+        return DVR_WRITE_SUCCESS;
+    }
+    maySendIptvPlaybackStatusCallback();
+    return DVR_WRITE_FAILURE_REASON_UNKNOWN;
+}
+
 bool Dvr::writeRecordFMQ(const vector<int8_t>& data) {
     lock_guard<mutex> lock(mWriteLock);
     if (mRecordStatus == RecordStatus::OVERFLOW) {
@@ -486,7 +523,7 @@
     return mRecordStatus;
 }
 
-bool Dvr::addPlaybackFilter(int64_t filterId, std::shared_ptr<IFilter> filter) {
+bool Dvr::addPlaybackFilter(int64_t filterId, std::shared_ptr<Filter> filter) {
     mFilters[filterId] = filter;
     return true;
 }
diff --git a/tv/tuner/aidl/default/Dvr.h b/tv/tuner/aidl/default/Dvr.h
index 293c533..07f95ad 100644
--- a/tv/tuner/aidl/default/Dvr.h
+++ b/tv/tuner/aidl/default/Dvr.h
@@ -43,6 +43,19 @@
 
 using DvrMQ = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
 
+const int DVR_WRITE_SUCCESS = 0;
+const int DVR_WRITE_FAILURE_REASON_FMQ_FULL = 1;
+const int DVR_WRITE_FAILURE_REASON_UNKNOWN = 2;
+
+const int TS_SIZE = 188;
+const int IPTV_BUFFER_SIZE = TS_SIZE * 7 * 8;  // defined in service_streamer_udp in cbs v3 project
+
+// Thresholds are defined to indicate how full the buffers are.
+const double HIGH_THRESHOLD_PERCENT = 0.90;
+const double LOW_THRESHOLD_PERCENT = 0.15;
+const int IPTV_PLAYBACK_STATUS_THRESHOLD_HIGH = IPTV_BUFFER_SIZE * HIGH_THRESHOLD_PERCENT;
+const int IPTV_PLAYBACK_STATUS_THRESHOLD_LOW = IPTV_BUFFER_SIZE * LOW_THRESHOLD_PERCENT;
+
 struct MediaEsMetaData {
     bool isAudio;
     int startIndex;
@@ -80,8 +93,9 @@
      * Return false is any of the above processes fails.
      */
     bool createDvrMQ();
+    int writePlaybackFMQ(void* buf, size_t size);
     bool writeRecordFMQ(const std::vector<int8_t>& data);
-    bool addPlaybackFilter(int64_t filterId, std::shared_ptr<IFilter> filter);
+    bool addPlaybackFilter(int64_t filterId, std::shared_ptr<Filter> filter);
     bool removePlaybackFilter(int64_t filterId);
     bool readPlaybackFMQ(bool isVirtualFrontend, bool isRecording);
     bool processEsDataOnPlayback(bool isVirtualFrontend, bool isRecording);
@@ -96,12 +110,13 @@
     DvrType mType;
     uint32_t mBufferSize;
     std::shared_ptr<IDvrCallback> mCallback;
-    std::map<int64_t, std::shared_ptr<IFilter>> mFilters;
+    std::map<int64_t, std::shared_ptr<Filter>> mFilters;
 
     void deleteEventFlag();
     bool readDataFromMQ();
     void getMetaDataValue(int& index, int8_t* dataOutputBuffer, int& value);
     void maySendPlaybackStatusCallback();
+    void maySendIptvPlaybackStatusCallback();
     void maySendRecordStatusCallback();
     PlaybackStatus checkPlaybackStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
                                              int64_t highThreshold, int64_t lowThreshold);
diff --git a/tv/tuner/aidl/default/Filter.cpp b/tv/tuner/aidl/default/Filter.cpp
index ba9602e..212d329 100644
--- a/tv/tuner/aidl/default/Filter.cpp
+++ b/tv/tuner/aidl/default/Filter.cpp
@@ -326,6 +326,10 @@
     ALOGV("%s", __FUNCTION__);
     mFilterThreadRunning = true;
     std::vector<DemuxFilterEvent> events;
+
+    mFilterCount += 1;
+    mDemux->setIptvThreadRunning(true);
+
     // All the filter event callbacks in start are for testing purpose.
     switch (mType.mainType) {
         case DemuxFilterMainType::TS:
@@ -362,6 +366,11 @@
 ::ndk::ScopedAStatus Filter::stop() {
     ALOGV("%s", __FUNCTION__);
 
+    mFilterCount -= 1;
+    if (mFilterCount == 0) {
+        mDemux->setIptvThreadRunning(false);
+    }
+
     mFilterThreadRunning = false;
     if (mFilterThread.joinable()) {
         mFilterThread.join();
@@ -565,6 +574,8 @@
 
     ALOGD("[Filter] filter %" PRIu64 " threadLoop start.", mFilterId);
 
+    ALOGI("IPTV DVR Playback status on Filter: %d", mIptvDvrPlaybackStatus);
+
     // For the first time of filter output, implementation needs to send the filter
     // Event Callback without waiting for the DATA_CONSUMED to init the process.
     while (mFilterThreadRunning) {
diff --git a/tv/tuner/aidl/default/Filter.h b/tv/tuner/aidl/default/Filter.h
index 0985296..e2a0c7a 100644
--- a/tv/tuner/aidl/default/Filter.h
+++ b/tv/tuner/aidl/default/Filter.h
@@ -147,6 +147,7 @@
     bool isMediaFilter() { return mIsMediaFilter; };
     bool isPcrFilter() { return mIsPcrFilter; };
     bool isRecordFilter() { return mIsRecordFilter; };
+    void setIptvDvrPlaybackStatus(PlaybackStatus newStatus) { mIptvDvrPlaybackStatus = newStatus; };
 
   private:
     // Demux service
@@ -286,6 +287,9 @@
     int mStartId = 0;
     uint8_t mScramblingStatusMonitored = 0;
     uint8_t mIpCidMonitored = 0;
+
+    PlaybackStatus mIptvDvrPlaybackStatus;
+    std::atomic<int> mFilterCount = 0;
 };
 
 }  // namespace tuner
diff --git a/tv/tuner/aidl/default/Frontend.cpp b/tv/tuner/aidl/default/Frontend.cpp
index cd072bf..57ed1ba 100644
--- a/tv/tuner/aidl/default/Frontend.cpp
+++ b/tv/tuner/aidl/default/Frontend.cpp
@@ -34,6 +34,8 @@
     mTuner = nullptr;
     // Init callback to nullptr
     mCallback = nullptr;
+    mIptvPluginInterface = nullptr;
+    mIptvPluginStreamer = nullptr;
 
     switch (mType) {
         case FrontendType::ISDBS: {
@@ -213,20 +215,82 @@
     return ::ndk::ScopedAStatus::ok();
 }
 
-::ndk::ScopedAStatus Frontend::tune(const FrontendSettings& /* in_settings */) {
-    ALOGV("%s", __FUNCTION__);
+void Frontend::readTuneByte(dtv_streamer* streamer, void* buf, size_t buf_size, int timeout_ms) {
+    ssize_t bytes_read = mIptvPluginInterface->read_stream(streamer, buf, buf_size, timeout_ms);
+    if (bytes_read <= 0) {
+        ALOGI("[   ERROR   ] Tune byte couldn't be read.");
+        return;
+    }
+    mCallback->onEvent(FrontendEventType::LOCKED);
+    mIsLocked = true;
+}
+
+::ndk::ScopedAStatus Frontend::tune(const FrontendSettings& in_settings) {
     if (mCallback == nullptr) {
-        ALOGW("[   WARN   ] Frontend callback is not set when tune");
+        ALOGW("[   WARN   ] Frontend callback is not set for tunin0g");
         return ::ndk::ScopedAStatus::fromServiceSpecificError(
                 static_cast<int32_t>(Result::INVALID_STATE));
     }
 
     if (mType != FrontendType::IPTV) {
         mTuner->frontendStartTune(mId);
-    }
+        mCallback->onEvent(FrontendEventType::LOCKED);
+        mIsLocked = true;
+    } else {
+        // This is a reference implementation for IPTV. It uses an additional socket buffer.
+        // Vendors can use hardware memory directly to make the implementation more performant.
+        ALOGI("[   INFO   ] Frontend type is set to IPTV, tag = %d id=%d", in_settings.getTag(),
+              mId);
 
-    mCallback->onEvent(FrontendEventType::LOCKED);
-    mIsLocked = true;
+        // load udp plugin for reading TS data
+        const char* path = "/vendor/lib/iptv_udp_plugin.so";
+        DtvPlugin* plugin = new DtvPlugin(path);
+        if (!plugin) {
+            ALOGE("Failed to create DtvPlugin, plugin_path is invalid");
+            return ::ndk::ScopedAStatus::fromServiceSpecificError(
+                    static_cast<int32_t>(Result::INVALID_ARGUMENT));
+        }
+        bool plugin_loaded = plugin->load();
+        if (!plugin_loaded) {
+            ALOGE("Failed to load plugin");
+            return ::ndk::ScopedAStatus::fromServiceSpecificError(
+                    static_cast<int32_t>(Result::INVALID_ARGUMENT));
+        }
+        mIptvPluginInterface = plugin->interface();
+
+        // validate content_url format
+        std::string content_url = in_settings.get<FrontendSettings::Tag::iptv>()->contentUrl;
+        std::string transport_desc = "{ \"uri\": \"" + content_url + "\"}";
+        ALOGI("[   INFO   ] transport_desc: %s", transport_desc.c_str());
+        bool is_transport_desc_valid = plugin->validate(transport_desc.c_str());
+        if (!is_transport_desc_valid) {  // not of format protocol://ip:port
+            ALOGE("[   INFO   ] transport_desc is not valid");
+            return ::ndk::ScopedAStatus::fromServiceSpecificError(
+                    static_cast<int32_t>(Result::INVALID_ARGUMENT));
+        }
+        mIptvTransportDescription = transport_desc;
+
+        // create a streamer and open it for reading data
+        dtv_streamer* streamer = mIptvPluginInterface->create_streamer();
+        mIptvPluginStreamer = streamer;
+        int open_fd = mIptvPluginInterface->open_stream(streamer, transport_desc.c_str());
+        if (open_fd < 0) {
+            ALOGE("[   INFO   ] could not open stream");
+            return ::ndk::ScopedAStatus::fromServiceSpecificError(
+                    static_cast<int32_t>(Result::INVALID_ARGUMENT));
+        }
+        ALOGI("[   INFO   ] open_stream successful, open_fd=%d", open_fd);
+
+        size_t buf_size = 1;
+        int timeout_ms = 2000;
+        void* buf = malloc(sizeof(char) * buf_size);
+        if (buf == nullptr) ALOGI("malloc buf failed [TUNE]");
+        ALOGI("[   INFO   ] [Tune] Allocated buffer of size %zu", buf_size);
+        mIptvFrontendTuneThread =
+                std::thread(&Frontend::readTuneByte, this, streamer, buf, buf_size, timeout_ms);
+        if (mIptvFrontendTuneThread.joinable()) mIptvFrontendTuneThread.join();
+        free(buf);
+    }
 
     return ::ndk::ScopedAStatus::ok();
 }
@@ -1002,6 +1066,18 @@
     return mId;
 }
 
+dtv_plugin* Frontend::getIptvPluginInterface() {
+    return mIptvPluginInterface;
+}
+
+string Frontend::getIptvTransportDescription() {
+    return mIptvTransportDescription;
+}
+
+dtv_streamer* Frontend::getIptvPluginStreamer() {
+    return mIptvPluginStreamer;
+}
+
 bool Frontend::supportsSatellite() {
     return mType == FrontendType::DVBS || mType == FrontendType::ISDBS ||
            mType == FrontendType::ISDBS3;
diff --git a/tv/tuner/aidl/default/Frontend.h b/tv/tuner/aidl/default/Frontend.h
index 85bd636..17a1aee 100644
--- a/tv/tuner/aidl/default/Frontend.h
+++ b/tv/tuner/aidl/default/Frontend.h
@@ -21,6 +21,7 @@
 #include <iostream>
 #include <thread>
 #include "Tuner.h"
+#include "dtv_plugin.h"
 
 using namespace std;
 
@@ -60,6 +61,10 @@
     FrontendType getFrontendType();
     int32_t getFrontendId();
     string getSourceFile();
+    dtv_plugin* getIptvPluginInterface();
+    string getIptvTransportDescription();
+    dtv_streamer* getIptvPluginStreamer();
+    void readTuneByte(dtv_streamer* streamer, void* buf, size_t size, int timeout_ms);
     bool isLocked();
     void getFrontendInfo(FrontendInfo* _aidl_return);
     void setTunerService(std::shared_ptr<Tuner> tuner);
@@ -81,6 +86,10 @@
     std::ifstream mFrontendData;
     FrontendCapabilities mFrontendCaps;
     vector<FrontendStatusType> mFrontendStatusCaps;
+    dtv_plugin* mIptvPluginInterface;
+    string mIptvTransportDescription;
+    dtv_streamer* mIptvPluginStreamer;
+    std::thread mIptvFrontendTuneThread;
 };
 
 }  // namespace tuner
diff --git a/tv/tuner/aidl/default/Timer.h b/tv/tuner/aidl/default/Timer.h
new file mode 100644
index 0000000..c6327cb
--- /dev/null
+++ b/tv/tuner/aidl/default/Timer.h
@@ -0,0 +1,17 @@
+#include <chrono>
+using namespace std::chrono;
+class Timer {
+  public:
+    Timer() { start_time = steady_clock::now(); }
+
+    ~Timer() { stop_time = steady_clock::now(); }
+
+    double get_elapsed_time_ms() {
+        auto current_time = std::chrono::steady_clock::now();
+        return duration_cast<milliseconds>(current_time - start_time).count();
+    }
+
+  private:
+    time_point<steady_clock> start_time;
+    time_point<steady_clock> stop_time;
+};
\ No newline at end of file
diff --git a/tv/tuner/aidl/default/dtv_plugin.cpp b/tv/tuner/aidl/default/dtv_plugin.cpp
new file mode 100644
index 0000000..4e73ee5
--- /dev/null
+++ b/tv/tuner/aidl/default/dtv_plugin.cpp
@@ -0,0 +1,130 @@
+#include "dtv_plugin.h"
+#include <dlfcn.h>
+#include <libgen.h>
+#include <utils/Log.h>
+
+DtvPlugin::DtvPlugin(const char* plugin_path) {
+    path_ = plugin_path;
+    basename_ = basename(path_);
+    module_ = NULL;
+    interface_ = NULL;
+    loaded_ = false;
+}
+
+DtvPlugin::~DtvPlugin() {
+    if (module_ != NULL) {
+        if (dlclose(module_)) ALOGE("DtvPlugin: Failed to close plugin '%s'", basename_);
+    }
+}
+
+bool DtvPlugin::load() {
+    ALOGI("Loading plugin '%s' from path '%s'", basename_, path_);
+
+    module_ = dlopen(path_, RTLD_LAZY);
+    if (module_ == NULL) {
+        ALOGE("DtvPlugin::Load::Failed to load plugin '%s'", basename_);
+        ALOGE("dlopen error: %s", dlerror());
+        return false;
+    }
+
+    interface_ = (dtv_plugin*)dlsym(module_, "plugin_entry");
+
+    if (interface_ == NULL) {
+        ALOGE("plugin_entry is NULL.");
+        goto error;
+    }
+
+    if (!interface_->get_transport_types || !interface_->get_streamer_count ||
+        !interface_->validate || !interface_->create_streamer || !interface_->destroy_streamer ||
+        !interface_->open_stream || !interface_->close_stream || !interface_->read_stream) {
+        ALOGW("Plugin: missing one or more callbacks");
+        goto error;
+    }
+
+    loaded_ = true;
+
+    return true;
+
+error:
+    if (dlclose(module_)) ALOGE("Failed to close plugin '%s'", basename_);
+
+    return false;
+}
+
+int DtvPlugin::getStreamerCount() {
+    if (!loaded_) {
+        ALOGE("DtvPlugin::GetStreamerCount: Plugin '%s' not loaded!", basename_);
+        return 0;
+    }
+
+    return interface_->get_streamer_count();
+}
+
+bool DtvPlugin::isTransportTypeSupported(const char* transport_type) {
+    const char** transport;
+
+    if (!loaded_) {
+        ALOGE("Plugin '%s' not loaded!", basename_);
+        return false;
+    }
+
+    transport = interface_->get_transport_types();
+    if (transport == NULL) return false;
+
+    while (*transport) {
+        if (strcmp(transport_type, *transport) == 0) return true;
+        transport++;
+    }
+
+    return false;
+}
+
+bool DtvPlugin::validate(const char* transport_desc) {
+    if (!loaded_) {
+        ALOGE("Plugin '%s' is not loaded!", basename_);
+        return false;
+    }
+
+    return interface_->validate(transport_desc);
+}
+
+bool DtvPlugin::getProperty(const char* key, void* value, int* size) {
+    if (!loaded_) {
+        ALOGE("Plugin '%s' is not loaded!", basename_);
+        return false;
+    }
+
+    if (!interface_->get_property) return false;
+
+    *size = interface_->get_property(NULL, key, value, *size);
+
+    return *size < 0 ? false : true;
+}
+
+bool DtvPlugin::setProperty(const char* key, const void* value, int size) {
+    int ret;
+
+    if (!loaded_) {
+        ALOGE("Plugin '%s': not loaded!", basename_);
+        return false;
+    }
+
+    if (!interface_->set_property) return false;
+
+    ret = interface_->set_property(NULL, key, value, size);
+
+    return ret < 0 ? false : true;
+}
+
+struct dtv_plugin* DtvPlugin::interface() {
+    if (!loaded_) {
+        ALOGE("Plugin '%s' is not loaded!", basename_);
+        return NULL;
+    }
+
+    return interface_;
+}
+
+const char* DtvPlugin::pluginBasename() {
+    return basename_;
+}
diff --git a/tv/tuner/aidl/default/dtv_plugin.h b/tv/tuner/aidl/default/dtv_plugin.h
new file mode 100644
index 0000000..0ee5489
--- /dev/null
+++ b/tv/tuner/aidl/default/dtv_plugin.h
@@ -0,0 +1,31 @@
+#ifndef LIVE_DTV_PLUGIN_H_
+#define LIVE_DTV_PLUGIN_H_
+
+#include <fstream>
+#include "dtv_plugin_api.h"
+
+class DtvPlugin {
+  public:
+    DtvPlugin(const char* plugin_path);
+    ~DtvPlugin();
+
+    bool load();
+    int getStreamerCount();
+    bool validate(const char* transport_desc);
+    bool isTransportTypeSupported(const char* transport_type);
+    //    /* plugin-wide properties */
+    bool getProperty(const char* key, void* value, int* size);
+    bool setProperty(const char* key, const void* value, int size);
+
+    struct dtv_plugin* interface();
+    const char* pluginBasename();
+
+  protected:
+    const char* path_;
+    char* basename_;
+    void* module_;
+    struct dtv_plugin* interface_;
+    bool loaded_;
+};
+
+#endif  // LIVE_DTV_PLUGIN_H_
diff --git a/tv/tuner/aidl/default/dtv_plugin_api.h b/tv/tuner/aidl/default/dtv_plugin_api.h
new file mode 100644
index 0000000..8fe7c1d
--- /dev/null
+++ b/tv/tuner/aidl/default/dtv_plugin_api.h
@@ -0,0 +1,137 @@
+#ifndef LIVE_DTV_PLUGIN_API_H_
+#define LIVE_DTV_PLUGIN_API_H_
+
+#include <stdint.h>
+
+struct dtv_streamer;
+
+struct dtv_plugin {
+    uint32_t version;
+
+    /**
+     * get_transport_types() - Retrieve a list of supported transport types.
+     *
+     * Return: A NULL-terminated list of supported transport types.
+     */
+    const char** (*get_transport_types)(void);
+
+    /**
+     * get_streamer_count() - Get number of streamers that can be created.
+     *
+     * Return: The number of streamers that can be created.
+     */
+    int (*get_streamer_count)(void);
+
+    /**
+     * validate() - Check if transport description is valid.
+     * @transport_desc: NULL-terminated transport description in json format.
+     *
+     * Return: 1 if valid, 0 otherwise.
+     */
+    int (*validate)(const char* transport_desc);
+
+    /**
+     * create_streamer() - Create a streamer object.
+     *
+     * Return: A pointer to a new streamer object.
+     */
+    struct dtv_streamer* (*create_streamer)(void);
+
+    /**
+     * destroy_streamer() - Free a streamer object and all associated resources.
+     * @st: Pointer to a streamer object
+     */
+    void (*destroy_streamer)(struct dtv_streamer* streamer);
+
+    /**
+     * set_property() - Set a key/value pair property.
+     * @streamer: Pointer to a streamer object (may be NULL for plugin-wide properties).
+     * @key: NULL-terminated property name.
+     * @value: Property value.
+     * @size: Property value size.
+     *
+     * Return: 0 if success, -1 otherwise.
+     */
+    int (*set_property)(struct dtv_streamer* streamer, const char* key, const void* value,
+                        size_t size);
+
+    /**
+     * get_property() - Get a property's value.
+     * @streamer: Pointer to a streamer (may be NULL for plugin-wide properties).
+     * @key: NULL-terminated property name.
+     * @value: Property value.
+     * @size: Property value size.
+     *
+     * Return: >= 0 if success, -1 otherwise.
+     *
+     * If size is 0, get_property will return the size needed to hold the value.
+     */
+    int (*get_property)(struct dtv_streamer* streamer, const char* key, void* value, size_t size);
+
+    /**
+     * add_pid() - Add a TS filter on a given pid.
+     * @streamer: The streamer that outputs the TS.
+     * @pid: The pid to add to the TS output.
+     *
+     * Return: 0 if success, -1 otherwise.
+     *
+     * This function is optional but can be useful if a hardware remux is
+     * available.
+     */
+    int (*add_pid)(struct dtv_streamer* streamer, int pid);
+
+    /**
+     * remove_pid() - Remove a TS filter on a given pid.
+     * @streamer: The streamer that outputs the TS.
+     * @pid: The pid to remove from the TS output.
+     *
+     * Return: 0 if success, -1 otherwise.
+     *
+     * This function is optional.
+     */
+    int (*remove_pid)(struct dtv_streamer* streamer, int pid);
+
+    /**
+     * open_stream() - Open a stream from a transport description.
+     * @streamer: The streamer which will handle the stream.
+     * @transport_desc: NULL-terminated transport description in json format.
+     *
+     * The streamer will allocate the resources and make the appropriate
+     * connections to handle this transport.
+     * This function returns a file descriptor that can be polled for events.
+     *
+     * Return: A file descriptor if success, -1 otherwise.
+     */
+    int (*open_stream)(struct dtv_streamer* streamer, const char* transport_desc);
+
+    /**
+     * close_stream() - Release an open stream.
+     * @streamer: The streamer from which the stream should be released.
+     */
+    void (*close_stream)(struct dtv_streamer* streamer);
+
+    /**
+     * read_stream() - Read stream data.
+     * @streamer: The streamer to read from.
+     * @buf: The destination buffer.
+     * @count: The number of bytes to read.
+     * @timeout_ms: Timeout in ms.
+     *
+     * Return: The number of bytes read, -1 if error.
+     */
+    ssize_t (*read_stream)(struct dtv_streamer* streamer, void* buf, size_t count, int timeout_ms);
+};
+
+struct dtv_plugin_event {
+    int id;
+    char data[0];
+};
+
+enum {
+    DTV_PLUGIN_EVENT_SIGNAL_LOST = 1,
+    DTV_PLUGIN_EVENT_SIGNAL_READY,
+};
+
+#define PROPERTY_STATISTICS "statistics"
+
+#endif  // LIVE_DTV_PLUGIN_API_H_
diff --git a/tv/tuner/aidl/vts/functional/FilterTests.cpp b/tv/tuner/aidl/vts/functional/FilterTests.cpp
index 53afef7..533d0e6 100644
--- a/tv/tuner/aidl/vts/functional/FilterTests.cpp
+++ b/tv/tuner/aidl/vts/functional/FilterTests.cpp
@@ -305,13 +305,18 @@
     ndk::ScopedAStatus status;
 
     status = mFilters[filterId]->configureMonitorEvent(monitorEventTypes);
+    return AssertionResult(status.isOk());
+}
+
+AssertionResult FilterTests::testMonitorEvent(uint64_t filterId, uint32_t monitorEventTypes) {
+    EXPECT_TRUE(mFilterCallbacks[filterId]) << "Test with getNewlyOpenedFilterId first.";
     if (monitorEventTypes & static_cast<int32_t>(DemuxFilterMonitorEventType::SCRAMBLING_STATUS)) {
         mFilterCallbacks[filterId]->testFilterScramblingEvent();
     }
     if (monitorEventTypes & static_cast<int32_t>(DemuxFilterMonitorEventType::IP_CID_CHANGE)) {
         mFilterCallbacks[filterId]->testFilterIpCidEvent();
     }
-    return AssertionResult(status.isOk());
+    return AssertionResult(true);
 }
 
 AssertionResult FilterTests::startIdTest(int64_t filterId) {
diff --git a/tv/tuner/aidl/vts/functional/FilterTests.h b/tv/tuner/aidl/vts/functional/FilterTests.h
index f579441..f57093e 100644
--- a/tv/tuner/aidl/vts/functional/FilterTests.h
+++ b/tv/tuner/aidl/vts/functional/FilterTests.h
@@ -124,6 +124,7 @@
     AssertionResult configAvFilterStreamType(AvStreamType type, int64_t filterId);
     AssertionResult configIpFilterCid(int32_t ipCid, int64_t filterId);
     AssertionResult configureMonitorEvent(int64_t filterId, int32_t monitorEventTypes);
+    AssertionResult testMonitorEvent(uint64_t filterId, uint32_t monitorEventTypes);
     AssertionResult getFilterMQDescriptor(int64_t filterId, bool getMqDesc);
     AssertionResult startFilter(int64_t filterId);
     AssertionResult stopFilter(int64_t filterId);
diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
index 9db82c8..3664b6c 100644
--- a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
+++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
@@ -60,6 +60,11 @@
     }
     ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
+    ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+    if (filterConf.monitorEventTypes > 0) {
+        ASSERT_TRUE(mFilterTests.testMonitorEvent(filterId, filterConf.monitorEventTypes));
+    }
+    ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
     ASSERT_TRUE(mFilterTests.stopFilter(filterId));
     ASSERT_TRUE(mFilterTests.closeFilter(filterId));
     ASSERT_TRUE(mDemuxTests.closeDemux());
diff --git a/usb/aidl/Android.bp b/usb/aidl/Android.bp
index b82f6d5..b61576d 100644
--- a/usb/aidl/Android.bp
+++ b/usb/aidl/Android.bp
@@ -45,6 +45,6 @@
         },
 
     ],
-    frozen: true,
+    frozen: false,
 
 }
diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ComplianceWarning.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ComplianceWarning.aidl
index 8b67070..c7c9103 100644
--- a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ComplianceWarning.aidl
+++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ComplianceWarning.aidl
@@ -38,4 +38,9 @@
   DEBUG_ACCESSORY = 2,
   BC_1_2 = 3,
   MISSING_RP = 4,
+  INPUT_POWER_LIMITED = 5,
+  MISSING_DATA_LINES = 6,
+  ENUMERATION_FAIL = 7,
+  FLAKY_CONNECTION = 8,
+  UNRELIABLE_IO = 9,
 }
diff --git a/usb/aidl/android/hardware/usb/ComplianceWarning.aidl b/usb/aidl/android/hardware/usb/ComplianceWarning.aidl
index 4c18a31..bf79399 100644
--- a/usb/aidl/android/hardware/usb/ComplianceWarning.aidl
+++ b/usb/aidl/android/hardware/usb/ComplianceWarning.aidl
@@ -56,4 +56,29 @@
      * Type-C Cable and Connector Specification.
      */
     MISSING_RP = 4,
+    /**
+     * Used to indicate the charging setups on the USB ports are unable to
+     * deliver negotiated power.
+     */
+    INPUT_POWER_LIMITED = 5,
+    /**
+     * Used to indicate the cable/connector on the USB ports are missing
+     * the required wires on the data pins to make data transfer.
+     */
+    MISSING_DATA_LINES = 6,
+    /**
+     * Used to indicate enumeration failures on the USB ports, potentially due to
+     * signal integrity issues or other causes.
+     */
+    ENUMERATION_FAIL = 7,
+    /**
+     * Used to indicate unexpected data disconnection on the USB ports,
+     * potentially due to signal integrity issues or other causes.
+     */
+    FLAKY_CONNECTION = 8,
+    /**
+     * Used to indicate unreliable or slow data transfer on the USB ports,
+     * potentially due to signal integrity issues or other causes.
+     */
+    UNRELIABLE_IO = 9,
 }
diff --git a/usb/aidl/default/Android.bp b/usb/aidl/default/Android.bp
index 472e732..ee8e479 100644
--- a/usb/aidl/default/Android.bp
+++ b/usb/aidl/default/Android.bp
@@ -34,7 +34,7 @@
         "Usb.cpp",
     ],
     shared_libs: [
-        "android.hardware.usb-V2-ndk",
+        "android.hardware.usb-V3-ndk",
         "libbase",
         "libbinder_ndk",
         "libcutils",
@@ -43,9 +43,11 @@
     ],
 }
 
-filegroup {
+prebuilt_etc {
     name: "android.hardware.usb-service.example.xml",
-    srcs: ["android.hardware.usb-service.example.xml"],
+    src: "android.hardware.usb-service.example.xml",
+    sub_dir: "vintf",
+    installable: false,
 }
 
 filegroup {
diff --git a/usb/aidl/default/android.hardware.usb-service.example.xml b/usb/aidl/default/android.hardware.usb-service.example.xml
index c3f07f5..7ac2067 100644
--- a/usb/aidl/default/android.hardware.usb-service.example.xml
+++ b/usb/aidl/default/android.hardware.usb-service.example.xml
@@ -1,7 +1,7 @@
 <manifest version="1.0" type="device">
     <hal format="aidl">
         <name>android.hardware.usb</name>
-        <version>2</version>
+        <version>3</version>
         <interface>
             <name>IUsb</name>
             <instance>default</instance>
diff --git a/usb/apex/Android.bp b/usb/aidl/default/apex/Android.bp
similarity index 70%
rename from usb/apex/Android.bp
rename to usb/aidl/default/apex/Android.bp
index 765aa21..29278dd 100644
--- a/usb/apex/Android.bp
+++ b/usb/aidl/default/apex/Android.bp
@@ -16,33 +16,22 @@
     default_applicable_licenses: ["hardware_interfaces_license"],
 }
 
-apex_key {
-    name: "com.android.hardware.usb.key",
-    public_key: "com.android.hardware.usb.avbpubkey",
-    private_key: "com.android.hardware.usb.pem",
-}
-
-android_app_certificate {
-    name: "com.android.hardware.usb.certificate",
-    certificate: "com.android.hardware.usb",
-}
-
 apex {
     name: "com.android.hardware.usb",
     manifest: "manifest.json",
     file_contexts: "file_contexts",
-    key: "com.android.hardware.usb.key",
-    certificate: ":com.android.hardware.usb.certificate",
+    key: "com.android.hardware.key",
+    certificate: ":com.android.hardware.certificate",
     updatable: false,
-    soc_specific: true,
-    use_vndk_as_stable: true,
+    vendor: true,
+
     binaries: ["android.hardware.usb-service.example"],
     prebuilts: [
         "com.android.hardware.usb.rc", // init .rc
         "android.hardware.usb.accessory.prebuilt.xml",
         "android.hardware.usb.host.prebuilt.xml",
+        "android.hardware.usb-service.example.xml",
     ],
-    vintf_fragments: [":android.hardware.usb-service.example.xml"],
 }
 
 // Replace the binary path from /vendor/bin to /apex/{name}/bin in the init .rc file
@@ -50,7 +39,7 @@
     name: "com.android.hardware.usb.rc-gen",
     srcs: [":android.hardware.usb-service.example.rc"],
     out: ["com.android.hardware.usb.rc"],
-    cmd: "sed -E 's/\\/vendor/\\/apex\\/com.android.hardware.usb/' $(in) > $(out)",
+    cmd: "sed -E 's@/vendor/bin@/apex/com.android.hardware.usb/bin@' $(in) > $(out)",
 }
 
 prebuilt_etc {
diff --git a/usb/aidl/default/apex/file_contexts b/usb/aidl/default/apex/file_contexts
new file mode 100644
index 0000000..53404f7
--- /dev/null
+++ b/usb/aidl/default/apex/file_contexts
@@ -0,0 +1,3 @@
+(/.*)?                                                 u:object_r:vendor_file:s0
+/etc(/.*)?                                             u:object_r:vendor_configs_file:s0
+/bin/hw/android\.hardware\.usb-service\.example        u:object_r:hal_usb_default_exec:s0
\ No newline at end of file
diff --git a/usb/apex/manifest.json b/usb/aidl/default/apex/manifest.json
similarity index 100%
rename from usb/apex/manifest.json
rename to usb/aidl/default/apex/manifest.json
diff --git a/usb/aidl/vts/Android.bp b/usb/aidl/vts/Android.bp
index d0e0eec..cf9299e 100644
--- a/usb/aidl/vts/Android.bp
+++ b/usb/aidl/vts/Android.bp
@@ -34,7 +34,7 @@
         "libbinder_ndk",
     ],
     static_libs: [
-        "android.hardware.usb-V2-ndk",
+        "android.hardware.usb-V3-ndk",
     ],
     test_suites: [
         "general-tests",
diff --git a/usb/aidl/vts/VtsAidlUsbTargetTest.cpp b/usb/aidl/vts/VtsAidlUsbTargetTest.cpp
index e9aa65b..7b7269d 100644
--- a/usb/aidl/vts/VtsAidlUsbTargetTest.cpp
+++ b/usb/aidl/vts/VtsAidlUsbTargetTest.cpp
@@ -654,11 +654,18 @@
   EXPECT_EQ(2, usb_last_cookie);
   EXPECT_EQ(transactionId, last_transactionId);
 
-  // Current compliance values range from [1, 4]
   if (usb_last_port_status.supportsComplianceWarnings) {
     for (auto warning : usb_last_port_status.complianceWarnings) {
       EXPECT_TRUE((int)warning >= (int)ComplianceWarning::OTHER);
-      EXPECT_TRUE((int)warning <= (int)ComplianceWarning::MISSING_RP);
+      /*
+       * Version 2 compliance values range from [1, 4]
+       * Version 3 compliance values range from [1, 9]
+       */
+      if (usb_version < 3) {
+        EXPECT_TRUE((int)warning <= (int)ComplianceWarning::MISSING_RP);
+      } else {
+        EXPECT_TRUE((int)warning <= (int)ComplianceWarning::UNRELIABLE_IO);
+      }
     }
   }
 
diff --git a/usb/apex/com.android.hardware.usb.avbpubkey b/usb/apex/com.android.hardware.usb.avbpubkey
deleted file mode 100644
index 0302d63..0000000
--- a/usb/apex/com.android.hardware.usb.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/usb/apex/com.android.hardware.usb.pem b/usb/apex/com.android.hardware.usb.pem
deleted file mode 100644
index e1e57da..0000000
--- a/usb/apex/com.android.hardware.usb.pem
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJKQIBAAKCAgEAwdimmHgIZHrep3H3YfVaNYEAGg45LUEPIiwHV6aIC9V7zjBS
-SftD30Z21jGyk7hmtas6WMI2vRBDNGrZWDgPeiEQoxXQinuU4Ug5S5X2F8LpWs0y
-ZeNFwQkqZwqGdQlkmy8upfb6T7rDxqRv+C0AtGP1r4r36+Xh5ld5stVaMK0UNhZt
-VW0nQAxyeJL3tm0zfiEA9Zu7FF2IyHm+bo9+eJ7WXfjiJfkclLgqlX3ec2cvVqAf
-NHisj18PEd/qtC64b+FnkgbsdHzWbo8HW5x4STkGXNnH+O3dvkWBX60MOfywfZFw
-+yaz5mt7K+ft/V4UA7zKiAEFM+J1lND9/UMJnd0XMYYtcRQF8lmu4dlcjfbbAm0k
-VgoUEsizIeMPLrMj837uVloKKzIXmPsVsfMarP/MrX6TJfzdUhdm01pVO1g0wtHJ
-J3eYQsEnOI7RjL+uZDQvPWAnr71pvacn66PAJC1UPulEEla5lhd30RDItbJkngXp
-3UggW32ZOQt3Oc8P0eo9SCnBlHtCVr8wfxAbxCoPR9qIdX3azkQRqcKqGbBbPnkc
-hSCzeIofUkYGibfbZg4k1yY82xEqZuN7J1zycoGP4wyhXeRLTRWxfPR5dxxmQZaS
-67A1LWrYvAzF8Rd44VMRlI/Qk6zuBsL01j2dfBqit+le+viQmTYb3BpV+1kCAwEA
-AQKCAgAmSfX2LddyiXaLWo6DsePkp5tuihqvHqevl0TIAmPi+oMe4hqO9GueoZt9
-iYl9djILdkvrFkmbpKexpd1SeJhOBlPz8q4jfG+W5B41GOToIp7XSarHx1GS5I2U
-ltaiLX3KzVIIhDVDJF/hT7+yJKl7+DaiOu/nj5vEVMj8EvpinP1eBaYI9quHEi5W
-NKlrRjyikEBRQzZ7ulH3T1zXF87iYnVzUGLTH1aO5aW7q4YSA3KtSKmBQsjK9PrU
-DAefGY9iwgIkLOvtwm7UnbnVVZ3I0NO56WZ/e/SNzcrVLCg7F/eAhgbsBOQKAnbs
-4D35CuknJ9ZVcOYnLncNMw7IRMKULKYLAuLLN1X33y22qaVxYA42rq13mZrijlua
-CMQ2Ur+GNcq8OI3mRDO38yKeJ5b4885LQdlrXXyoGnSjlkU5n8U9Jw6q2rZGiWlk
-4Q71g+KUl0rtXSnFSIJLNTK6Cd3ETStxswLvvCvfLTrRQcO8f2SdVxblmsc9eCDs
-JUxz6Sahkpb9hsY8fozu6laXC/5Ezy0TinRgGjQM/DQqbXtFXgse56mDxzSho5oh
-Spy3X7Q/v4VUtrSKsEZEIEVWCpplzVULpHenCDbU58rHyEcS7ew+kwlfHC73iEhX
-HPujSIKvStO7yCEeY6IdhON8iVX34uvQeAgEe4+rehQHLZUg0QKCAQEA9AS3imKF
-yEt0yNRLcdXSqYkak+OM2kfhBBcLndCT7Terr6yluv/SkPGYjUbmr2XiygMv8IwO
-8f+KbWsNwYCaB22HVYVGL0oUYAlCvWhnia3Iwn6ZZuXuJv5mmfqt/GMlaIfohNLy
-zI2OlcpcAuRfNlenjNyd+inxwdXL28Z86kbabnUlijgqpu4KFOYOlxbTRv93IlfM
-Ico1pZkLS1glDMFLetF+IWq4zqpXrdgNUk1KX3sofOCfZQnlWFrrHbXJPCgPAtlv
-xP4dmJQgtWkWwxUlelfz34LcCUVX2aTlgKjuvgvyCt2ZPWixXbDtjsCBTn3xBhoY
-kDp2OyMC+d543QKCAQEAy11GpYOQvTMKbV07EmN9jTRYg7gRrxsT3Kd4Xy+NpIY8
-v6J5Keeppk9t6WBrJi2cQU/EoHcd3fRkWMnWMNorZDiCu34VG5bfa4pTqnSLdLC4
-/e5UHdHqCy9deAwhlHYWbAx0KnxXWGxkq05dXvQsVuOtAs528NcujnLpwDONQt5P
-e3RIZmOOjr+7rqGp3vA9SuNOINOQpeKxQT6GRGw4mlYofdwOPaE1wCsO8vQCNmCJ
-DEfdm+hWjTLAV2IBCfi5BKRsIAXrABPzkzDeLGDmaQkJTDpK8UQcrFnqItGeo+yl
-fDjxA0zAPWYGcyXcXbtvayX+zCk/SKwQABqUtaumrQKCAQEA0mdizwsGqd8OMsCC
-0QP64j4a0Zvqbqh9yCYK2Sfo9SkEe7SVLnm5WUtIK8EP1fs3ItK+ul454MZj2Nbv
-BINbzL3PbJk/HDV2/hveFS154UgcjD/XC9eEktDXLTvuW2ot7kUJ48V0n5YLdPMI
-hWHfCx9nlFkCSptyHp23aqhqOyOe4pFWLikh9c/Yl46K1BJVWKmcUtt7Y0NVIJWn
-HG9Dew0MhTkv1aaM9X4Bnh9l1SpZz5yFG7AfIGL5A0dZ5cNCYgF0eBN+gVBPuqk2
-ztVvUATizOwblwThr4jAKCU70sVXHj10lZPftwiXrt6I54brt/92HLnRpkMSgQk+
-Xq9KbQKCAQAXxPM47UPBmXGijr8UyyQlmPSvkJggi12q8LgVCA3aKQZ4r5jR2Q3v
-LmF+YZKkh7g3ugcValbHVoVUC2NJmnZv5FsDZx04eE3s1+Inji+ul+lHZM/YHGzq
-mcKnAWP7YkIEpv/850OeRi0OCL7JFmkITtwt88vbIouCgtPnbx8XrbxEhbbgoMpM
-zQQ2yRZ9xD6lviOnmpLRkMl/ArvWy39iKqfY7huMAIezylSY+QQ5LtdV5CB21JUp
-M8FfdUkBzVxyunUY2Rg6jhpuHcwaC8lihXfcvQN9Z6SiUHAZWb7dEg/VkSI6bIIb
-qw0d8FLtcbb4IxzA6CFJcTL9kB3JjiKRAoIBAQC15t3mQHb9iCM4P4U9fpR4syvN
-46vDMhtj3vejerzOro2R7UUCJDvT59DrCQvtKO/ZCyhdTyuyResu6r1vbwq3KWiB
-i0RIeW87cKgJRr6w+KivB+a805WfI9zNRz778b7ajEpBkOs4vRPWu6S1306tdvgM
-Dhj7GT9UFh/k7pNuoSbiuaPUqgZRP55nzgj/FoIN985wnxo/ugckSqZ1bFGFXhYt
-zfIdFvPkf1BlLCnLTE8yESsJ3P37Gfj2XRv9h2I2/8qAGZniKtbVWHlu+5LDJf6V
-x9VpDAH2ZQAqRC3za3gfTjMsglYi7mUDeMYlB4osURNt7jDtElEmsto7AAkI
------END RSA PRIVATE KEY-----
diff --git a/usb/apex/com.android.hardware.usb.pk8 b/usb/apex/com.android.hardware.usb.pk8
deleted file mode 100644
index 9f3f39b..0000000
--- a/usb/apex/com.android.hardware.usb.pk8
+++ /dev/null
Binary files differ
diff --git a/usb/apex/com.android.hardware.usb.x509.pem b/usb/apex/com.android.hardware.usb.x509.pem
deleted file mode 100644
index 210c30d..0000000
--- a/usb/apex/com.android.hardware.usb.x509.pem
+++ /dev/null
@@ -1,34 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIF1TCCA70CFFEdsyLGoTRk+VIzqb6aDl1tXeuZMA0GCSqGSIb3DQEBCwUAMIGl
-MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
-bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEi
-MCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTEhMB8GA1UEAwwYY29t
-LmFuZHJvaWQuaGFyZHdhcmUudXNiMCAXDTIxMTExNzIyMzAwM1oYDzQ3NTkxMDE0
-MjIzMDAzWjCBpTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAU
-BgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNVBAsM
-B0FuZHJvaWQxIjAgBgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5jb20xITAf
-BgNVBAMMGGNvbS5hbmRyb2lkLmhhcmR3YXJlLnVzYjCCAiIwDQYJKoZIhvcNAQEB
-BQADggIPADCCAgoCggIBAM2E0E9ubNU/or7r9UIcFrC4l7CeM0HwtwSjTUKV1Z9K
-7rPFoUPE3cQ+cResnWQay8IGnomLYptAIMe8sLCC83LwU1ucTihxX87qq2W3V14w
-U4AkqDzNvYqKiD3yz9WofKxcu7ut8+1O4Pvp11X8UXuy5kNzf8WGpGB04k6U6KtA
-q8+U8+4h9h1Uvhaa0AeG9Yp22LzRLTb3J+eCoGHJnjoXYsd9T/gvedyXKkuf0lWB
-b3Aor3/CQrpdCW2/iJbuASdBdfilpQShaagxy2wmQsYxnT8ZWf+tIDYjg3xqqVPl
-GChFwCQBdaTuLI/k9gbaXkSxuzRkp5wc/ELhYbkhIS25yefAF2C6num++AHQBh1+
-qO0fHztsK80c5cVoDPWu17/nP7y3QloRyLFUrL3hVW1RQaFwE2Hmv4H0UwVAsleU
-ZIsz2ifTjiSl/tnkFTx0I6BVk7T87QhO3WXN4v6VDYZKeD4gQYS0NfwplahejrFw
-s3EcwKgt6f0KlIpzoEQBmNQBXxsRgL31GWCwCszb7+VrTMzgUpO41R3PyewbeaZk
-S/SHyEOwyf0WIvnZhZ/5CNd9qirClu6jS8kdLvwC2qA25VqSPw126EX1e2xUqm02
-C/6c7JDVocuQhvsJOnnpZt68Iwgw9g/xLCLA9RszH9ccRctZqRnzHB1AjTrBOq0P
-AgMBAAEwDQYJKoZIhvcNAQELBQADggIBAELbSot2Io/JZIYLTxgeReI4lk1KUzf8
-fGlDNlRm+goxOHXWQgvXgiftwM9IOB+H+EtNHAA9Q6ojAmfRe6rZC4p0ZxWOamtR
-V+pQj0c6Zvx8HJPMQdyoHx538iNXM093s2wnf+QuACb3BnvkK7uuLGAlIzWdImtL
-DKKFN05nppViY04tGP5HgT57b7YGwdkEp6euCJcyWIKjlyEH/bwTWM8ws/Px6uhw
-+5W2K7KrBsdRKPBF7qwXoS8Ak8pS5de9Xd7mbGBLaUtjsZ0pJbq0aFpuT0GbLWUm
-wiD5Ljq3ea/2GZxbHGiXQ2yNjFSd/jpuxDnnm99t7+HGw1v5Jld+hUVqXXfVNhWe
-hUKIv5TOk1nttNdsaLyDtxyt22JX7NnoPM0MqrkYwA8Xqrbv0VC8D/CVjiBC9Tce
-crhpCISNfQSkdEn/c+q/naFUvQy8oYqXkg1TjeGlcxwJOpGliYbbYT6VAwuI/ssI
-yX3Fkr8f5KhfN2aFnOpidknmLp9EyL2j5bxAtSD9xAHtczMn10uCUdLELjRB1L4f
-1qY+EjpIgK0NIFuEt9K5uZXirXq3K3eixKeJFNji3x/X8NgDALSdnT8JIlSg4DMg
-iWupLrQ9CSHMlgh5P43ALamiRIHQNqEwgj8OIGzsvQTSLbRjbPWYcDZa+Q1hosiA
-Fv7vjDI6oySM
------END CERTIFICATE-----
diff --git a/usb/apex/file_contexts b/usb/apex/file_contexts
deleted file mode 100644
index f223a56..0000000
--- a/usb/apex/file_contexts
+++ /dev/null
@@ -1,5 +0,0 @@
-(/.*)?                                         u:object_r:vendor_file:s0
-# Permission XMLs
-/etc/permissions(/.*)?                         u:object_r:vendor_configs_file:s0
-# binary
-/bin/hw/android\.hardware\.usb-service\.example        u:object_r:hal_usb_default_exec:s0
\ No newline at end of file
diff --git a/uwb/aidl/default/Android.bp b/uwb/aidl/default/Android.bp
index c6d1a52..f9b79de 100644
--- a/uwb/aidl/default/Android.bp
+++ b/uwb/aidl/default/Android.bp
@@ -15,11 +15,13 @@
     prefer_rlib: true,
     rustlibs: [
         "android.hardware.uwb-V1-rust",
+        "liblibc",
         "liblogger",
         "liblog_rust",
         "libbinder_rs",
         "libbinder_tokio_rs",
         "libtokio",
+        "libtokio_util",
         "libnix",
         "libanyhow",
     ],
@@ -46,23 +48,12 @@
     installable: false,
 }
 
-apex_key {
-    name: "com.android.hardware.uwb.key",
-    public_key: "com.android.hardware.uwb.avbpubkey",
-    private_key: "com.android.hardware.uwb.pem",
-}
-
-android_app_certificate {
-    name: "com.android.hardware.uwb.certificate",
-    certificate: "com.android.hardware.uwb",
-}
-
 apex {
     name: "com.android.hardware.uwb",
     manifest: "manifest.json",
     file_contexts: "file_contexts",
-    key: "com.android.hardware.uwb.key",
-    certificate: ":com.android.hardware.uwb.certificate",
+    key: "com.android.hardware.key",
+    certificate: ":com.android.hardware.certificate",
     updatable: false,
     vendor: true,
 
diff --git a/uwb/aidl/default/com.android.hardware.uwb.avbpubkey b/uwb/aidl/default/com.android.hardware.uwb.avbpubkey
deleted file mode 100644
index 7a7fce8..0000000
--- a/uwb/aidl/default/com.android.hardware.uwb.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/uwb/aidl/default/com.android.hardware.uwb.pem b/uwb/aidl/default/com.android.hardware.uwb.pem
deleted file mode 100644
index cd38ef8..0000000
--- a/uwb/aidl/default/com.android.hardware.uwb.pem
+++ /dev/null
@@ -1,52 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQChE5EVDbSAKAru
-myK22LP72i2ivpJGi8DqRjgA5r6Zz3OKwtvrtSBaTtACv3j6ZALLndA2sgxbt64B
-ik1vU1tpnrGnaqcdlgDio6QIfpavWEcZ9rOHuGXvLlv3Tzc2gyr1UKsaGtce+os8
-jbfyEl4keN59oI2D8Nj1VVKYRKS+mrp4+hnvt3itGbvIKLCgGBYlcSxIiWKDfJXy
-jQEpb5pZy3wCdn2CCrTb64oclkmYbGcfoYuSBV6+PBrGa2b0b5aWm40YFbgZ5h+j
-Hlkb7sX7Vz499DK1lbm8rRt+EkHZ/9IFS1DBSj6MK3LKyGspj7BzJ25UQI23B3pM
-iaNsjBR1gGwqJxWpjuuZm9qdNPMSUUSxs/EB0GX96jnC5HFjh3sLOWKqqP6SRCZW
-xYRzjAno1L1jjPXdDqM9n2aJHu35+YVG/sFn20eIBSxH9u1hSnOw8ccc0Zo774cc
-oTJHKlL9GUO5SEGLkz3XTMEe6e55yujChymMe5jz8jkkCaXqsNDB0AMUtQ6g0Kzu
-FNUdTS2DAr/Xo7qxJ6VoDwOx/OQeDQSjSuojt/JMHEp6QMir+/axVWuxIaegjElH
-Kmx7Ie6ZGZscaTxKFEYEkWaIeJKTml0NxEitrJAibc58itj93zBMP2EDve46ghWJ
-SlmeeVmn0CuYeE8EHwZSB4I7EaHP1QIDAQABAoICADQESZnu7xdj1wPu/Wrm2FjA
-wHQJ7trxTr9ZJcTEv1CUec+Z1cNsnqILSYlZpAvYOD7hG9hN70e/LWY573+//4lA
-Qka6XnVjd625AsPrfWXqsCtmS9vMZL60zeYzorTr7veBsX+go0/RwR0w9vIplFVa
-4x7Wtlyhbq7retzJbhpPhWCEA9Qx/7qG0Ol2mnNY0+4Lei2CkFm95f6KIpHrBIFz
-AP0anrVcF8PdcKCCuAmNGFBSruvc6Beu+UaScJEHaC4C1bGtceKLOjRHHKe0mCLu
-rZ78OVQCohSYIoS4CKQJxR0IkW+aNlC1x0BNMK4fRPArD6oNnrY4p/oHiMsJAaHc
-tLWeUqvlSAdK0C5ejUzdjTiZW7rzI2gluy+BBKY1nByQsVWK7saG6hEON63idfl+
-GwPPb253n49UIk3UlUy1PxVb/othlw0dlD7p4wAlOJoRlOuJnqAifwWckS2rpNHO
-9wR1tak8hLi2LMDJqu/GlHoSqkb+JdAV5yujLQ8k1So/y/4bvNY2pRpXPydxkOew
-FY/BOoslS6RD5/XHfSJsK/Oq53Y+d04qdV7jwgrp4lqFEqMwJgzVEoC5cMvJVeK9
-/wjUdWw9hg0DSk8LVmNbIWZjZs4C39uQljmt8s+exSmzm5MSQU3K11vGRGKOsPlA
-5ZzRRaV81lBYKhATE8aRAoIBAQDfPgJ3l/38RPyoNrJuhOPH4UJj0rDX0+5ezqla
-iAXDxec55lM8qbPDW3jl/MWE3LePVokKN8DBxwZxi9Po4UEQ4hG4+zIyPcT1AQlH
-+kHGG0TBtJox8J+DkG5oW79wXCn6tyl8ZLwWNa6T/3vIi3BNadnoe3AFqgcHVwHz
-e/tl0z6Tz56r/eNN6dl01+D31rirqiYRbe3982ZiX3JfS3t+AIQoxcU4suU+DC37
-G5h0zbzjgXP+ZrpPfh6O7JwH7Ac83nJAYBsVcHU4P/ZstkIWjuzleEQcDcsj3sHu
-NO2/wbReo/8Yvk0q2vUQm9IkWlU83H2KLPzIa1VuzTfPql0bAoIBAQC4tlP393ly
-krhmRLs6msAjXp5oZzoyHnrYvn+5LlKoPul98EqAW9GczLSlgjYO3w2DqnimBhU/
-YorWvkhcSJsrjiEn7CZ3UXuXoLciRiyHk/3xSDP4CLZ8tvUcEGs34LITXMPlrIk1
-WfYseSukE1TB6GDEj4MsxFILKhwnxGWeD9x9+ejOPj7+Jk3oTiZPVc67LR0MDgDz
-hRZW/8VOsequ+B/TF9LiwUHI3Ps3Geh3kKMR6DQaxXW08HDk1NGsZmKXq+O+JWFT
-3ZwCNnXqUCpm9YsD6QXTHILtBHVS5cxE65BFs08WEAqcgDhr3Yczz8L/UN/qR+FH
-RSOcymVFHQXPAoIBAQDK8Lsbbw+UGj64yGhysdnD5dINnwXmXiHPC/3Gb+sVqr3l
-060Nc5QYXvpL0PraKi+wXVFc+YwAXGZOKHfut38H0wubZreeFqsKsvN1/Fl4t2dM
-1FpsVbscxdqogedJRG9hHMrY61ZUtl5K6jDkAWaI6VYP0s7mR0f2czEx4B6M1XmI
-s3AiGD5foNtvLaS0iPz+CUJsC8wTVQZZHT2CxcKwq9V4nzkHrxFY04elQ9PXMwSo
-qRECTu7FvvgWo5/AT9/QhMPGI9fbKI1XIkZpU1JG4Y0XmboI6r0lkaYoXvNWo8fN
-VTZcjvrln4CypYRmSbw9BJAXYYg2xeQ3QtWesdfLAoIBAQCM9ifyjpvSQgITmdRA
-jySeWXEOP+j7oqMhkY+rZJyT5R8Pizdv6aJ3xQj/XfWfN736gzf7i5zfeHZ4F1Ll
-iktQ2/CVpPReDoMBXhckQuVsuhYL8owmd4+8cWtw9V69j+6WNC8Tsa4sVvE1U2to
-lZATQyHGH7d9jH0IJCTEfG8IRxZ/1R5DduFf1x+Rb0JxPQy9b1pBftZfAWvhDOQo
-gEKXMKgo0n+PqOhpP6s/i7gKtwibe9d3rsV7RhsBpyA0LxaCpRzyWViDRhXu4lzu
-aitR04U5gLV/PLz14Hcgwlo3JoY9iu+J6MgQUxG7z52EfsNTUQbwpdZYK31YBGVw
-bwulAoIBAQClsNAtbbwmExh3P0rjkgQOVy7FMQXgOyldR/LYTRI6Zbt47Jb4eO6M
-vfKK4C67/RoD1PEWgdboY8axPNj3dR1RdAuyTOOJfhJYVCW2eRS2fjSjYmUnLoFB
-N2ABYE8yKmmcPIlcJzXs3Jzeb+URxtDrvuEWPBHm8Ia1u29t42W7pg4AZHitilTl
-Su1Xi+Ic2n0NVzqZzLVUb6dKr2NFAjynthOCFXbbwD6awFeTxFIXVGT9TPTaiIlA
-Ssnh+yii4NHRoZjivUCsrfEfn+NiCzOgrfXTtUiRDvhZHdpqAIqQ6lpju7naCCwG
-vApJsLERxjCi0eqDwVx/e6xbeOVUZc/m
------END PRIVATE KEY-----
diff --git a/uwb/aidl/default/com.android.hardware.uwb.pk8 b/uwb/aidl/default/com.android.hardware.uwb.pk8
deleted file mode 100644
index 8855197..0000000
--- a/uwb/aidl/default/com.android.hardware.uwb.pk8
+++ /dev/null
Binary files differ
diff --git a/uwb/aidl/default/com.android.hardware.uwb.x509.pem b/uwb/aidl/default/com.android.hardware.uwb.x509.pem
deleted file mode 100644
index 762ad88..0000000
--- a/uwb/aidl/default/com.android.hardware.uwb.x509.pem
+++ /dev/null
@@ -1,34 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIF1TCCA70CFEagTJLwbnblSQu8MjFHXcEDFQ1MMA0GCSqGSIb3DQEBCwUAMIGl
-MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
-bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEi
-MCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTEhMB8GA1UEAwwYY29t
-LmFuZHJvaWQuaGFyZHdhcmUudXdiMCAXDTIzMDgyMTA0MTE0M1oYDzQ3NjEwNzE3
-MDQxMTQzWjCBpTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAU
-BgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNVBAsM
-B0FuZHJvaWQxIjAgBgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5jb20xITAf
-BgNVBAMMGGNvbS5hbmRyb2lkLmhhcmR3YXJlLnV3YjCCAiIwDQYJKoZIhvcNAQEB
-BQADggIPADCCAgoCggIBAJpvvE7i50v4hE5VmlFTiOsQ+zOYWF+MjzEMXcqLQDzs
-26XBYJV7O3TwgrQNp1XV8KCPTAF8APEyGb237rlZO/3KvS9zYa3zFfEMPg5pnFmR
-QLWSfqrXm7ttKIQHNVAjqrLnlt8pjvKS/t/UXLAWgHiXnd2ZX62tQjyDouszLEwP
-59zJbkgX9o6fwEssdRTpuyoRoyPylfK3Thk22RBSTt+pS6pwqeUj4vttrPr7k7YU
-S5CeB7XVBRPRzXFrpasgzQqp2deOpqxC44H90+pl7QmBHFrnGk+90lcOPYgqF2Ah
-DfCBxAHzn8ioSsAJ0U34yqoNbWIUX3ZHgq1ru1uctdLwGMrKmqsw4uvfXLV+sjoi
-qUFW+9uEOHAVnmZEO2T7LAlUIoAGgdd58EJ+hdXP2yvNpGVPestrwWttAaMJoEJN
-qEWt67+iEjdEpVn0lUFo+up42mYEXGUAOpoc6t5WJinrQTY4JK/OIGJPUiOjbUTI
-T/hQTddr0HNGsKP2zRv0d49VtVht6wR0HlO1srCFZAGxRIdBtjjenu1l7ALMWlHw
-4YXLSpQoeYfavV8C3j5bzz6KaDj6HcKvRbyHRX8+JL9kFDgpOD1yI/jaaPepRAe6
-qek5jEJMEFj0tFifB8e0CZbURreXa1tErWybfuDkaNBDtAHL333/IQGI0vV7zr0x
-AgMBAAEwDQYJKoZIhvcNAQELBQADggIBAFqnBngYd7rXHwIXHYKZuRMf+NLQxNFN
-f0m6ioFYgNnMwoMQidBHdRsJ2qTb9zfyt5O1/5Wel8Ix9aUCRbJejyX2lYXNIJNC
-ykahkoP+3CEA8QtPkmtWzNraDJRh2eYn6V5DJOWyvkz02DsHA4mxatYkF7dUfqLx
-f/y1y3cbnTyAraotTRb3W/F/zEYbcCzwlFZDT/IASVM89WRDE76+rp7/wDNJ0aEJ
-l6cQaUWDPbrvDWZaptRWfnebPtg81v03qKauwrBADddxu+/Nqs0iczBdsP1tdXr/
-Hxs47D7+fZnytVQglldBG4yky9YceL22yft/bCNDe5d7nF1/iUpJNbRIcrZxD86Z
-wwPpo0VMJ+r6VuD6UGQTS6Zyi94QD3GYYkGrPPwyrLKpp1EU6dV32bWCNp5eQojY
-FyTse/lfAJ9/Xu3klHYuR9xOaJiH+2MmtJTwKdjvK5h+EIiVbyrDJJHBd9sWav3a
-Emo+wKaIxQzXEUjWxUwy7eAAwq5WzawLMMQ97P0zIgasNIlUHW2cvBzVsQlM1tN7
-2t+7UPs5RifK5hadK6Ge1oqkG0xC1t65E7yGwrPMzKuz9aPgg7j1YAaETcKuFOHG
-rZr/0kALZc6VKNSZ8eM2P5ObrelLOe4ED9Ha1ZhmnCDXN9BP2gwqmCekwfNJ3cPA
-GObJiF81f9ZM
------END CERTIFICATE-----
diff --git a/uwb/aidl/default/src/uwb_chip.rs b/uwb/aidl/default/src/uwb_chip.rs
index 9587efb..2b8e481 100644
--- a/uwb/aidl/default/src/uwb_chip.rs
+++ b/uwb/aidl/default/src/uwb_chip.rs
@@ -4,32 +4,34 @@
 };
 use android_hardware_uwb::binder;
 use async_trait::async_trait;
-use binder::{Result, Strong};
+use binder::{DeathRecipient, IBinder, Result, Strong};
 
-use tokio::fs::{File, OpenOptions};
-use tokio::io::{AsyncReadExt, AsyncWriteExt};
+use std::sync::Arc;
+use tokio::io::unix::AsyncFd;
+use tokio::select;
 use tokio::sync::Mutex;
+use tokio_util::sync::CancellationToken;
 
+use std::fs::{File, OpenOptions};
+use std::io::{self, Read, Write};
 use std::os::fd::AsRawFd;
-
-use std::io;
-
-use nix::sys::termios;
+use std::os::unix::fs::OpenOptionsExt;
 
 enum State {
     Closed,
     Opened {
         callbacks: Strong<dyn IUwbClientCallback>,
-        #[allow(dead_code)]
-        tasks: tokio::task::JoinSet<()>,
+        handle: tokio::task::JoinHandle<()>,
         serial: File,
+        death_recipient: DeathRecipient,
+        token: CancellationToken,
     },
 }
 
 pub struct UwbChip {
     name: String,
     path: String,
-    state: Mutex<State>,
+    state: Arc<Mutex<State>>,
 }
 
 impl UwbChip {
@@ -37,23 +39,54 @@
         Self {
             name,
             path,
-            state: Mutex::new(State::Closed),
+            state: Arc::new(Mutex::new(State::Closed)),
         }
     }
 }
 
+impl State {
+    /// Terminate the reader task.
+    async fn close(&mut self) -> Result<()> {
+        if let State::Opened { ref mut token, ref callbacks, ref mut death_recipient, ref mut handle, .. } = *self {
+            log::info!("waiting for task cancellation");
+            callbacks.as_binder().unlink_to_death(death_recipient)?;
+            token.cancel();
+            handle.await.unwrap();
+            log::info!("task successfully cancelled");
+            callbacks.onHalEvent(UwbEvent::CLOSE_CPLT, UwbStatus::OK)?;
+            *self = State::Closed;
+        }
+        Ok(())
+    }
+}
+
 pub fn makeraw(file: File) -> io::Result<File> {
     let fd = file.as_raw_fd();
 
-    let mut attrs = termios::tcgetattr(fd)?;
-
-    termios::cfmakeraw(&mut attrs);
-
-    termios::tcsetattr(fd, termios::SetArg::TCSANOW, &attrs)?;
+    // Configure the file descritpro as raw fd.
+    use nix::sys::termios::*;
+    let mut attrs = tcgetattr(fd)?;
+    cfmakeraw(&mut attrs);
+    tcsetattr(fd, SetArg::TCSANOW, &attrs)?;
 
     Ok(file)
 }
 
+/// Wrapper around Read::read to handle EWOULDBLOCK.
+/// /!\ will actively wait for more data, make sure to call
+/// this method only when data is immediately expected.
+fn read_exact(file: &mut File, mut buf: &mut [u8]) -> io::Result<()> {
+    while buf.len() > 0 {
+        match file.read(buf) {
+            Ok(0) => panic!("unexpectedly reached end of file"),
+            Ok(read_len) => buf = &mut buf[read_len..],
+            Err(err) if err.kind() == io::ErrorKind::WouldBlock => continue,
+            Err(err) => return Err(err),
+        }
+    }
+    Ok(())
+}
+
 impl binder::Interface for UwbChip {}
 
 #[async_trait]
@@ -65,60 +98,113 @@
     async fn open(&self, callbacks: &Strong<dyn IUwbClientCallback>) -> Result<()> {
         log::debug!("open: {:?}", &self.path);
 
+        let mut state = self.state.lock().await;
+
+        if matches!(*state, State::Opened { .. }) {
+            log::error!("the state is already opened");
+            return Err(binder::ExceptionCode::ILLEGAL_STATE.into());
+        }
+
         let serial = OpenOptions::new()
             .read(true)
             .write(true)
             .create(false)
+            .custom_flags(libc::O_NONBLOCK)
             .open(&self.path)
-            .await
             .and_then(makeraw)
             .map_err(|_| binder::StatusCode::UNKNOWN_ERROR)?;
 
-        let mut state = self.state.lock().await;
+        let state_death_recipient = self.state.clone();
+        let mut death_recipient = DeathRecipient::new(move || {
+            let mut state = state_death_recipient.blocking_lock();
+            log::info!("Uwb service has died");
+            if let State::Opened { ref mut token, .. } = *state {
+                token.cancel();
+                *state = State::Closed;
+            }
+        });
 
-        if let State::Closed = *state {
-            let client_callbacks = callbacks.clone();
+        callbacks.as_binder().link_to_death(&mut death_recipient)?;
 
-            let mut tasks = tokio::task::JoinSet::new();
-            let mut reader = serial
-                .try_clone()
-                .await
-                .map_err(|_| binder::StatusCode::UNKNOWN_ERROR)?;
+        let token = CancellationToken::new();
+        let cloned_token = token.clone();
 
-            tasks.spawn(async move {
-                loop {
-                    const UWB_HEADER_SIZE: usize = 4;
+        let client_callbacks = callbacks.clone();
 
-                    let mut buffer = vec![0; UWB_HEADER_SIZE];
-                    reader
-                        .read_exact(&mut buffer[0..UWB_HEADER_SIZE])
-                        .await
-                        .unwrap();
+        let reader = serial
+            .try_clone()
+            .map_err(|_| binder::StatusCode::UNKNOWN_ERROR)?;
 
-                    let length = buffer[3] as usize + UWB_HEADER_SIZE;
+        let join_handle = tokio::task::spawn(async move {
+            log::info!("UCI reader task started");
+            let mut reader = AsyncFd::new(reader).unwrap();
 
-                    buffer.resize(length, 0);
-                    reader
-                        .read_exact(&mut buffer[UWB_HEADER_SIZE..length])
-                        .await
-                        .unwrap();
+            loop {
+                const UWB_HEADER_SIZE: usize = 4;
+                let mut buffer = vec![0; UWB_HEADER_SIZE];
 
-                    client_callbacks.onUciMessage(&buffer[..]).unwrap();
-                }
-            });
+                // The only time where the task can be safely
+                // cancelled is when no packet bytes have been read.
+                //
+                // - read_exact() cannot be used here since it is not
+                //   cancellation safe.
+                // - read() cannot be used because it cannot be cancelled:
+                //   the syscall is executed blocking on the threadpool
+                //   and completes after termination of the task when
+                //   the pipe receives more data.
+                let read_len = loop {
+                    // On some platforms, the readiness detecting mechanism
+                    // relies on edge-triggered notifications. This means that
+                    // the OS will only notify Tokio when the file descriptor
+                    // transitions from not-ready to ready. For this to work
+                    // you should first try to read or write and only poll for
+                    // readiness if that fails with an error of
+                    // std::io::ErrorKind::WouldBlock.
+                    match reader.get_mut().read(&mut buffer) {
+                        Ok(0) => {
+                            log::error!("file unexpectedly closed");
+                            return;
+                        }
+                        Ok(read_len) => break read_len,
+                        Err(err) if err.kind() == io::ErrorKind::WouldBlock => (),
+                        Err(_) => panic!("unexpected read failure"),
+                    }
 
-            callbacks.onHalEvent(UwbEvent::OPEN_CPLT, UwbStatus::OK)?;
+                    let mut guard = select! {
+                        _ = cloned_token.cancelled() => {
+                            log::info!("task is cancelled!");
+                            return;
+                        },
+                        result = reader.readable() => result.unwrap()
+                    };
 
-            *state = State::Opened {
-                callbacks: callbacks.clone(),
-                tasks,
-                serial,
-            };
+                    guard.clear_ready();
+                };
 
-            Ok(())
-        } else {
-            Err(binder::ExceptionCode::ILLEGAL_STATE.into())
-        }
+                // Read the remaining header bytes, if truncated.
+                read_exact(reader.get_mut(), &mut buffer[read_len..]).unwrap();
+
+                let length = buffer[3] as usize + UWB_HEADER_SIZE;
+                buffer.resize(length, 0);
+
+                // Read the payload bytes.
+                read_exact(reader.get_mut(), &mut buffer[UWB_HEADER_SIZE..]).unwrap();
+
+                client_callbacks.onUciMessage(&buffer).unwrap();
+            }
+        });
+
+        callbacks.onHalEvent(UwbEvent::OPEN_CPLT, UwbStatus::OK)?;
+
+        *state = State::Opened {
+            callbacks: callbacks.clone(),
+            handle: join_handle,
+            serial,
+            death_recipient,
+            token,
+        };
+
+        Ok(())
     }
 
     async fn close(&self) -> Result<()> {
@@ -126,10 +212,8 @@
 
         let mut state = self.state.lock().await;
 
-        if let State::Opened { ref callbacks, .. } = *state {
-            callbacks.onHalEvent(UwbEvent::CLOSE_CPLT, UwbStatus::OK)?;
-            *state = State::Closed;
-            Ok(())
+        if matches!(*state, State::Opened { .. }) {
+            state.close().await
         } else {
             Err(binder::ExceptionCode::ILLEGAL_STATE.into())
         }
@@ -162,7 +246,6 @@
         if let State::Opened { ref mut serial, .. } = &mut *self.state.lock().await {
             serial
                 .write(data)
-                .await
                 .map(|written| written as i32)
                 .map_err(|_| binder::StatusCode::UNKNOWN_ERROR.into())
         } else {
diff --git a/vibrator/OWNERS b/vibrator/OWNERS
index 62a567e..c4de58a 100644
--- a/vibrator/OWNERS
+++ b/vibrator/OWNERS
@@ -1,8 +1,2 @@
 # Bug component: 345036
-
 include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS
-
-chrispaulo@google.com
-michaelwr@google.com
-nathankulczak@google.com
-taikuo@google.com
diff --git a/vibrator/aidl/default/Android.bp b/vibrator/aidl/default/Android.bp
index 78bb4ed..fb71a82 100644
--- a/vibrator/aidl/default/Android.bp
+++ b/vibrator/aidl/default/Android.bp
@@ -32,9 +32,11 @@
     },
 }
 
-filegroup {
+prebuilt_etc {
     name: "android.hardware.vibrator.xml",
-    srcs: ["android.hardware.vibrator.xml"],
+    src: "android.hardware.vibrator.xml",
+    sub_dir: "vintf",
+    installable: false,
 }
 
 cc_binary {
diff --git a/vibrator/aidl/default/apex/Android.bp b/vibrator/aidl/default/apex/Android.bp
index 7949057..39626bf 100644
--- a/vibrator/aidl/default/apex/Android.bp
+++ b/vibrator/aidl/default/apex/Android.bp
@@ -2,17 +2,6 @@
     default_applicable_licenses: ["hardware_interfaces_license"],
 }
 
-apex_key {
-    name: "com.android.hardware.vibrator.key",
-    public_key: "com.android.hardware.vibrator.avbpubkey",
-    private_key: "com.android.hardware.vibrator.pem",
-}
-
-android_app_certificate {
-    name: "com.android.hardware.vibrator.certificate",
-    certificate: "com.android.hardware.vibrator",
-}
-
 prebuilt_etc {
     name: "com.android.hardware.vibrator.rc",
     src: "com.android.hardware.vibrator.rc",
@@ -22,20 +11,19 @@
 apex {
     name: "com.android.hardware.vibrator",
     manifest: "apex_manifest.json",
-    key: "com.android.hardware.vibrator.key",
-    certificate: ":com.android.hardware.vibrator.certificate",
+    key: "com.android.hardware.key",
+    certificate: ":com.android.hardware.certificate",
     file_contexts: "file_contexts",
-    use_vndk_as_stable: true,
     updatable: false,
-    // Install the apex in /vendor/apex
-    soc_specific: true,
+    vendor: true,
+
     binaries: [
         "android.hardware.vibrator-service.example",
     ],
     prebuilts: [
         "com.android.hardware.vibrator.rc",
+        "android.hardware.vibrator.xml",
     ],
-    vintf_fragments: [":android.hardware.vibrator.xml"],
     // vibrator.default.so is not needed by the AIDL service binary.
     overrides: ["vibrator.default"],
 }
diff --git a/vibrator/aidl/default/apex/com.android.hardware.vibrator.avbpubkey b/vibrator/aidl/default/apex/com.android.hardware.vibrator.avbpubkey
deleted file mode 100644
index a6ca630..0000000
--- a/vibrator/aidl/default/apex/com.android.hardware.vibrator.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/vibrator/aidl/default/apex/com.android.hardware.vibrator.pem b/vibrator/aidl/default/apex/com.android.hardware.vibrator.pem
deleted file mode 100644
index c0f5c50..0000000
--- a/vibrator/aidl/default/apex/com.android.hardware.vibrator.pem
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJKAIBAAKCAgEAnIEo7HGpVc62cFPmwT3MOiHQANHVbawHpbp//x3xK7acK6So
-rUu8XJnsO0kAA/Vwtfqqo5GgmBVUr4ahW+MJ/W/X7NP5hAetQWQcGFc0Yhqmoi1g
-vTDRon2i6+mhfUFJqqy8t3vLFehqgkQCe8gGiEVl43+PODtfIae+AaPbTl8R9ErQ
-l3ESBfRElkMQo2beC7e8k5joLEA3q85Q7rdSFUdhoUSXMVRHmBVJxezYI/3SLFtj
-7ULKaGtOgUFlj5R9akr9y8+sG9NBJI5HJZgqz46l+ZuNkdkPlDK6arD5eL9lkpxt
-GeXhtwBPdgZIuSTe/tFeYU65MmwLxVnStDJ67k3AFKC0Zg7ISVemCRofSOEhyUm3
-DoB1G9UiBj3pNmhwPNYiohW3pFZuLgBArAT8nTi4txmCBMNStz7kNVW8sv8nlBEz
-D+nOuoB8tGAF7+fsaBKVQI6yJd2tgkpRM5E8NNrLYL+ignRztzfHjNDtw5Xn4QHA
-Ds1GrNqq25uBxvrH5cDa2HFvZryuejlzvGp6YAkPUwZrYJ/ij7i+98EvXowgED/7
-gFnXmkkyW1LQ0BUxJtKKEjnSno8leGbcospSUDt8uiBF7Vnn3atokRpl6rr2k0qa
-5ELx+vrQT+LUUo0MuZkAW9qKfYLBF8UOCiK1Ytvs9bxK3Jb93E+lJAe/HQkCAwEA
-AQKCAgADnZA+dhm9W7snOSj5id3v8dwGSNKvZ+v9TiOq1xw9MEjHUVR8PGWrlfq5
-G+SeMstZyOKsSK73FHcSXv/XSZVvf2fzlqoK/Mpp2lAz17/kDE2RLY8wj7IoGNLs
-tEcAx8NV6AusCXYVmXrsa3nLNkHAYCoMaWP7npOCCYgALbLhSpz1kczj0r7h2FTF
-S+NUgwnaJ3J5zmx+qTUgCPIhsaZ5y15cBWOgxhupTcSYh/IuUqzKTYovbv2SD/iO
-T95yxLFpBTZ7wN5u/iBhIdBO9Ab5KIh5Dbjlh6guekWINXJt8a39BxQWJxNh0OYF
-CfwgGtPz+w49HT52Bbz34C1X8FqaoXxJUHaJ7e83Y/1qzENNPOsmdscMuzrjlVox
-gyQPIS5HzASD2NktnShwE2QUMhZcovkPIhFxos7JDMvOTXf6LVMXKWGa4HZqb4Z2
-dBp/bZzuV+OOHXq9emCkRwO5aor2qfefSf+xcycDWzaWTJfvnEXulFXYtqiGOEL7
-hyvr38Tll6dOLO8KkwvHaf51wZl/jCTo+7akdpokUh0Klg3/KKRiaScHQotkrVuD
-MGL+kWSZqZ6sZKs8z3xh4oj2d6P1qHEeu85+DY/GyHJ2Ek4athcT0jmqb4A/0Tq9
-1zr5IXo+ps20YjW5bFvXbvKVZYggsJyacw6WhTFD9eN8fn+UoQKCAQEAzwrs/QWv
-yWoWLOaF39bL6JSdq8juynswdb2NjcHV/b7dzhgf0aDnA6WtJcHlfRcnA8haeoEQ
-n0qzPAirexz2AtWfJm41gYTqWTwaaNkbwxGMMvLV/IQebk3pHdAdONFYpLloJvlt
-4ys8W1gdMKwzSRvR4VPYwIfIqb/vYxZhme0JBF0X5iPFkID9Q1cJeaRx9PhCvX4o
-LBb6impUkeTIhxbGgbuudGyhcyvKrPdcx1ts69r6NOme2hvBhrbZGVaUEtlHvEu0
-1JvNvPJyK2XvHI3EtERzjUPm3s+Gh5REvdXXaz1GC4HUSFZkOG8/HgSZoEYVkSJH
-QoCnfXc4VG4jrQKCAQEAwYL4KvpltG85DNoYava71adQfYwitQah8omGU+dqWjjQ
-m8WLKo1cjEO6tfIp7UFSz4mJvwhxj9aqdwu2RyGoeZHKOhxluZIH9mcoPggL7Kgj
-xEJfkwy5zbReujM71n5FOhR2zOltXXa5YrN9983fZeZK8FRchEBDwyUf73dkwRri
-uvyY793OIqYjuJXO/9dtSyK1jEmDUTLoquM610RLLK4j8hXQ9C/sMlDfHzlUzXff
-ZsvWL5U4D3e6cL55cP7lr8cYR1z+AcZsjd7eNlNXO1v4o50B7bOayr7/zsTlfXss
-ZoP7yJcYeXEpcIx5KPS44CAaDeZfQkOFcz7DzFQqTQKCAQEAx2aQZAdsC6GehdPm
-r3PhorgvOlkkkeIfA+ZxREug2udOG8VkL7K1iu+vWKPrb5QywRPfAAj5h1CcWn9H
-GCUGUiiHRK3z3i+yvAqErOIcOLzXt+HkcXSVEkr67vmWizgkFVFzm8WyLY1gbeDp
-DA1sv0aJ1me4Y4Tin4n49geCLIr7mjZGZCGjjs6MHKTgvUTBc9r9/B5adkwTM+fA
-V1puPpySxjOJixtsSs2sPvVlZ6MHvgeB3h/6G7mLo0DKyfp2Vcjpq9GF8RW1Cfq+
-NknQBkILZkpet3jkC0b3G/CSW/ptpBy5Ly/00U5S639I3JI1mwSklMjctJHPvahq
-mfYRaQKCAQAnMzzKmAbaUl2gON4RbQIH+ejYRfcR7NIJq8pGXO6ycCfyJkZWzGQf
-FelQykmsAjugRyBcTn2Swc2uZ/T429yhI+NveikxOl/ajnMcfczMmBMGwttRkpZh
-EVTPK2nHvbSQW2zlfbPl5xMO54VxGYdTwR8VKEHFmK8hbPfXLrx+Uc/0SQ9CKBCF
-/FnoHpDcSuuc+N8GGC492K5BT96vlOoVlwE5HSpDDSIv3yoTzS1cohfjXw94fCXr
-HDnsdOls9nXY8d/9NN1Pxr5ezvL81k0pfSwVGM03Ndb5k0+Gt2Q10ynfaoUq0VDn
-6QCYCBzTKx/4ZwhgIHbTmZIDEoffcH1RAoIBACIpqVGAa2/t3c0BCN7PLPGIsC/w
-5YwxqVNUM0FK220RoO0vbSEGvba8NJyBKSaokgPwhmNlmVE+r17B78oLfP8GFtAx
-Jz52WkjVULNb07WSIHCUxeoNZf9qEANdvfMwW4effjkrmxEtVLHdGC72Lg+am30s
-QaJqLmKsRZeE6Js2+ZXeRKoXN8wLLGp/CDVnDdLUEK6SdsujI4jXEq3DT+9eGh6X
-mXl7vWFzbPrAGcHM4Ad8uY2BHznCGxvJn4E4u+EH+l4OX334Q4gM+gOIRt0xkHVG
-9OXFRrjPVCWZCH1dOdkC5DomKCgyKBHLObtBv3dHXXDtEbeZ8fGbJFuoBhA=
------END RSA PRIVATE KEY-----
diff --git a/vibrator/aidl/default/apex/com.android.hardware.vibrator.pk8 b/vibrator/aidl/default/apex/com.android.hardware.vibrator.pk8
deleted file mode 100644
index d20cc33..0000000
--- a/vibrator/aidl/default/apex/com.android.hardware.vibrator.pk8
+++ /dev/null
Binary files differ
diff --git a/vibrator/aidl/default/apex/com.android.hardware.vibrator.x509.pem b/vibrator/aidl/default/apex/com.android.hardware.vibrator.x509.pem
deleted file mode 100644
index 993245b..0000000
--- a/vibrator/aidl/default/apex/com.android.hardware.vibrator.x509.pem
+++ /dev/null
@@ -1,34 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIF3zCCA8cCFBJmLB9vGtl8nENMVa8g51Y2vRhPMA0GCSqGSIb3DQEBCwUAMIGq
-MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
-bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEi
-MCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTEmMCQGA1UEAwwdY29t
-LmFuZHJvaWQuaGFyZHdhcmUudmlicmF0b3IwIBcNMjEwOTE2MTcyOTA5WhgPNDc1
-OTA4MTMxNzI5MDlaMIGqMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5p
-YTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4G
-A1UECwwHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNv
-bTEmMCQGA1UEAwwdY29tLmFuZHJvaWQuaGFyZHdhcmUudmlicmF0b3IwggIiMA0G
-CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDV857uzUSXWQNWIuiNRwt1zd/eSB8P
-iVNq0qTexlZvamXr6h5VwX45iJeDVCxlx3uktXXP2NH7U01A5gl8Hix6iodNe3sJ
-fFIhzUiqdMfAn+6RWcJcb3v6J58D6R+htgtaSgv8WOLkIZyhbmU3NToc7dNe2j0U
-wh6xfYhpj/s0RManSTZW19C2H8g5eNfhEZgDT+KOUIgepv/x6Y5IR147JPh8Ha7g
-vxM87ceErSvr3e8uXDWUZtQ6IDfF2NkxJJGJos4IAteXbkG60q76V8pmWLCqIsMM
-tRcIpTEJUIbnbxAqSu+crZqQowu9HrJMYnqunlmXASeluxXdl8VKOVNMZHy3ipj7
-HjoTUJoiEVDLYeT7db76k2lDFH/JRtnoe3BBinUEKvGT3rOjy55C4E2DSMSM1Laz
-zkRcJ4hlzFQLXD5/iwWgW6me1lmnOEqFJZolc1fEc+VfEdZdwJmZF6Clm5av2hDm
-Oq09qL02nXy0OyAoCI6IcrrAlFFolgel32nWp1R7c+N2+6vLMP3KR50TgSiwHNNQ
-weZhpP1GrXDyVj+ilL5T/2ionvjIvDBgOi8B0IwiqeHY7lqsSyMOuKTi5WPrJ86E
-GNJ+PlivA/P9MAatem4kzCT2t3DbVC+dtybiUAmVFb2Ls+dVK4nHcbGTW9AuBijD
-COEHQgi4Xs6lnwIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQDGvq99QUxMh+DaI2Pd
-s4fQ9MmYxGDxULhjqgGAjDbL3jQMG2FxPTu1Z2VJgg4n+PTNsgsqgn1JgQM3gvFp
-8FKhoxtzM5V8pPxphCG7U/f3ZsmXdLl69sbVkMRhorhQ8H54q0O/T3Ig/ULZgblE
-xCRT1REB693tUQOCYgWgnsOpvySfujYhNBirl48Hw9zrRmQNTsO20dKwkZUvkVow
-/pawucYrHibgwgKWz8kB3Dl4DODiLybek0hGzr59Joul9eMsxTuQsHAIUv0KO2Ny
-5WT7GIX6qYc+VrnXsO+PHtx5GTUjrJiue3aggoW6X7gu3Z6KuIOiVTVLVp6wSJtt
-VHv90HTu2lYxtPyPSOpFfwFOTicN+5VmLTQwPvPRqsnCaYc+K2iWyEhN/PnSfFNc
-t/TX9HT3ljXq9yfshQmQJ27pdUiYs9Avt7fEXpJjQ0Tn9w8jRS5gsrnTUXTG6HXf
-I4lsMSAApFZa112PwU7xAIIaipBauuMjQCabD/thBzB6d29Rlbz3cjBzoky9h2vb
-XNIVo5O2Jiz7OJQ/7mubvJqIBngiaDK78n2hSdYglI1hgcf0KaQIJUridzmjt0kp
-xXcwIz7nJxhNpbsYnDnqwqz9en8a4N+KeoQleYROo2kEtE434AJkzdABV4IKRafj
-cbPWuY6F2faWAjkSOEhBfGOKOw==
------END CERTIFICATE-----
diff --git a/vibrator/aidl/default/apex/file_contexts b/vibrator/aidl/default/apex/file_contexts
index f811656..f061caa 100644
--- a/vibrator/aidl/default/apex/file_contexts
+++ b/vibrator/aidl/default/apex/file_contexts
@@ -1,3 +1,4 @@
-(/.*)? 							u:object_r:vendor_file:s0
-/bin/hw/android\.hardware\.vibrator-service\.example	u:object_r:hal_vibrator_default_exec:s0
+(/.*)?                                                  u:object_r:vendor_file:s0
+/etc(/.*)?                                              u:object_r:vendor_configs_file:s0
+/bin/hw/android\.hardware\.vibrator-service\.example    u:object_r:hal_vibrator_default_exec:s0
 
diff --git a/wifi/aidl/Android.bp b/wifi/aidl/Android.bp
index 7bc8ae7..ac95f85 100644
--- a/wifi/aidl/Android.bp
+++ b/wifi/aidl/Android.bp
@@ -27,6 +27,9 @@
     srcs: [
         "android/hardware/wifi/*.aidl",
     ],
+    imports: [
+        "android.hardware.wifi.common-V1",
+    ],
     stability: "vintf",
     backend: {
         java: {
@@ -42,6 +45,9 @@
                 enabled: false,
             },
         },
+        cpp: {
+            enabled: false,
+        },
     },
     versions_with_info: [
         {
@@ -49,6 +55,5 @@
             imports: [],
         },
     ],
-    frozen: true,
-
+    frozen: false,
 }
diff --git a/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/IWifiChip.aidl b/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/IWifiChip.aidl
index 4ea2081..2d7fe03 100644
--- a/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/IWifiChip.aidl
+++ b/wifi/aidl/aidl_api/android.hardware.wifi/current/android/hardware/wifi/IWifiChip.aidl
@@ -83,6 +83,7 @@
   void triggerSubsystemRestart();
   void enableStaChannelForPeerNetwork(in int channelCategoryEnableFlag);
   void setMloMode(in android.hardware.wifi.IWifiChip.ChipMloMode mode);
+  @PropagateAllowBlocking android.hardware.wifi.IWifiApIface createApOrBridgedApIface(in android.hardware.wifi.IfaceConcurrencyType iface, in android.hardware.wifi.common.OuiKeyedData[] vendorData);
   const int NO_POWER_CAP_CONSTANT = 0x7FFFFFFF;
   @Backing(type="int") @VintfStability
   enum FeatureSetMask {
diff --git a/wifi/aidl/android/hardware/wifi/IWifiChip.aidl b/wifi/aidl/android/hardware/wifi/IWifiChip.aidl
index c1caa7e..733ff62 100644
--- a/wifi/aidl/android/hardware/wifi/IWifiChip.aidl
+++ b/wifi/aidl/android/hardware/wifi/IWifiChip.aidl
@@ -33,6 +33,7 @@
 import android.hardware.wifi.WifiIfaceMode;
 import android.hardware.wifi.WifiRadioCombination;
 import android.hardware.wifi.WifiUsableChannel;
+import android.hardware.wifi.common.OuiKeyedData;
 
 /**
  * Interface that represents a chip that must be configured as a single unit.
@@ -1149,4 +1150,27 @@
      *
      */
     void setMloMode(in ChipMloMode mode);
+
+    /**
+     * Create an AP or bridged AP iface on the chip using vendor-provided configuration parameters.
+     *
+     * Depending on the mode the chip is configured in, the interface creation
+     * may fail (code: |WifiStatusCode.ERROR_NOT_AVAILABLE|) if we've already
+     * reached the maximum allowed (specified in |ChipIfaceCombination|) number
+     * of ifaces of the AP or AP_BRIDGED type.
+     *
+     * @param  iface IfaceConcurrencyType to be created. Takes one of
+               |IfaceConcurrencyType.AP| or |IfaceConcurrencyType.AP_BRIDGED|
+     * @param  vendorData Vendor-provided configuration data as a list of |OuiKeyedData|.
+     * @return AIDL interface object representing the iface if
+     *         successful, null otherwise.
+     * @throws ServiceSpecificException with one of the following values:
+     *         |WifiStatusCode.ERROR_WIFI_CHIP_INVALID|,
+     *         |WifiStatusCode.ERROR_NOT_SUPPORTED|,
+     *         |WifiStatusCode.ERROR_NOT_AVAILABLE|,
+     *         |WifiStatusCode.ERROR_INVALID_ARGS|
+     */
+    @PropagateAllowBlocking
+    IWifiApIface createApOrBridgedApIface(
+            in IfaceConcurrencyType iface, in OuiKeyedData[] vendorData);
 }
diff --git a/wifi/aidl/android/hardware/wifi/NanDiscoveryCommonConfig.aidl b/wifi/aidl/android/hardware/wifi/NanDiscoveryCommonConfig.aidl
index 58777c5..4bedce0 100644
--- a/wifi/aidl/android/hardware/wifi/NanDiscoveryCommonConfig.aidl
+++ b/wifi/aidl/android/hardware/wifi/NanDiscoveryCommonConfig.aidl
@@ -73,8 +73,9 @@
     /**
      * Arbitrary information communicated in discovery packets - there is no semantic meaning to
      * these bytes. They are passed-through from publisher to subscriber as-is with no parsing. Max
-     * length: |NanCapabilities.maxExtendedServiceSpecificInfoLen|. Spec: Service Descriptor
-     * Extension Attribute (SDEA) / Service Info
+     * length: |NanCapabilities.maxExtendedServiceSpecificInfoLen|. This info is using Generic
+     * Service Protocol with setting Service Info type to 2 (Generic). NAN Spec: Service
+     * Descriptor Extension Attribute (SDEA) / Service Info
      */
     byte[] extendedServiceSpecificInfo;
     /**
diff --git a/wifi/aidl/default/Android.bp b/wifi/aidl/default/Android.bp
index 91d609d..31a3531 100644
--- a/wifi/aidl/default/Android.bp
+++ b/wifi/aidl/default/Android.bp
@@ -105,7 +105,7 @@
         "libwifi-hal",
         "libwifi-system-iface",
         "libxml2",
-        "android.hardware.wifi-V1-ndk",
+        "android.hardware.wifi-V2-ndk",
     ],
 
     export_include_dirs: ["."],
@@ -132,7 +132,7 @@
         "libwifi-hal",
         "libwifi-system-iface",
         "libxml2",
-        "android.hardware.wifi-V1-ndk",
+        "android.hardware.wifi-V2-ndk",
     ],
     static_libs: ["android.hardware.wifi-service-lib"],
     init_rc: ["android.hardware.wifi-service.rc"],
@@ -161,7 +161,7 @@
         "libwifi-hal",
         "libwifi-system-iface",
         "libxml2",
-        "android.hardware.wifi-V1-ndk",
+        "android.hardware.wifi-V2-ndk",
     ],
     static_libs: ["android.hardware.wifi-service-lib"],
     init_rc: ["android.hardware.wifi-service-lazy.rc"],
@@ -192,7 +192,8 @@
     static_libs: [
         "libgmock",
         "libgtest",
-        "android.hardware.wifi-V1-ndk",
+        "android.hardware.wifi-V2-ndk",
+        "android.hardware.wifi.common-V1-ndk",
         "android.hardware.wifi-service-lib",
     ],
     shared_libs: [
diff --git a/wifi/aidl/default/android.hardware.wifi-service.xml b/wifi/aidl/default/android.hardware.wifi-service.xml
index 5398ee7..3b68c8e 100644
--- a/wifi/aidl/default/android.hardware.wifi-service.xml
+++ b/wifi/aidl/default/android.hardware.wifi-service.xml
@@ -1,6 +1,7 @@
 <manifest version="1.0" type="device">
 	<hal format="aidl">
 		<name>android.hardware.wifi</name>
+		<version>2</version>
 		<fqname>IWifi/default</fqname>
 	</hal>
 </manifest>
diff --git a/wifi/aidl/default/wifi.cpp b/wifi/aidl/default/wifi.cpp
index 255266b..12017b6 100644
--- a/wifi/aidl/default/wifi.cpp
+++ b/wifi/aidl/default/wifi.cpp
@@ -18,14 +18,151 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <cutils/properties.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
 
 #include "aidl_return_util.h"
 #include "aidl_sync_util.h"
 #include "wifi_status_util.h"
 
 namespace {
+using android::base::unique_fd;
+
 // Starting Chip ID, will be assigned to primary chip
 static constexpr int32_t kPrimaryChipId = 0;
+constexpr char kCpioMagic[] = "070701";
+constexpr char kTombstoneFolderPath[] = "/data/vendor/tombstones/wifi/";
+
+// Helper function for |cpioArchiveFilesInDir|
+bool cpioWriteHeader(int out_fd, struct stat& st, const char* file_name, size_t file_name_len) {
+    const int buf_size = 32 * 1024;
+    std::array<char, buf_size> read_buf;
+    ssize_t llen = snprintf(
+            read_buf.data(), buf_size, "%s%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X",
+            kCpioMagic, static_cast<int>(st.st_ino), st.st_mode, st.st_uid, st.st_gid,
+            static_cast<int>(st.st_nlink), static_cast<int>(st.st_mtime),
+            static_cast<int>(st.st_size), major(st.st_dev), minor(st.st_dev), major(st.st_rdev),
+            minor(st.st_rdev), static_cast<uint32_t>(file_name_len), 0);
+    if (write(out_fd, read_buf.data(), llen < buf_size ? llen : buf_size - 1) == -1) {
+        PLOG(ERROR) << "Error writing cpio header to file " << file_name;
+        return false;
+    }
+    if (write(out_fd, file_name, file_name_len) == -1) {
+        PLOG(ERROR) << "Error writing filename to file " << file_name;
+        return false;
+    }
+
+    // NUL Pad header up to 4 multiple bytes.
+    llen = (llen + file_name_len) % 4;
+    if (llen != 0) {
+        const uint32_t zero = 0;
+        if (write(out_fd, &zero, 4 - llen) == -1) {
+            PLOG(ERROR) << "Error padding 0s to file " << file_name;
+            return false;
+        }
+    }
+    return true;
+}
+
+// Helper function for |cpioArchiveFilesInDir|
+size_t cpioWriteFileContent(int fd_read, int out_fd, struct stat& st) {
+    // writing content of file
+    std::array<char, 32 * 1024> read_buf;
+    ssize_t llen = st.st_size;
+    size_t n_error = 0;
+    while (llen > 0) {
+        ssize_t bytes_read = read(fd_read, read_buf.data(), read_buf.size());
+        if (bytes_read == -1) {
+            PLOG(ERROR) << "Error reading file";
+            return ++n_error;
+        }
+        llen -= bytes_read;
+        if (write(out_fd, read_buf.data(), bytes_read) == -1) {
+            PLOG(ERROR) << "Error writing data to file";
+            return ++n_error;
+        }
+        if (bytes_read == 0) {  // this should never happen, but just in case
+                                // to unstuck from while loop
+            PLOG(ERROR) << "Unexpected read result";
+            n_error++;
+            break;
+        }
+    }
+    llen = st.st_size % 4;
+    if (llen != 0) {
+        const uint32_t zero = 0;
+        if (write(out_fd, &zero, 4 - llen) == -1) {
+            PLOG(ERROR) << "Error padding 0s to file";
+            return ++n_error;
+        }
+    }
+    return n_error;
+}
+
+// Helper function for |cpioArchiveFilesInDir|
+bool cpioWriteFileTrailer(int out_fd) {
+    const int buf_size = 4096;
+    std::array<char, buf_size> read_buf;
+    read_buf.fill(0);
+    ssize_t llen = snprintf(read_buf.data(), 4096, "070701%040X%056X%08XTRAILER!!!", 1, 0x0b, 0);
+    if (write(out_fd, read_buf.data(), (llen < buf_size ? llen : buf_size - 1) + 4) == -1) {
+        PLOG(ERROR) << "Error writing trailing bytes";
+        return false;
+    }
+    return true;
+}
+
+// Archives all files in |input_dir| and writes result into |out_fd|
+// Logic obtained from //external/toybox/toys/posix/cpio.c "Output cpio archive"
+// portion
+size_t cpioArchiveFilesInDir(int out_fd, const char* input_dir) {
+    struct dirent* dp;
+    size_t n_error = 0;
+    std::unique_ptr<DIR, decltype(&closedir)> dir_dump(opendir(input_dir), closedir);
+    if (!dir_dump) {
+        PLOG(ERROR) << "Failed to open directory";
+        return ++n_error;
+    }
+    while ((dp = readdir(dir_dump.get()))) {
+        if (dp->d_type != DT_REG) {
+            continue;
+        }
+        std::string cur_file_name(dp->d_name);
+        struct stat st;
+        const std::string cur_file_path = kTombstoneFolderPath + cur_file_name;
+        if (stat(cur_file_path.c_str(), &st) == -1) {
+            PLOG(ERROR) << "Failed to get file stat for " << cur_file_path;
+            n_error++;
+            continue;
+        }
+        const int fd_read = open(cur_file_path.c_str(), O_RDONLY);
+        if (fd_read == -1) {
+            PLOG(ERROR) << "Failed to open file " << cur_file_path;
+            n_error++;
+            continue;
+        }
+        std::string file_name_with_last_modified_time =
+                cur_file_name + "-" + std::to_string(st.st_mtime);
+        // string.size() does not include the null terminator. The cpio FreeBSD
+        // file header expects the null character to be included in the length.
+        const size_t file_name_len = file_name_with_last_modified_time.size() + 1;
+        unique_fd file_auto_closer(fd_read);
+        if (!cpioWriteHeader(out_fd, st, file_name_with_last_modified_time.c_str(),
+                             file_name_len)) {
+            return ++n_error;
+        }
+        size_t write_error = cpioWriteFileContent(fd_read, out_fd, st);
+        if (write_error) {
+            return n_error + write_error;
+        }
+    }
+    if (!cpioWriteFileTrailer(out_fd)) {
+        return ++n_error;
+    }
+    return n_error;
+}
+
 }  // namespace
 
 namespace aidl {
@@ -83,16 +220,18 @@
 binder_status_t Wifi::dump(int fd, const char** args, uint32_t numArgs) {
     const auto lock = acquireGlobalLock();
     LOG(INFO) << "-----------Debug was called----------------";
-    if (chips_.size() == 0) {
-        LOG(INFO) << "No chips to display.";
-        return STATUS_OK;
+    if (chips_.size() != 0) {
+        for (std::shared_ptr<WifiChip> chip : chips_) {
+            if (!chip.get()) continue;
+            chip->dump(fd, args, numArgs);
+        }
     }
-
-    for (std::shared_ptr<WifiChip> chip : chips_) {
-        if (!chip.get()) continue;
-        chip->dump(fd, args, numArgs);
+    uint32_t n_error = cpioArchiveFilesInDir(fd, kTombstoneFolderPath);
+    if (n_error != 0) {
+        LOG(ERROR) << n_error << " errors occurred in cpio function";
     }
     ::android::base::WriteStringToFd("\n", fd);
+    fsync(fd);
     return STATUS_OK;
 }
 
diff --git a/wifi/aidl/default/wifi_chip.cpp b/wifi/aidl/default/wifi_chip.cpp
index 8265e5b..d72775c 100644
--- a/wifi/aidl/default/wifi_chip.cpp
+++ b/wifi/aidl/default/wifi_chip.cpp
@@ -32,13 +32,8 @@
 #define P2P_MGMT_DEVICE_PREFIX "p2p-dev-"
 
 namespace {
-using aidl::android::hardware::wifi::IfaceType;
-using aidl::android::hardware::wifi::IWifiChip;
-using CoexRestriction = aidl::android::hardware::wifi::IWifiChip::CoexRestriction;
-using ChannelCategoryMask = aidl::android::hardware::wifi::IWifiChip::ChannelCategoryMask;
 using android::base::unique_fd;
 
-constexpr char kCpioMagic[] = "070701";
 constexpr size_t kMaxBufferSizeBytes = 1024 * 1024 * 3;
 constexpr uint32_t kMaxRingBufferFileAgeSeconds = 60 * 60 * 10;
 constexpr uint32_t kMaxRingBufferFileNum = 20;
@@ -215,135 +210,6 @@
     return success;
 }
 
-// Helper function for |cpioArchiveFilesInDir|
-bool cpioWriteHeader(int out_fd, struct stat& st, const char* file_name, size_t file_name_len) {
-    const int buf_size = 32 * 1024;
-    std::array<char, buf_size> read_buf;
-    ssize_t llen = snprintf(
-            read_buf.data(), buf_size, "%s%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X",
-            kCpioMagic, static_cast<int>(st.st_ino), st.st_mode, st.st_uid, st.st_gid,
-            static_cast<int>(st.st_nlink), static_cast<int>(st.st_mtime),
-            static_cast<int>(st.st_size), major(st.st_dev), minor(st.st_dev), major(st.st_rdev),
-            minor(st.st_rdev), static_cast<uint32_t>(file_name_len), 0);
-    if (write(out_fd, read_buf.data(), llen < buf_size ? llen : buf_size - 1) == -1) {
-        PLOG(ERROR) << "Error writing cpio header to file " << file_name;
-        return false;
-    }
-    if (write(out_fd, file_name, file_name_len) == -1) {
-        PLOG(ERROR) << "Error writing filename to file " << file_name;
-        return false;
-    }
-
-    // NUL Pad header up to 4 multiple bytes.
-    llen = (llen + file_name_len) % 4;
-    if (llen != 0) {
-        const uint32_t zero = 0;
-        if (write(out_fd, &zero, 4 - llen) == -1) {
-            PLOG(ERROR) << "Error padding 0s to file " << file_name;
-            return false;
-        }
-    }
-    return true;
-}
-
-// Helper function for |cpioArchiveFilesInDir|
-size_t cpioWriteFileContent(int fd_read, int out_fd, struct stat& st) {
-    // writing content of file
-    std::array<char, 32 * 1024> read_buf;
-    ssize_t llen = st.st_size;
-    size_t n_error = 0;
-    while (llen > 0) {
-        ssize_t bytes_read = read(fd_read, read_buf.data(), read_buf.size());
-        if (bytes_read == -1) {
-            PLOG(ERROR) << "Error reading file";
-            return ++n_error;
-        }
-        llen -= bytes_read;
-        if (write(out_fd, read_buf.data(), bytes_read) == -1) {
-            PLOG(ERROR) << "Error writing data to file";
-            return ++n_error;
-        }
-        if (bytes_read == 0) {  // this should never happen, but just in case
-                                // to unstuck from while loop
-            PLOG(ERROR) << "Unexpected read result";
-            n_error++;
-            break;
-        }
-    }
-    llen = st.st_size % 4;
-    if (llen != 0) {
-        const uint32_t zero = 0;
-        if (write(out_fd, &zero, 4 - llen) == -1) {
-            PLOG(ERROR) << "Error padding 0s to file";
-            return ++n_error;
-        }
-    }
-    return n_error;
-}
-
-// Helper function for |cpioArchiveFilesInDir|
-bool cpioWriteFileTrailer(int out_fd) {
-    const int buf_size = 4096;
-    std::array<char, buf_size> read_buf;
-    read_buf.fill(0);
-    ssize_t llen = snprintf(read_buf.data(), 4096, "070701%040X%056X%08XTRAILER!!!", 1, 0x0b, 0);
-    if (write(out_fd, read_buf.data(), (llen < buf_size ? llen : buf_size - 1) + 4) == -1) {
-        PLOG(ERROR) << "Error writing trailing bytes";
-        return false;
-    }
-    return true;
-}
-
-// Archives all files in |input_dir| and writes result into |out_fd|
-// Logic obtained from //external/toybox/toys/posix/cpio.c "Output cpio archive"
-// portion
-size_t cpioArchiveFilesInDir(int out_fd, const char* input_dir) {
-    struct dirent* dp;
-    size_t n_error = 0;
-    std::unique_ptr<DIR, decltype(&closedir)> dir_dump(opendir(input_dir), closedir);
-    if (!dir_dump) {
-        PLOG(ERROR) << "Failed to open directory";
-        return ++n_error;
-    }
-    while ((dp = readdir(dir_dump.get()))) {
-        if (dp->d_type != DT_REG) {
-            continue;
-        }
-        std::string cur_file_name(dp->d_name);
-        struct stat st;
-        const std::string cur_file_path = kTombstoneFolderPath + cur_file_name;
-        if (stat(cur_file_path.c_str(), &st) == -1) {
-            PLOG(ERROR) << "Failed to get file stat for " << cur_file_path;
-            n_error++;
-            continue;
-        }
-        const int fd_read = open(cur_file_path.c_str(), O_RDONLY);
-        if (fd_read == -1) {
-            PLOG(ERROR) << "Failed to open file " << cur_file_path;
-            n_error++;
-            continue;
-        }
-        std::string file_name_with_last_modified_time =
-                cur_file_name + "-" + std::to_string(st.st_mtime);
-        // string.size() does not include the null terminator. The cpio FreeBSD
-        // file header expects the null character to be included in the length.
-        const size_t file_name_len = file_name_with_last_modified_time.size() + 1;
-        unique_fd file_auto_closer(fd_read);
-        if (!cpioWriteHeader(out_fd, st, file_name_with_last_modified_time.c_str(),
-                             file_name_len)) {
-            return ++n_error;
-        }
-        size_t write_error = cpioWriteFileContent(fd_read, out_fd, st);
-        if (write_error) {
-            return n_error + write_error;
-        }
-    }
-    if (!cpioWriteFileTrailer(out_fd)) {
-        return ++n_error;
-    }
-    return n_error;
-}
-
 // Helper function to create a non-const char*.
 std::vector<char> makeCharVec(const std::string& str) {
     std::vector<char> vec(str.size() + 1);
@@ -503,6 +369,14 @@
                            &WifiChip::createBridgedApIfaceInternal, _aidl_return);
 }
 
+ndk::ScopedAStatus WifiChip::createApOrBridgedApIface(
+        IfaceConcurrencyType in_ifaceType, const std::vector<common::OuiKeyedData>& in_vendorData,
+        std::shared_ptr<IWifiApIface>* _aidl_return) {
+    return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+                           &WifiChip::createApOrBridgedApIfaceInternal, _aidl_return, in_ifaceType,
+                           in_vendorData);
+}
+
 ndk::ScopedAStatus WifiChip::getApIfaceNames(std::vector<std::string>* _aidl_return) {
     return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
                            &WifiChip::getApIfaceNamesInternal, _aidl_return);
@@ -651,7 +525,7 @@
                            &WifiChip::setLatencyModeInternal, in_mode);
 }
 
-binder_status_t WifiChip::dump(int fd, const char**, uint32_t) {
+binder_status_t WifiChip::dump(int fd __unused, const char**, uint32_t) {
     {
         std::unique_lock<std::mutex> lk(lock_t);
         for (const auto& item : ringbuffer_map_) {
@@ -664,11 +538,6 @@
     if (!writeRingbufferFilesInternal()) {
         LOG(ERROR) << "Error writing files to flash";
     }
-    uint32_t n_error = cpioArchiveFilesInDir(fd, kTombstoneFolderPath);
-    if (n_error != 0) {
-        LOG(ERROR) << n_error << " errors occurred in cpio function";
-    }
-    fsync(fd);
     return STATUS_OK;
 }
 
@@ -993,6 +862,18 @@
     return {iface, ndk::ScopedAStatus::ok()};
 }
 
+std::pair<std::shared_ptr<IWifiApIface>, ndk::ScopedAStatus>
+WifiChip::createApOrBridgedApIfaceInternal(
+        IfaceConcurrencyType ifaceType, const std::vector<common::OuiKeyedData>& /* vendorData */) {
+    if (ifaceType == IfaceConcurrencyType::AP) {
+        return createApIfaceInternal();
+    } else if (ifaceType == IfaceConcurrencyType::AP_BRIDGED) {
+        return createBridgedApIfaceInternal();
+    } else {
+        return {nullptr, createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS)};
+    }
+}
+
 std::pair<std::vector<std::string>, ndk::ScopedAStatus> WifiChip::getApIfaceNamesInternal() {
     if (ap_ifaces_.empty()) {
         return {std::vector<std::string>(), ndk::ScopedAStatus::ok()};
diff --git a/wifi/aidl/default/wifi_chip.h b/wifi/aidl/default/wifi_chip.h
index 1a36032..c6517db 100644
--- a/wifi/aidl/default/wifi_chip.h
+++ b/wifi/aidl/default/wifi_chip.h
@@ -19,6 +19,7 @@
 
 #include <aidl/android/hardware/wifi/BnWifiChip.h>
 #include <aidl/android/hardware/wifi/IWifiRttController.h>
+#include <aidl/android/hardware/wifi/common/OuiKeyedData.h>
 #include <android-base/macros.h>
 
 #include <list>
@@ -96,6 +97,10 @@
     ndk::ScopedAStatus requestFirmwareDebugDump(std::vector<uint8_t>* _aidl_return) override;
     ndk::ScopedAStatus createApIface(std::shared_ptr<IWifiApIface>* _aidl_return) override;
     ndk::ScopedAStatus createBridgedApIface(std::shared_ptr<IWifiApIface>* _aidl_return) override;
+    ndk::ScopedAStatus createApOrBridgedApIface(
+            IfaceConcurrencyType in_ifaceType,
+            const std::vector<common::OuiKeyedData>& in_vendorData,
+            std::shared_ptr<IWifiApIface>* _aidl_return) override;
     ndk::ScopedAStatus getApIfaceNames(std::vector<std::string>* _aidl_return) override;
     ndk::ScopedAStatus getApIface(const std::string& in_ifname,
                                   std::shared_ptr<IWifiApIface>* _aidl_return) override;
@@ -176,6 +181,8 @@
     ndk::ScopedAStatus createVirtualApInterface(const std::string& apVirtIf);
     std::pair<std::shared_ptr<IWifiApIface>, ndk::ScopedAStatus> createApIfaceInternal();
     std::pair<std::shared_ptr<IWifiApIface>, ndk::ScopedAStatus> createBridgedApIfaceInternal();
+    std::pair<std::shared_ptr<IWifiApIface>, ndk::ScopedAStatus> createApOrBridgedApIfaceInternal(
+            IfaceConcurrencyType ifaceType, const std::vector<common::OuiKeyedData>& vendorData);
     std::pair<std::vector<std::string>, ndk::ScopedAStatus> getApIfaceNamesInternal();
     std::pair<std::shared_ptr<IWifiApIface>, ndk::ScopedAStatus> getApIfaceInternal(
             const std::string& ifname);
diff --git a/wifi/apex/Android.bp b/wifi/apex/Android.bp
index f8ba5c4..f8c8e6f 100644
--- a/wifi/apex/Android.bp
+++ b/wifi/apex/Android.bp
@@ -2,17 +2,6 @@
     default_applicable_licenses: ["hardware_interfaces_license"],
 }
 
-apex_key {
-    name: "com.android.hardware.wifi.key",
-    public_key: "com.android.hardware.wifi.avbpubkey",
-    private_key: "com.android.hardware.wifi.pem",
-}
-
-android_app_certificate {
-    name: "com.android.hardware.wifi.certificate",
-    certificate: "com.android.hardware.wifi",
-}
-
 genrule {
     name: "gen-android.hardware.wifi.rc",
     srcs: [":default-android.hardware.wifi-service.rc"],
@@ -36,12 +25,12 @@
 apex {
     name: "com.android.hardware.wifi",
     manifest: "apex_manifest.json",
-    key: "com.android.hardware.wifi.key",
-    certificate: ":com.android.hardware.wifi.certificate",
+    key: "com.android.hardware.key",
+    certificate: ":com.android.hardware.certificate",
     file_contexts: "file_contexts",
-    use_vndk_as_stable: true,
     updatable: false,
-    soc_specific: true,
+    vendor: true,
+
     binaries: [
         "android.hardware.wifi-service",
     ],
diff --git a/wifi/apex/com.android.hardware.wifi.avbpubkey b/wifi/apex/com.android.hardware.wifi.avbpubkey
deleted file mode 100644
index 63fba77..0000000
--- a/wifi/apex/com.android.hardware.wifi.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/wifi/apex/com.android.hardware.wifi.pem b/wifi/apex/com.android.hardware.wifi.pem
deleted file mode 100644
index 9e589ac..0000000
--- a/wifi/apex/com.android.hardware.wifi.pem
+++ /dev/null
@@ -1,52 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQCtT/vNnVVg2QVD
-eMdG+YZ8qYz/fbAQF9hnH8dE5RWHXzoYItBG7DSOuVT4T6POBVmFaVKlWDd7tDyw
-nFO3MmE2/FzhVSiBPwhMITa7UIERr3od3rWJ5g6oaTCOu4wa98L466Jp60f2fYSZ
-M9lGiKaDpLDSpxTU9hexjp7C4PfinnkYnlHBnrFXTmiO6f8AvOEwFFx73/rUNoe7
-F3TkGvjZDqHvE+pjz/nilkhXYuOl3zgSaeznJ9+TO5C/Z+Xr+zRhaJGI4v5Dkgmc
-jNy74+0hjwobXO3iWE44InQMvMh8zDKBx9l1oSsFoG3waj9ayqSYD7M74RX3PkUL
-QrhgAHZWi5iEnpu50xBzAqZB1ZDVkdZiKiGzweJ8OqltnVpvzlnW3rA3y3HtFyLm
-73C4ll9MdLaw266vBxgZfFOcjpphbbh9J9uGjOCJY1AxUzsqKygFD2CyOdb1jab3
-AC6VvRa+bLtv8fd2etp3atXv+Y9ACUX6zNK6Oa8Zktoo2Z//OLtcrk7xhgKKDkUF
-OPQrIjW9x0fdClDioIS+y7EHNUrfyRL7XPtUqGCszgz5jK2SMVGMpFaEtfbyNP1H
-BTGXdzcDP0RZdOOKTdBFgoRW5+6TH5CU9Nl4uevpxzwsTkXg0a+W1BGP+s9J7ssH
-rNPmHz+pbKZPDs/nxlpsKq+N+K8cCwIDAQABAoICAAJL943Lgnikl53DyXxGzUH0
-q0Itg7pK3prLQIRItubS2727JGB0O+QST650u7p8tql+clJvn1ib1FwQzkk0uTYV
-1RNFYiKIV89Od1+3GubFmQwxSd2Yd2RC9JpHoP0wgFx1HvNhY1RAaJPxLHVzVSWU
-dqVsAmoqErlPJwp1GcPejsNFQdcbh8Uc7GTMdA0p86AD/Q/FMZlDWbwgfPOS6e5S
-c9HrxSTqeijHDhFeZZ7qnN8dmT6c+CkG1o26zkC41QJfdOJIA8+YbVkuQrSYuilC
-MIOZUSu5ONwklL4geFWzDQ5MPDUDXEMYU6ymc817tv+u4ZSvEG/02sxh53iaOPc6
-C6im4nm6ZlRP9HzIvLAiRSbvwEb9cnLKgYpioSGXehDYg3zMPURwoqIxw+7IlIUh
-s+rhmCJV62PK1Z0nmo7m7S8r+z6j4G7aztGcbvdecocOJYOQB1VB8Zh4bNEIWp0a
-GDteG6aWXOCHoYRWAOluppDWa7Z+EgesU3Oj9Prn/ekUzzXx3V30zSTZYxRnQCO0
-vZIZ6hrlsNJcNrDpxmiWBaEOd4k5QI39pu6fDHc+UEEJMN+7eVk8d9QVA/LhrCjc
-JH1EVjtWosMUeMaMTpcmHTQKnEvmT2LO2fxGlF4JvjzDdVMloJrIP2LSQjovo2PX
-x9UXVu8Dt3kQRefZ42XxAoIBAQDkBoSYVajaFlyv9lW8oPmrQyzp93ite+TKVR8z
-PmcFQukjp5Gm8PGlGtGoH8aeE9Ev+R86aNPFy4n4ZJ7fmZYH1hxZ6dSc/56k6lLn
-uVKvTudYqwcRgBKuSZ8IDPFz3sHd+MnOBonDIri28fcBTDNv4LJP/6cAUoIOCCPm
-lQtJBfMNMDOXG4jv1Rv/1P5opGFATerRCubOxmeaFhZIDEjvN5WvLK5jmL7I8lX3
-X+gPiMHFWBQFmVTOHeVYEORDO5xFCOvHqCVB78b2xe1NkkrQ0CexpdflJVLE9IWt
-wWH43nhjxaK+eiBPrj37BliJvNaYfuxqcAj3p8c5KweFEtP7AoIBAQDCkx72K/Ja
-rFrrI0Avfo+3n6yoTMtMkiVhMuIKKPBJ2o4Opc/uixk+uLH5UH0X5GZsE51FAhUD
-L3bWMxV+vzVefWS8Iryo94ucvnfqhsN3KZXHDGoAuYv72jND8qPLKksM0dceMBfl
-aoyXSPAe8zAZeEx4iR2axgtnA0EbiXIaNVE/LF+WYdM74jvk/QtRzJ8IAAkSc5Rr
-GiTHeeP3nRnkjWjkmwKXYSJHcrEl26c/2ckeZORtblqxR9wMU5OgvJvMSzmOIpVH
-Y5lylZUOTuJCkApHFNKdRsawsSNKsy4yfY1byN/WkODb7le6Crt1gcwldMxDZAo9
-iB25FHq4zksxAoIBAQC+SBYkDO9PtnN4PycCto5B9VeokmN42bdthKT5nSxY/qIQ
-p8fquIvdzEiCdKnIxh69WrVNh6aZGyWySz0suDyzo1+bRH6w2LrpQcUXK9YtBroV
-ivrmBqsQF82G6U4f9BZxhifZLimN1g6wU7Bcu9r8lFQYX+1bXn66+N4EkAGP2VAe
-hEe45Dhccsjfrzzx06J4B81YzjEXAgf4VFAZpW7DeO4G9VE9OXyTsW49dSHwvJ1+
-ceabWX2kVtxIpiflVvwru6sNvGoC4PV2fmptXhPitqE5JHzJ8mBkjOx0t7hq9jMe
-hxEsxDrsYynDrWL65cNqFBhzJbTF/ZNJSHgI+1I7AoIBAQC7m0shJOJ61vCbA9Qh
-dzBvZn/9jn3/CHMOMxeLoEl/jEGokevZHzlqJn9D2n2jCdBPqOHc5dMIzT0R7xNs
-sERvJQx58ixh5r0wlt3cva++N9R4pdmXdVApuAvyGgQgIllWtQVr0AdaZs/EFsmf
-re/Uvw9MsThgQVBBNPwT5wSjjIEYHlrUDuKzPMFvWyUM6/Tyq8YTimmykvSfeUF7
-QHj0y/w1X9ixyTBaH5X64L10bTLkIXe2o87CXH0pTXRsaS73XhjSmTnCKaCMwPmF
-YD383BFs1AD3MITnXQSgQ//pIvGnbBmXMv38UOU5NpvlAw+pleJVoCHXjmTKTZq+
-kfohAoIBAQCrEecN8XEPjGdtY71rDYEwHGM6G4czHX0PNhlMjQos3aBVZ/YvVxPG
-pkXbms3GRXv4W92u7b2MwEKBPxcBepEdDZN9wSpe63bDFE6gpkipDhgj97JlLEUd
-s7h6oOoazdxmsRZyFho99SRQWrvyOiiKdLJCRZiqjUB4roOQmy7f9VAz6+OxyGV9
-XZigwW6bfUzMCmhx5Ss6zW8wZI+gINQh+2sDmiBiMQv14Ya5zlNYN+4/257Sk/jS
-o3QUDJITbReq/DNZ6UUzQS+AZ7ztc81rk5kRg0I33FZarRJ7TLAz+XmZZFoIOORz
-etEvMk8bJ4u7X89NWW/i2J+kQiDQg819
------END PRIVATE KEY-----
diff --git a/wifi/apex/com.android.hardware.wifi.pk8 b/wifi/apex/com.android.hardware.wifi.pk8
deleted file mode 100644
index f4481bf..0000000
--- a/wifi/apex/com.android.hardware.wifi.pk8
+++ /dev/null
Binary files differ
diff --git a/wifi/apex/com.android.hardware.wifi.x509.pem b/wifi/apex/com.android.hardware.wifi.x509.pem
deleted file mode 100644
index c942e71..0000000
--- a/wifi/apex/com.android.hardware.wifi.x509.pem
+++ /dev/null
@@ -1,36 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIGOzCCBCOgAwIBAgIUIEueuBFEoCFmLyEvXDsKVuZeH0EwDQYJKoZIhvcNAQEL
-BQAwgasxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
-DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
-b2lkMScwJQYDVQQDDB5jb20uYW5kcm9pZC5oYXJkd2FyZS53aWZpLmFwZXgxIjAg
-BgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5jb20wIBcNMjIxMDA2MTY1MDQy
-WhgPNDc2MDA5MDExNjUwNDJaMIGrMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2Fs
-aWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9p
-ZDEQMA4GA1UECwwHQW5kcm9pZDEnMCUGA1UEAwweY29tLmFuZHJvaWQuaGFyZHdh
-cmUud2lmaS5hcGV4MSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29t
-MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAo4N23iL0J42qG2lt4ysD
-t6XxAKdi4sTWB6ZjFWerBm11s3yLr1KBZV82z3j2o7+EOaWa/91Mgc+aoiGYUa1f
-IweugKTrnRmrzOvtq/Wp5PGqSxsmLPPvH3wyEB/CMgAn0pqDf0WyCWJ2kAlcCkmy
-qVNFupkGFyIE6gkoc+AmJGbSTquDlL07R/8XYDicqrcgeqy9ZaCJ5FLfmbnnRb2A
-vm4Un7e5dFz5+dPaOJXf4AOMUSPUd7fuBliGYFLzcZnYQbzMktXa4XnPuSlmerwy
-EwY767L2bjRjaSgPb0Js13gZ4S4ZHZe07AV7HPlt/EzbxV/jtMgHl4xn5p0WhVnK
-MPtLsO/e7FkDPAKpT02sgUK6pVKqgBGOWm27tmTB09lscMLQeVFqwpoFq2zMUrDn
-ipDFMWRBeLrEDKx41zF3gqdPmP+SMkQYJu4OATIXOndIeo7AN9iE+N6snHkckNyE
-saCwmnzyhVAbMn/epfIQZz3zcyljA9yfOpv5jctN4es+3i0htDnoNO9ij4M5fxuP
-jtNAP3EA61nKZ5+Js0/MMQKrfwCLogPl/4vCNuuGT2rhCkhq1CLYXmb9ghvVzcPe
-BOGXNDKdB+aUTxrQUOYlHf0cRDHdU6cchrz9+QhR7t9dlibtiyCZE34xgR3klmyz
-XJ3M1r/QRihjARH7exrrwiUCAwEAAaNTMFEwHQYDVR0OBBYEFGN9tMk+4SDk7twk
-MrLjM+nRxGDJMB8GA1UdIwQYMBaAFGN9tMk+4SDk7twkMrLjM+nRxGDJMA8GA1Ud
-EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAHoTfVBRG0rVE7gkV526gwR5
-/3mXyYA57lKJhZ21fu2qfTE6WYj4DsOTZKrPIAUFv/SnzZ6Rvv7W81XV5M/1m+5R
-/1wYvWwm3FBOvvt4VDUiel0Zfc9+nwynjz1seYdXU8fNIOzBcr9hutkYdRZDkNpc
-Zcl4NG04TzyedkQ/0SyHnygmq4ZY9OUEvrNaWBFHzw2SQhYvHh8jUrqpPvoJz0Px
-avKI8bOgXTJRJ+Pk7hjXDFQY/fbE0RGivorPMLs+XHaEIb+YPyXsX4OZwowG5KL8
-txyvUsH+qZToytdPk4LCuwYBobBlr+AAg7pxOtbDW1ITDhQ9n05UFK2iUwsJecgg
-VDA0K3GuCjmGVmkw7SFRYfToCyGWah8sQCUMCCcsof7gS+dMxuWeee+sRxRJcJY+
-xR2uySe8g4ohwNjQ0zLQv7PZOKQh91yEWxRXmhPYVpiVAjpSD2zH7Vd6CJ9xji//
-5S1UrxWwQ5igvu8v5veqNAW7uXGXADnxL69HVGTLm0XDIUUOAUIG8waiVkYUo3UU
-AzAFbF7ewYMKdg7ylUYcTaMRIsKYW/3/1t3NJv2z99W4V/p8e1kRCeWMPB5C+/Lo
-b/hSWj1NF9AJ30ukBndMh6bRprq+G5NLV6OaoCLp606CMdXdT8uw9lYCt7DbQHG9
-Hw3iw61svpUwcaWfN1hI
------END CERTIFICATE-----
diff --git a/wifi/common/aidl/Android.bp b/wifi/common/aidl/Android.bp
new file mode 100644
index 0000000..1913451
--- /dev/null
+++ b/wifi/common/aidl/Android.bp
@@ -0,0 +1,47 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.wifi.common",
+    vendor_available: true,
+    srcs: [
+        "android/hardware/wifi/common/*.aidl",
+    ],
+    headers: [
+        "PersistableBundle_aidl",
+    ],
+    stability: "vintf",
+    backend: {
+        java: {
+            sdk_version: "module_current",
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.wifi",
+            ],
+            min_sdk_version: "30",
+        },
+        cpp: {
+            enabled: false,
+        },
+    },
+}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/wifi/common/aidl/aidl_api/android.hardware.wifi.common/current/android/hardware/wifi/common/OuiKeyedData.aidl
similarity index 87%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
copy to wifi/common/aidl/aidl_api/android.hardware.wifi.common/current/android/hardware/wifi/common/OuiKeyedData.aidl
index b9df1f5..640a1f6 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/wifi/common/aidl/aidl_api/android.hardware.wifi.common/current/android/hardware/wifi/common/OuiKeyedData.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,9 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
-/* @hide */
-@Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+package android.hardware.wifi.common;
+@VintfStability
+parcelable OuiKeyedData {
+  int oui;
+  android.os.PersistableBundle vendorData;
 }
diff --git a/wifi/common/aidl/android/hardware/wifi/common/OuiKeyedData.aidl b/wifi/common/aidl/android/hardware/wifi/common/OuiKeyedData.aidl
new file mode 100644
index 0000000..4e6a99f
--- /dev/null
+++ b/wifi/common/aidl/android/hardware/wifi/common/OuiKeyedData.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.wifi.common;
+
+import android.os.PersistableBundle;
+
+/**
+ * Data for OUI-based configuration.
+ */
+@VintfStability
+parcelable OuiKeyedData {
+    /**
+     * OUI : 24-bit organizationally unique identifier to identify the vendor/OEM.
+     * See https://standards-oui.ieee.org/ for more information.
+     */
+    int oui;
+    /**
+     * Vendor data. Expected fields should be defined by the vendor.
+     */
+    PersistableBundle vendorData;
+}
diff --git a/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test.cpp b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test.cpp
index bb99ae4..46cba93 100644
--- a/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test.cpp
+++ b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test.cpp
@@ -48,6 +48,13 @@
     virtual void SetUp() override {
         wifi_instance_name_ = std::get<0>(GetParam());
         hostapd_instance_name_ = std::get<1>(GetParam());
+
+        // Disable Wi-Fi framework to avoid interference
+        isWifiEnabled_ = isWifiFrameworkEnabled();
+        isScanAlwaysEnabled_ = isWifiScanAlwaysAvailable();
+        toggleWifiFramework(false);
+        toggleWifiScanAlwaysAvailable(false);
+
         stopSupplicantIfNeeded(wifi_instance_name_);
         startHostapdAndWaitForHidlService(wifi_instance_name_,
                                           hostapd_instance_name_);
@@ -58,14 +65,21 @@
     virtual void TearDown() override {
         HIDL_INVOKE_VOID_WITHOUT_ARGUMENTS(hostapd_, terminate);
         stopHostapd(wifi_instance_name_);
+
+        // Restore Wi-Fi framework state
+        toggleWifiFramework(isWifiEnabled_);
+        toggleWifiScanAlwaysAvailable(isScanAlwaysEnabled_);
     }
 
    protected:
-    std::string getPrimaryWlanIfaceName() {
+     bool isWifiEnabled_ = false;
+     bool isScanAlwaysEnabled_ = false;
+
+     std::string getPrimaryWlanIfaceName() {
         std::array<char, PROPERTY_VALUE_MAX> buffer;
         property_get("wifi.interface", buffer.data(), "wlan0");
         return buffer.data();
-    }
+     }
 
     IHostapd::IfaceParams getIfaceParamsWithAcs() {
         IHostapd::IfaceParams iface_params;
diff --git a/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test_utils.cpp b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test_utils.cpp
index 56f285d..4c452fb 100644
--- a/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test_utils.cpp
+++ b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test_utils.cpp
@@ -98,3 +98,24 @@
         ::android::hardware::wifi::hostapd::V1_1::IHostapd::castFrom(hostapd);
     return hostapd_1_1.get() != nullptr;
 }
+
+void toggleWifiFramework(bool enable) {
+    std::string cmd = "/system/bin/cmd wifi set-wifi-enabled ";
+    cmd += enable ? "enabled" : "disabled";
+    testing::checkSubstringInCommandOutput(cmd.c_str(), "X");
+}
+
+void toggleWifiScanAlwaysAvailable(bool enable) {
+    std::string cmd = "/system/bin/cmd wifi set-scan-always-available ";
+    cmd += enable ? "enabled" : "disabled";
+    testing::checkSubstringInCommandOutput(cmd.c_str(), "X");
+}
+
+bool isWifiFrameworkEnabled() {
+    return testing::checkSubstringInCommandOutput("/system/bin/cmd wifi status", "Wifi is enabled");
+}
+
+bool isWifiScanAlwaysAvailable() {
+    return testing::checkSubstringInCommandOutput("/system/bin/cmd wifi status",
+                                                  "Wifi scanning is always available");
+}
diff --git a/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test_utils.h b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test_utils.h
index 893de1e..aa34c9a 100644
--- a/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test_utils.h
+++ b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test_utils.h
@@ -17,14 +17,17 @@
 #ifndef HOSTAPD_HIDL_TEST_UTILS_H
 #define HOSTAPD_HIDL_TEST_UTILS_H
 
+#include <VtsCoreUtil.h>
 #include <android/hardware/wifi/hostapd/1.0/IHostapd.h>
 #include <android/hardware/wifi/hostapd/1.1/IHostapd.h>
 
 // Used to stop the android wifi framework before every test.
-void stopWifiFramework(const std::string& instance_name);
-void startWifiFramework(const std::string& instance_name);
 void stopSupplicantIfNeeded(const std::string& instance_name);
 void stopHostapd(const std::string& instance_name);
+void toggleWifiFramework(bool enable);
+void toggleWifiScanAlwaysAvailable(bool enable);
+bool isWifiFrameworkEnabled();
+bool isWifiScanAlwaysAvailable();
 // Used to configure the chip, driver and start wpa_hostapd before every
 // test.
 void startHostapdAndWaitForHidlService(
diff --git a/wifi/netlinkinterceptor/aidl/default/Android.bp b/wifi/netlinkinterceptor/aidl/default/Android.bp
index 5227e51..c3a0c03 100644
--- a/wifi/netlinkinterceptor/aidl/default/Android.bp
+++ b/wifi/netlinkinterceptor/aidl/default/Android.bp
@@ -25,8 +25,6 @@
 
 cc_binary {
     name: "android.hardware.net.nlinterceptor-service.default",
-    init_rc: ["nlinterceptor-default.rc"],
-    vintf_fragments: ["nlinterceptor-default.xml"],
     vendor: true,
     relative_install_path: "hw",
     defaults: ["nlinterceptor@defaults"],
@@ -45,4 +43,35 @@
         "service.cpp",
         "util.cpp",
     ],
+    installable: false, // installed in APEX
+}
+
+apex {
+    name: "com.android.hardware.net.nlinterceptor",
+    vendor: true,
+    manifest: "apex_manifest.json",
+    file_contexts: "apex_file_contexts",
+    key: "com.android.hardware.key",
+    certificate: ":com.android.hardware.certificate",
+    updatable: false,
+    binaries: [
+        "android.hardware.net.nlinterceptor-service.default",
+    ],
+    prebuilts: [
+        "nlinterceptor.rc",
+        "nlinterceptor.xml",
+    ],
+}
+
+prebuilt_etc {
+    name: "nlinterceptor.rc",
+    src: "nlinterceptor.rc",
+    installable: false,
+}
+
+prebuilt_etc {
+    name: "nlinterceptor.xml",
+    src: "nlinterceptor.xml",
+    sub_dir: "vintf",
+    installable: false,
 }
diff --git a/wifi/netlinkinterceptor/aidl/default/apex_file_contexts b/wifi/netlinkinterceptor/aidl/default/apex_file_contexts
new file mode 100644
index 0000000..6ee544c
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/default/apex_file_contexts
@@ -0,0 +1,3 @@
+(/.*)?                                                          u:object_r:vendor_file:s0
+/etc(/.*)?                                                      u:object_r:vendor_configs_file:s0
+/bin/hw/android\.hardware\.net\.nlinterceptor-service\.default	u:object_r:hal_nlinterceptor_default_exec:s0
diff --git a/wifi/netlinkinterceptor/aidl/default/apex_manifest.json b/wifi/netlinkinterceptor/aidl/default/apex_manifest.json
new file mode 100644
index 0000000..4ffeac5
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/default/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+    "name": "com.android.hardware.net.nlinterceptor",
+    "version": 1
+}
diff --git a/wifi/netlinkinterceptor/aidl/default/nlinterceptor-default.rc b/wifi/netlinkinterceptor/aidl/default/nlinterceptor-default.rc
deleted file mode 100644
index 353cb27..0000000
--- a/wifi/netlinkinterceptor/aidl/default/nlinterceptor-default.rc
+++ /dev/null
@@ -1,4 +0,0 @@
-service nlinterceptor /vendor/bin/hw/android.hardware.net.nlinterceptor-service.default
-    class hal
-    user root
-    group system inet
diff --git a/wifi/netlinkinterceptor/aidl/default/nlinterceptor.rc b/wifi/netlinkinterceptor/aidl/default/nlinterceptor.rc
new file mode 100644
index 0000000..ec9baa9
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/default/nlinterceptor.rc
@@ -0,0 +1,4 @@
+service nlinterceptor /apex/com.android.hardware.net.nlinterceptor/bin/hw/android.hardware.net.nlinterceptor-service.default
+    class hal
+    user root
+    group system inet
diff --git a/wifi/netlinkinterceptor/aidl/default/nlinterceptor-default.xml b/wifi/netlinkinterceptor/aidl/default/nlinterceptor.xml
similarity index 100%
rename from wifi/netlinkinterceptor/aidl/default/nlinterceptor-default.xml
rename to wifi/netlinkinterceptor/aidl/default/nlinterceptor.xml
diff --git a/wifi/supplicant/aidl/Android.bp b/wifi/supplicant/aidl/Android.bp
index ac5a952..76b0902 100644
--- a/wifi/supplicant/aidl/Android.bp
+++ b/wifi/supplicant/aidl/Android.bp
@@ -57,6 +57,6 @@
         },
 
     ],
-    frozen: true,
+    frozen: false,
 
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/AuthAlgMask.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/AuthAlgMask.aidl
index 9cd178d..4421018 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/AuthAlgMask.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/AuthAlgMask.aidl
@@ -34,8 +34,8 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum AuthAlgMask {
-  OPEN = 1,
-  SHARED = 2,
-  LEAP = 4,
-  SAE = 16,
+  OPEN = (1 << 0) /* 1 */,
+  SHARED = (1 << 1) /* 2 */,
+  LEAP = (1 << 2) /* 4 */,
+  SAE = (1 << 4) /* 16 */,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/AuxiliarySupplicantEventCode.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/AuxiliarySupplicantEventCode.aidl
index 471cfff..a339a92 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/AuxiliarySupplicantEventCode.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/AuxiliarySupplicantEventCode.aidl
@@ -34,7 +34,7 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum AuxiliarySupplicantEventCode {
-  EAP_METHOD_SELECTED = 0,
-  SSID_TEMP_DISABLED = 1,
-  OPEN_SSL_FAILURE = 2,
+  EAP_METHOD_SELECTED,
+  SSID_TEMP_DISABLED,
+  OPEN_SSL_FAILURE,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/BssTmDataFlagsMask.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/BssTmDataFlagsMask.aidl
index f215f05..6f0045c 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/BssTmDataFlagsMask.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/BssTmDataFlagsMask.aidl
@@ -34,12 +34,12 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum BssTmDataFlagsMask {
-  WNM_MODE_PREFERRED_CANDIDATE_LIST_INCLUDED = 1,
-  WNM_MODE_ABRIDGED = 2,
-  WNM_MODE_DISASSOCIATION_IMMINENT = 4,
-  WNM_MODE_BSS_TERMINATION_INCLUDED = 8,
-  WNM_MODE_ESS_DISASSOCIATION_IMMINENT = 16,
-  MBO_TRANSITION_REASON_CODE_INCLUDED = 32,
-  MBO_ASSOC_RETRY_DELAY_INCLUDED = 64,
-  MBO_CELLULAR_DATA_CONNECTION_PREFERENCE_INCLUDED = 128,
+  WNM_MODE_PREFERRED_CANDIDATE_LIST_INCLUDED = (1 << 0) /* 1 */,
+  WNM_MODE_ABRIDGED = (1 << 1) /* 2 */,
+  WNM_MODE_DISASSOCIATION_IMMINENT = (1 << 2) /* 4 */,
+  WNM_MODE_BSS_TERMINATION_INCLUDED = (1 << 3) /* 8 */,
+  WNM_MODE_ESS_DISASSOCIATION_IMMINENT = (1 << 4) /* 16 */,
+  MBO_TRANSITION_REASON_CODE_INCLUDED = (1 << 5) /* 32 */,
+  MBO_ASSOC_RETRY_DELAY_INCLUDED = (1 << 6) /* 64 */,
+  MBO_CELLULAR_DATA_CONNECTION_PREFERENCE_INCLUDED = (1 << 7) /* 128 */,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppAkm.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppAkm.aidl
index df2aef8..730843d 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppAkm.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppAkm.aidl
@@ -34,8 +34,8 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum DppAkm {
-  PSK = 0,
-  PSK_SAE = 1,
-  SAE = 2,
-  DPP = 3,
+  PSK,
+  PSK_SAE,
+  SAE,
+  DPP,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppCurve.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppCurve.aidl
index e69da44..14cb49f 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppCurve.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppCurve.aidl
@@ -34,10 +34,10 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum DppCurve {
-  PRIME256V1 = 0,
-  SECP384R1 = 1,
-  SECP521R1 = 2,
-  BRAINPOOLP256R1 = 3,
-  BRAINPOOLP384R1 = 4,
-  BRAINPOOLP512R1 = 5,
+  PRIME256V1,
+  SECP384R1,
+  SECP521R1,
+  BRAINPOOLP256R1,
+  BRAINPOOLP384R1,
+  BRAINPOOLP512R1,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppEventType.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppEventType.aidl
index 9e394fc..47c8cc0 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppEventType.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppEventType.aidl
@@ -34,6 +34,6 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum DppEventType {
-  CONFIGURATION_SENT = 0,
-  CONFIGURATION_APPLIED = 1,
+  CONFIGURATION_SENT,
+  CONFIGURATION_APPLIED,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppFailureCode.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppFailureCode.aidl
index 7e7c806..89fbc4b 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppFailureCode.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppFailureCode.aidl
@@ -34,16 +34,16 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum DppFailureCode {
-  INVALID_URI = 0,
-  AUTHENTICATION = 1,
-  NOT_COMPATIBLE = 2,
-  CONFIGURATION = 3,
-  BUSY = 4,
-  TIMEOUT = 5,
-  FAILURE = 6,
-  NOT_SUPPORTED = 7,
-  CONFIGURATION_REJECTED = 8,
-  CANNOT_FIND_NETWORK = 9,
-  ENROLLEE_AUTHENTICATION = 10,
-  URI_GENERATION = 11,
+  INVALID_URI,
+  AUTHENTICATION,
+  NOT_COMPATIBLE,
+  CONFIGURATION,
+  BUSY,
+  TIMEOUT,
+  FAILURE,
+  NOT_SUPPORTED,
+  CONFIGURATION_REJECTED,
+  CANNOT_FIND_NETWORK,
+  ENROLLEE_AUTHENTICATION,
+  URI_GENERATION,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppNetRole.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppNetRole.aidl
index c6d3522..77a910b 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppNetRole.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppNetRole.aidl
@@ -34,6 +34,6 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum DppNetRole {
-  STA = 0,
-  AP = 1,
+  STA,
+  AP,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppProgressCode.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppProgressCode.aidl
index f0618a5..ea244de 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppProgressCode.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppProgressCode.aidl
@@ -34,8 +34,8 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum DppProgressCode {
-  AUTHENTICATION_SUCCESS = 0,
-  RESPONSE_PENDING = 1,
-  CONFIGURATION_SENT_WAITING_RESPONSE = 2,
-  CONFIGURATION_ACCEPTED = 3,
+  AUTHENTICATION_SUCCESS,
+  RESPONSE_PENDING,
+  CONFIGURATION_SENT_WAITING_RESPONSE,
+  CONFIGURATION_ACCEPTED,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppStatusErrorCode.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppStatusErrorCode.aidl
index d72633b..21f07dd 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppStatusErrorCode.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/DppStatusErrorCode.aidl
@@ -34,7 +34,7 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum DppStatusErrorCode {
-  UNKNOWN = -1,
+  UNKNOWN = (-1) /* -1 */,
   SUCCESS = 0,
   NOT_COMPATIBLE = 1,
   AUTH_FAILURE = 2,
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/GroupCipherMask.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/GroupCipherMask.aidl
index f2da925..d22d3d0 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/GroupCipherMask.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/GroupCipherMask.aidl
@@ -34,12 +34,12 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum GroupCipherMask {
-  WEP40 = 2,
-  WEP104 = 4,
-  TKIP = 8,
-  CCMP = 16,
-  GTK_NOT_USED = 16384,
-  GCMP_256 = 256,
-  SMS4 = 128,
-  GCMP_128 = 64,
+  WEP40 = (1 << 1) /* 2 */,
+  WEP104 = (1 << 2) /* 4 */,
+  TKIP = (1 << 3) /* 8 */,
+  CCMP = (1 << 4) /* 16 */,
+  GTK_NOT_USED = (1 << 14) /* 16384 */,
+  GCMP_256 = (1 << 8) /* 256 */,
+  SMS4 = (1 << 7) /* 128 */,
+  GCMP_128 = (1 << 6) /* 64 */,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/GroupMgmtCipherMask.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/GroupMgmtCipherMask.aidl
index c24d6cc..23bb04f 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/GroupMgmtCipherMask.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/GroupMgmtCipherMask.aidl
@@ -34,7 +34,7 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum GroupMgmtCipherMask {
-  BIP_GMAC_128 = 2048,
-  BIP_GMAC_256 = 4096,
-  BIP_CMAC_256 = 8192,
+  BIP_GMAC_128 = (1 << 11) /* 2048 */,
+  BIP_GMAC_256 = (1 << 12) /* 4096 */,
+  BIP_CMAC_256 = (1 << 13) /* 8192 */,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantP2pIfaceCallback.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantP2pIfaceCallback.aidl
index da3ca52..80d8546 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantP2pIfaceCallback.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantP2pIfaceCallback.aidl
@@ -48,9 +48,17 @@
   oneway void onProvisionDiscoveryCompleted(in byte[] p2pDeviceAddress, in boolean isRequest, in android.hardware.wifi.supplicant.P2pProvDiscStatusCode status, in android.hardware.wifi.supplicant.WpsConfigMethods configMethods, in String generatedPin);
   oneway void onR2DeviceFound(in byte[] srcAddress, in byte[] p2pDeviceAddress, in byte[] primaryDeviceType, in String deviceName, in android.hardware.wifi.supplicant.WpsConfigMethods configMethods, in byte deviceCapabilities, in android.hardware.wifi.supplicant.P2pGroupCapabilityMask groupCapabilities, in byte[] wfdDeviceInfo, in byte[] wfdR2DeviceInfo);
   oneway void onServiceDiscoveryResponse(in byte[] srcAddress, in char updateIndicator, in byte[] tlvs);
+  /**
+   * @deprecated This callback is deprecated from AIDL v3, newer HAL should call onPeerClientJoined()
+   */
   oneway void onStaAuthorized(in byte[] srcAddress, in byte[] p2pDeviceAddress);
+  /**
+   * @deprecated This callback is deprecated from AIDL v3, newer HAL should call onPeerClientDisconnected()
+   */
   oneway void onStaDeauthorized(in byte[] srcAddress, in byte[] p2pDeviceAddress);
   oneway void onGroupFrequencyChanged(in String groupIfname, in int frequency);
   oneway void onDeviceFoundWithVendorElements(in byte[] srcAddress, in byte[] p2pDeviceAddress, in byte[] primaryDeviceType, in String deviceName, in android.hardware.wifi.supplicant.WpsConfigMethods configMethods, in byte deviceCapabilities, in android.hardware.wifi.supplicant.P2pGroupCapabilityMask groupCapabilities, in byte[] wfdDeviceInfo, in byte[] wfdR2DeviceInfo, in byte[] vendorElemBytes);
   oneway void onGroupStartedWithParams(in android.hardware.wifi.supplicant.P2pGroupStartedEventParams groupStartedEventParams);
+  oneway void onPeerClientJoined(in android.hardware.wifi.supplicant.P2pPeerClientJoinedEventParams clientJoinedEventParams);
+  oneway void onPeerClientDisconnected(in android.hardware.wifi.supplicant.P2pPeerClientDisconnectedEventParams clientDisconnectedEventParams);
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/IfaceType.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/IfaceType.aidl
index 557dbd7..e11c2f7 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/IfaceType.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/IfaceType.aidl
@@ -34,6 +34,6 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum IfaceType {
-  STA = 0,
-  P2P = 1,
+  STA,
+  P2P,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/IpVersion.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/IpVersion.aidl
index f571b44..9580314 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/IpVersion.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/IpVersion.aidl
@@ -34,6 +34,6 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="byte") @VintfStability
 enum IpVersion {
-  VERSION_4 = 0,
-  VERSION_6 = 1,
+  VERSION_4,
+  VERSION_6,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/KeyMgmtMask.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/KeyMgmtMask.aidl
index 7228480..35d51bc 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/KeyMgmtMask.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/KeyMgmtMask.aidl
@@ -34,21 +34,21 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum KeyMgmtMask {
-  WPA_EAP = 1,
-  WPA_PSK = 2,
-  NONE = 4,
-  IEEE8021X = 8,
-  FT_EAP = 32,
-  FT_PSK = 64,
-  OSEN = 32768,
-  WPA_EAP_SHA256 = 128,
-  WPA_PSK_SHA256 = 256,
-  SAE = 1024,
-  SUITE_B_192 = 131072,
-  OWE = 4194304,
-  DPP = 8388608,
-  WAPI_PSK = 4096,
-  WAPI_CERT = 8192,
-  FILS_SHA256 = 262144,
-  FILS_SHA384 = 524288,
+  WPA_EAP = (1 << 0) /* 1 */,
+  WPA_PSK = (1 << 1) /* 2 */,
+  NONE = (1 << 2) /* 4 */,
+  IEEE8021X = (1 << 3) /* 8 */,
+  FT_EAP = (1 << 5) /* 32 */,
+  FT_PSK = (1 << 6) /* 64 */,
+  OSEN = (1 << 15) /* 32768 */,
+  WPA_EAP_SHA256 = (1 << 7) /* 128 */,
+  WPA_PSK_SHA256 = (1 << 8) /* 256 */,
+  SAE = (1 << 10) /* 1024 */,
+  SUITE_B_192 = (1 << 17) /* 131072 */,
+  OWE = (1 << 22) /* 4194304 */,
+  DPP = (1 << 23) /* 8388608 */,
+  WAPI_PSK = (1 << 12) /* 4096 */,
+  WAPI_CERT = (1 << 13) /* 8192 */,
+  FILS_SHA256 = (1 << 18) /* 262144 */,
+  FILS_SHA384 = (1 << 19) /* 524288 */,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/OcspType.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/OcspType.aidl
index 89de811..d5ed084 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/OcspType.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/OcspType.aidl
@@ -34,8 +34,8 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum OcspType {
-  NONE = 0,
-  REQUEST_CERT_STATUS = 1,
-  REQUIRE_CERT_STATUS = 2,
-  REQUIRE_ALL_CERTS_STATUS = 3,
+  NONE,
+  REQUEST_CERT_STATUS,
+  REQUIRE_CERT_STATUS,
+  REQUIRE_ALL_CERTS_STATUS,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/P2pFrameTypeMask.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/P2pFrameTypeMask.aidl
index 6e1b957..3c6f8ed 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/P2pFrameTypeMask.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/P2pFrameTypeMask.aidl
@@ -34,17 +34,17 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum P2pFrameTypeMask {
-  P2P_FRAME_PROBE_REQ_P2P = 1,
-  P2P_FRAME_PROBE_RESP_P2P = 2,
-  P2P_FRAME_PROBE_RESP_P2P_GO = 4,
-  P2P_FRAME_BEACON_P2P_GO = 8,
-  P2P_FRAME_P2P_PD_REQ = 16,
-  P2P_FRAME_P2P_PD_RESP = 32,
-  P2P_FRAME_P2P_GO_NEG_REQ = 64,
-  P2P_FRAME_P2P_GO_NEG_RESP = 128,
-  P2P_FRAME_P2P_GO_NEG_CONF = 256,
-  P2P_FRAME_P2P_INV_REQ = 512,
-  P2P_FRAME_P2P_INV_RESP = 1024,
-  P2P_FRAME_P2P_ASSOC_REQ = 2048,
-  P2P_FRAME_P2P_ASSOC_RESP = 4096,
+  P2P_FRAME_PROBE_REQ_P2P = (1 << 0) /* 1 */,
+  P2P_FRAME_PROBE_RESP_P2P = (1 << 1) /* 2 */,
+  P2P_FRAME_PROBE_RESP_P2P_GO = (1 << 2) /* 4 */,
+  P2P_FRAME_BEACON_P2P_GO = (1 << 3) /* 8 */,
+  P2P_FRAME_P2P_PD_REQ = (1 << 4) /* 16 */,
+  P2P_FRAME_P2P_PD_RESP = (1 << 5) /* 32 */,
+  P2P_FRAME_P2P_GO_NEG_REQ = (1 << 6) /* 64 */,
+  P2P_FRAME_P2P_GO_NEG_RESP = (1 << 7) /* 128 */,
+  P2P_FRAME_P2P_GO_NEG_CONF = (1 << 8) /* 256 */,
+  P2P_FRAME_P2P_INV_REQ = (1 << 9) /* 512 */,
+  P2P_FRAME_P2P_INV_RESP = (1 << 10) /* 1024 */,
+  P2P_FRAME_P2P_ASSOC_REQ = (1 << 11) /* 2048 */,
+  P2P_FRAME_P2P_ASSOC_RESP = (1 << 12) /* 4096 */,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/P2pGroupCapabilityMask.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/P2pGroupCapabilityMask.aidl
index ffee12c..e477131 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/P2pGroupCapabilityMask.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/P2pGroupCapabilityMask.aidl
@@ -34,11 +34,11 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum P2pGroupCapabilityMask {
-  GROUP_OWNER = 1,
-  PERSISTENT_GROUP = 2,
-  GROUP_LIMIT = 4,
-  INTRA_BSS_DIST = 8,
-  CROSS_CONN = 16,
-  PERSISTENT_RECONN = 32,
-  GROUP_FORMATION = 64,
+  GROUP_OWNER = (1 << 0) /* 1 */,
+  PERSISTENT_GROUP = (1 << 1) /* 2 */,
+  GROUP_LIMIT = (1 << 2) /* 4 */,
+  INTRA_BSS_DIST = (1 << 3) /* 8 */,
+  CROSS_CONN = (1 << 4) /* 16 */,
+  PERSISTENT_RECONN = (1 << 5) /* 32 */,
+  GROUP_FORMATION = (1 << 6) /* 64 */,
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/P2pPeerClientDisconnectedEventParams.aidl
similarity index 85%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
copy to wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/P2pPeerClientDisconnectedEventParams.aidl
index b9df1f5..90e9f5e 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/P2pPeerClientDisconnectedEventParams.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,10 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
-/* @hide */
-@Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+package android.hardware.wifi.supplicant;
+@VintfStability
+parcelable P2pPeerClientDisconnectedEventParams {
+  String groupInterfaceName;
+  byte[6] clientInterfaceAddress;
+  byte[6] clientDeviceAddress;
 }
diff --git a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/P2pPeerClientJoinedEventParams.aidl
similarity index 85%
copy from radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
copy to wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/P2pPeerClientJoinedEventParams.aidl
index b9df1f5..800f5b3 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.satellite/current/android/hardware/radio/satellite/IndicationFilter.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/P2pPeerClientJoinedEventParams.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,10 +31,11 @@
 // with such a backward incompatible change, it has a high risk of breaking
 // later when a module using the interface is updated, e.g., Mainline modules.
 
-package android.hardware.radio.satellite;
-/* @hide */
-@Backing(type="int") @JavaDerive(toString=true) @VintfStability
-enum IndicationFilter {
-  NONE = 0,
-  SATELLITE_MODE = 1,
+package android.hardware.wifi.supplicant;
+@VintfStability
+parcelable P2pPeerClientJoinedEventParams {
+  String groupInterfaceName;
+  byte[6] clientInterfaceAddress;
+  byte[6] clientDeviceAddress;
+  int clientIpAddress;
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/PairwiseCipherMask.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/PairwiseCipherMask.aidl
index d9b00e1..a4c7b60 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/PairwiseCipherMask.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/PairwiseCipherMask.aidl
@@ -34,10 +34,10 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum PairwiseCipherMask {
-  NONE = 1,
-  TKIP = 8,
-  CCMP = 16,
-  GCMP_128 = 64,
-  SMS4 = 128,
-  GCMP_256 = 256,
+  NONE = (1 << 0) /* 1 */,
+  TKIP = (1 << 3) /* 8 */,
+  CCMP = (1 << 4) /* 16 */,
+  GCMP_128 = (1 << 6) /* 64 */,
+  SMS4 = (1 << 7) /* 128 */,
+  GCMP_256 = (1 << 8) /* 256 */,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ProtoMask.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ProtoMask.aidl
index de92428..ba79025 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ProtoMask.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ProtoMask.aidl
@@ -34,8 +34,8 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum ProtoMask {
-  WPA = 1,
-  RSN = 2,
-  WAPI = 4,
-  OSEN = 8,
+  WPA = (1 << 0) /* 1 */,
+  RSN = (1 << 1) /* 2 */,
+  WAPI = (1 << 2) /* 4 */,
+  OSEN = (1 << 3) /* 8 */,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyClassifierParamsMask.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyClassifierParamsMask.aidl
index 9c0c0b6..fda5e3e 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyClassifierParamsMask.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyClassifierParamsMask.aidl
@@ -34,12 +34,12 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum QosPolicyClassifierParamsMask {
-  SRC_IP = 1,
-  DST_IP = 2,
-  SRC_PORT = 4,
-  DST_PORT_RANGE = 8,
-  PROTOCOL_NEXT_HEADER = 16,
-  FLOW_LABEL = 32,
-  DOMAIN_NAME = 64,
-  DSCP = 128,
+  SRC_IP = (1 << 0) /* 1 */,
+  DST_IP = (1 << 1) /* 2 */,
+  SRC_PORT = (1 << 2) /* 4 */,
+  DST_PORT_RANGE = (1 << 3) /* 8 */,
+  PROTOCOL_NEXT_HEADER = (1 << 4) /* 16 */,
+  FLOW_LABEL = (1 << 5) /* 32 */,
+  DOMAIN_NAME = (1 << 6) /* 64 */,
+  DSCP = (1 << 7) /* 128 */,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyRequestType.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyRequestType.aidl
index 4c1e4fa..fd4e787 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyRequestType.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyRequestType.aidl
@@ -34,6 +34,6 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="byte") @VintfStability
 enum QosPolicyRequestType {
-  QOS_POLICY_ADD = 0,
-  QOS_POLICY_REMOVE = 1,
+  QOS_POLICY_ADD,
+  QOS_POLICY_REMOVE,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyScsRequestStatusCode.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyScsRequestStatusCode.aidl
index 4d81566..8e0467f 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyScsRequestStatusCode.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyScsRequestStatusCode.aidl
@@ -19,8 +19,8 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum QosPolicyScsRequestStatusCode {
-  SENT = 0,
-  ALREADY_ACTIVE = 1,
-  NOT_EXIST = 2,
-  INVALID = 3,
+  SENT,
+  ALREADY_ACTIVE,
+  NOT_EXIST,
+  INVALID,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyScsResponseStatusCode.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyScsResponseStatusCode.aidl
index 693d3e0..5d460c6 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyScsResponseStatusCode.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyScsResponseStatusCode.aidl
@@ -19,13 +19,13 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum QosPolicyScsResponseStatusCode {
-  SUCCESS = 0,
-  TCLAS_REQUEST_DECLINED = 1,
-  TCLAS_NOT_SUPPORTED_BY_AP = 2,
-  TCLAS_INSUFFICIENT_RESOURCES = 3,
-  TCLAS_RESOURCES_EXHAUSTED = 4,
-  TCLAS_PROCESSING_TERMINATED_INSUFFICIENT_QOS = 5,
-  TCLAS_PROCESSING_TERMINATED_POLICY_CONFLICT = 6,
-  TCLAS_PROCESSING_TERMINATED = 7,
-  TIMEOUT = 8,
+  SUCCESS,
+  TCLAS_REQUEST_DECLINED,
+  TCLAS_NOT_SUPPORTED_BY_AP,
+  TCLAS_INSUFFICIENT_RESOURCES,
+  TCLAS_RESOURCES_EXHAUSTED,
+  TCLAS_PROCESSING_TERMINATED_INSUFFICIENT_QOS,
+  TCLAS_PROCESSING_TERMINATED_POLICY_CONFLICT,
+  TCLAS_PROCESSING_TERMINATED,
+  TIMEOUT,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyStatusCode.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyStatusCode.aidl
index 4d40edc..9228632 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyStatusCode.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/QosPolicyStatusCode.aidl
@@ -34,8 +34,8 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="byte") @VintfStability
 enum QosPolicyStatusCode {
-  QOS_POLICY_SUCCESS = 0,
-  QOS_POLICY_REQUEST_DECLINED = 1,
-  QOS_POLICY_CLASSIFIER_NOT_SUPPORTED = 2,
-  QOS_POLICY_INSUFFICIENT_RESOURCES = 3,
+  QOS_POLICY_SUCCESS,
+  QOS_POLICY_REQUEST_DECLINED,
+  QOS_POLICY_CLASSIFIER_NOT_SUPPORTED,
+  QOS_POLICY_INSUFFICIENT_RESOURCES,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/SaeH2eMode.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/SaeH2eMode.aidl
index 978c337..4730d72 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/SaeH2eMode.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/SaeH2eMode.aidl
@@ -34,7 +34,7 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="byte") @VintfStability
 enum SaeH2eMode {
-  DISABLED = 0,
-  H2E_OPTIONAL = 1,
-  H2E_MANDATORY = 2,
+  DISABLED,
+  H2E_OPTIONAL,
+  H2E_MANDATORY,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/SupplicantStatusCode.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/SupplicantStatusCode.aidl
index d84ff95..d7ff798 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/SupplicantStatusCode.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/SupplicantStatusCode.aidl
@@ -34,16 +34,16 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum SupplicantStatusCode {
-  SUCCESS = 0,
-  FAILURE_UNKNOWN = 1,
-  FAILURE_ARGS_INVALID = 2,
-  FAILURE_IFACE_INVALID = 3,
-  FAILURE_IFACE_UNKNOWN = 4,
-  FAILURE_IFACE_EXISTS = 5,
-  FAILURE_IFACE_DISABLED = 6,
-  FAILURE_IFACE_NOT_DISCONNECTED = 7,
-  FAILURE_NETWORK_INVALID = 8,
-  FAILURE_NETWORK_UNKNOWN = 9,
-  FAILURE_UNSUPPORTED = 10,
-  FAILURE_ONGOING_REQUEST = 11,
+  SUCCESS,
+  FAILURE_UNKNOWN,
+  FAILURE_ARGS_INVALID,
+  FAILURE_IFACE_INVALID,
+  FAILURE_IFACE_UNKNOWN,
+  FAILURE_IFACE_EXISTS,
+  FAILURE_IFACE_DISABLED,
+  FAILURE_IFACE_NOT_DISCONNECTED,
+  FAILURE_NETWORK_INVALID,
+  FAILURE_NETWORK_UNKNOWN,
+  FAILURE_UNSUPPORTED,
+  FAILURE_ONGOING_REQUEST,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/TlsVersion.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/TlsVersion.aidl
index 22a374f..b31826a 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/TlsVersion.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/TlsVersion.aidl
@@ -34,8 +34,8 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum TlsVersion {
-  TLS_V1_0 = 0,
-  TLS_V1_1 = 1,
-  TLS_V1_2 = 2,
-  TLS_V1_3 = 3,
+  TLS_V1_0,
+  TLS_V1_1,
+  TLS_V1_2,
+  TLS_V1_3,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/TransitionDisableIndication.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/TransitionDisableIndication.aidl
index 7c63217..f1d7370 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/TransitionDisableIndication.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/TransitionDisableIndication.aidl
@@ -34,8 +34,8 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum TransitionDisableIndication {
-  USE_WPA3_PERSONAL = 1,
-  USE_SAE_PK = 2,
-  USE_WPA3_ENTERPRISE = 4,
-  USE_ENHANCED_OPEN = 8,
+  USE_WPA3_PERSONAL = (1 << 0) /* 1 */,
+  USE_SAE_PK = (1 << 1) /* 2 */,
+  USE_WPA3_ENTERPRISE = (1 << 2) /* 4 */,
+  USE_ENHANCED_OPEN = (1 << 3) /* 8 */,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/WpaDriverCapabilitiesMask.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/WpaDriverCapabilitiesMask.aidl
index 32e1510..330f2aa 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/WpaDriverCapabilitiesMask.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/WpaDriverCapabilitiesMask.aidl
@@ -34,11 +34,11 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum WpaDriverCapabilitiesMask {
-  MBO = 1,
-  OCE = 2,
-  SAE_PK = 4,
-  WFD_R2 = 8,
-  TRUST_ON_FIRST_USE = 16,
-  SET_TLS_MINIMUM_VERSION = 32,
-  TLS_V1_3 = 64,
+  MBO = (1 << 0) /* 1 */,
+  OCE = (1 << 1) /* 2 */,
+  SAE_PK = (1 << 2) /* 4 */,
+  WFD_R2 = (1 << 3) /* 8 */,
+  TRUST_ON_FIRST_USE = (1 << 4) /* 16 */,
+  SET_TLS_MINIMUM_VERSION = (1 << 5) /* 32 */,
+  TLS_V1_3 = (1 << 6) /* 64 */,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/WpsConfigMethods.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/WpsConfigMethods.aidl
index c98c479..b9ea211 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/WpsConfigMethods.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/WpsConfigMethods.aidl
@@ -34,18 +34,18 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum WpsConfigMethods {
-  USBA = 1,
-  ETHERNET = 2,
-  LABEL = 4,
-  DISPLAY = 8,
-  EXT_NFC_TOKEN = 16,
-  INT_NFC_TOKEN = 32,
-  NFC_INTERFACE = 64,
-  PUSHBUTTON = 128,
-  KEYPAD = 256,
-  VIRT_PUSHBUTTON = 640,
-  PHY_PUSHBUTTON = 1152,
-  P2PS = 4096,
-  VIRT_DISPLAY = 8200,
-  PHY_DISPLAY = 16392,
+  USBA = 0x0001,
+  ETHERNET = 0x0002,
+  LABEL = 0x0004,
+  DISPLAY = 0x0008,
+  EXT_NFC_TOKEN = 0x0010,
+  INT_NFC_TOKEN = 0x0020,
+  NFC_INTERFACE = 0x0040,
+  PUSHBUTTON = 0x0080,
+  KEYPAD = 0x0100,
+  VIRT_PUSHBUTTON = 0x0280,
+  PHY_PUSHBUTTON = 0x0480,
+  P2PS = 0x1000,
+  VIRT_DISPLAY = 0x2008,
+  PHY_DISPLAY = 0x4008,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/WpsDevPasswordId.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/WpsDevPasswordId.aidl
index 975f1ab..9a20187 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/WpsDevPasswordId.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/WpsDevPasswordId.aidl
@@ -34,12 +34,12 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum WpsDevPasswordId {
-  DEFAULT = 0,
-  USER_SPECIFIED = 1,
-  MACHINE_SPECIFIED = 2,
-  REKEY = 3,
-  PUSHBUTTON = 4,
-  REGISTRAR_SPECIFIED = 5,
-  NFC_CONNECTION_HANDOVER = 7,
-  P2PS_DEFAULT = 8,
+  DEFAULT = 0x0000,
+  USER_SPECIFIED = 0x0001,
+  MACHINE_SPECIFIED = 0x0002,
+  REKEY = 0x0003,
+  PUSHBUTTON = 0x0004,
+  REGISTRAR_SPECIFIED = 0x0005,
+  NFC_CONNECTION_HANDOVER = 0x0007,
+  P2PS_DEFAULT = 0x0008,
 }
diff --git a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/WpsProvisionMethod.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/WpsProvisionMethod.aidl
index f6dba23..177d218 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/WpsProvisionMethod.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/WpsProvisionMethod.aidl
@@ -34,7 +34,7 @@
 package android.hardware.wifi.supplicant;
 @Backing(type="int") @VintfStability
 enum WpsProvisionMethod {
-  PBC = 0,
-  DISPLAY = 1,
-  KEYPAD = 2,
+  PBC,
+  DISPLAY,
+  KEYPAD,
 }
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantP2pIfaceCallback.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantP2pIfaceCallback.aidl
index 9d6fa67..810fe48 100644
--- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantP2pIfaceCallback.aidl
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantP2pIfaceCallback.aidl
@@ -18,6 +18,8 @@
 
 import android.hardware.wifi.supplicant.P2pGroupCapabilityMask;
 import android.hardware.wifi.supplicant.P2pGroupStartedEventParams;
+import android.hardware.wifi.supplicant.P2pPeerClientDisconnectedEventParams;
+import android.hardware.wifi.supplicant.P2pPeerClientJoinedEventParams;
 import android.hardware.wifi.supplicant.P2pProvDiscStatusCode;
 import android.hardware.wifi.supplicant.P2pStatusCode;
 import android.hardware.wifi.supplicant.WpsConfigMethods;
@@ -192,6 +194,9 @@
 
     /**
      * Used to indicate when a STA device is connected to this device.
+     * <p>
+     * @deprecated This callback is deprecated from AIDL v3, newer HAL should call
+     * onPeerClientJoined()
      *
      * @param srcAddress MAC address of the device that was authorized.
      * @param p2pDeviceAddress P2P device address.
@@ -200,6 +205,9 @@
 
     /**
      * Used to indicate when a STA device is disconnected from this device.
+     * <p>
+     * @deprecated This callback is deprecated from AIDL v3, newer HAL should call
+     * onPeerClientDisconnected()
      *
      * @param srcAddress MAC address of the device that was deauthorized.
      * @param p2pDeviceAddress P2P device address.
@@ -251,4 +259,20 @@
      * @param groupStartedEventParams Parameters describing the P2P group.
      */
     void onGroupStartedWithParams(in P2pGroupStartedEventParams groupStartedEventParams);
+
+    /**
+     * Used to indicate that a P2P client has joined this device group owner.
+     *
+     * @param clientJoinedEventParams Parameters associated with peer client joined event.
+     */
+    void onPeerClientJoined(in P2pPeerClientJoinedEventParams clientJoinedEventParams);
+
+    /**
+     * Used to indicate that a P2P client has disconnected from this device group owner.
+     *
+     * @param clientDisconnectedEventParams Parameters associated with peer client disconnected
+     *         event.
+     */
+    void onPeerClientDisconnected(
+            in P2pPeerClientDisconnectedEventParams clientDisconnectedEventParams);
 }
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/P2pPeerClientDisconnectedEventParams.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/P2pPeerClientDisconnectedEventParams.aidl
new file mode 100644
index 0000000..936efd1
--- /dev/null
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/P2pPeerClientDisconnectedEventParams.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.wifi.supplicant;
+
+/**
+ * Parameters passed as a part of P2P peer client disconnected event.
+ */
+@VintfStability
+parcelable P2pPeerClientDisconnectedEventParams {
+    /** Interface name of this device group owner. (For ex: p2p-p2p0-1) */
+    String groupInterfaceName;
+
+    /** P2P group interface MAC address of the client that disconnected. */
+    byte[6] clientInterfaceAddress;
+
+    /** P2P device interface MAC address of the client that disconnected. */
+    byte[6] clientDeviceAddress;
+}
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/P2pPeerClientJoinedEventParams.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/P2pPeerClientJoinedEventParams.aidl
new file mode 100644
index 0000000..7eae2e5
--- /dev/null
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/P2pPeerClientJoinedEventParams.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.wifi.supplicant;
+
+/**
+ * Parameters passed as a part of P2P peer client joined event.
+ */
+@VintfStability
+parcelable P2pPeerClientJoinedEventParams {
+    /** Interface name of this device group owner. (For ex: p2p-p2p0-1) */
+    String groupInterfaceName;
+
+    /** P2P group interface MAC address of the client that joined. */
+    byte[6] clientInterfaceAddress;
+
+    /** P2P device interface MAC address of the client that joined. */
+    byte[6] clientDeviceAddress;
+
+    /**
+     * The P2P Client IPV4 address allocated via EAPOL exchange.
+     * The higher-order address bytes are in the lower-order int bytes
+     * (e.g. 1.2.3.4 is represented as 0x04030201).
+     * Refer Wi-Fi P2P Technical Specification v1.7 - Section  4.2.8
+     * "IP Address Allocation in EAPOL-Key Frames (4-Way Handshake)" for more details.
+     * The value is set to zero if the IP address is not allocated via EAPOL exchange.
+     */
+    int clientIpAddress;
+}
diff --git a/wifi/supplicant/aidl/vts/functional/Android.bp b/wifi/supplicant/aidl/vts/functional/Android.bp
index f7c619a..4eec180 100644
--- a/wifi/supplicant/aidl/vts/functional/Android.bp
+++ b/wifi/supplicant/aidl/vts/functional/Android.bp
@@ -44,7 +44,7 @@
         "android.hardware.wifi@1.5",
         "android.hardware.wifi.supplicant@1.0",
         "android.hardware.wifi.supplicant@1.1",
-        "android.hardware.wifi.supplicant-V2-ndk",
+        "android.hardware.wifi.supplicant-V3-ndk",
         "libwifi-system",
         "libwifi-system-iface",
         "VtsHalWifiV1_0TargetTestUtil",
@@ -80,7 +80,7 @@
         "android.hardware.wifi@1.5",
         "android.hardware.wifi.supplicant@1.0",
         "android.hardware.wifi.supplicant@1.1",
-        "android.hardware.wifi.supplicant-V2-ndk",
+        "android.hardware.wifi.supplicant-V3-ndk",
         "libwifi-system",
         "libwifi-system-iface",
         "VtsHalWifiV1_0TargetTestUtil",
@@ -116,7 +116,7 @@
         "android.hardware.wifi@1.5",
         "android.hardware.wifi.supplicant@1.0",
         "android.hardware.wifi.supplicant@1.1",
-        "android.hardware.wifi.supplicant-V2-ndk",
+        "android.hardware.wifi.supplicant-V3-ndk",
         "libwifi-system",
         "libwifi-system-iface",
         "VtsHalWifiV1_0TargetTestUtil",
diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp b/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp
index a260408..d3dd2e0 100644
--- a/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp
+++ b/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp
@@ -38,6 +38,8 @@
 using aidl::android::hardware::wifi::supplicant::P2pFrameTypeMask;
 using aidl::android::hardware::wifi::supplicant::P2pGroupCapabilityMask;
 using aidl::android::hardware::wifi::supplicant::P2pGroupStartedEventParams;
+using aidl::android::hardware::wifi::supplicant::P2pPeerClientDisconnectedEventParams;
+using aidl::android::hardware::wifi::supplicant::P2pPeerClientJoinedEventParams;
 using aidl::android::hardware::wifi::supplicant::P2pProvDiscStatusCode;
 using aidl::android::hardware::wifi::supplicant::P2pStatusCode;
 using aidl::android::hardware::wifi::supplicant::SupplicantStatusCode;
@@ -182,6 +184,15 @@
             const P2pGroupStartedEventParams& /* groupStartedEventParams */) override {
         return ndk::ScopedAStatus::ok();
     }
+    ::ndk::ScopedAStatus onPeerClientJoined(
+            const P2pPeerClientJoinedEventParams& /* clientJoinedEventParams */) override {
+        return ndk::ScopedAStatus::ok();
+    }
+    ::ndk::ScopedAStatus onPeerClientDisconnected(
+            const P2pPeerClientDisconnectedEventParams& /* clientDisconnectedEventParams */)
+            override {
+        return ndk::ScopedAStatus::ok();
+    }
 };
 
 class SupplicantP2pIfaceAidlTest : public testing::TestWithParam<std::string> {