Merge changes from topic "bt_socket_offload_hal" into main am: d34cb3bbe6

Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/3370079

Change-Id: I2b9dd539b2fa37870d94340736c1e2df5a268547
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/HapticGenerator.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/HapticGenerator.aidl
index 8addab7..aa1a86f 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/HapticGenerator.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/HapticGenerator.aidl
@@ -55,6 +55,9 @@
   parcelable HapticScale {
     int id;
     android.hardware.audio.effect.HapticGenerator.VibratorScale scale = android.hardware.audio.effect.HapticGenerator.VibratorScale.MUTE;
+    float scaleFactor = (-1.0f) /* -1.000000f */;
+    float adaptiveScaleFactor = (-1.0f) /* -1.000000f */;
+    const float UNDEFINED_SCALE_FACTOR = (-1.0f) /* -1.000000f */;
   }
   @VintfStability
   parcelable VibratorInformation {
diff --git a/audio/aidl/android/hardware/audio/effect/HapticGenerator.aidl b/audio/aidl/android/hardware/audio/effect/HapticGenerator.aidl
index 3cc5acb..f882d63 100644
--- a/audio/aidl/android/hardware/audio/effect/HapticGenerator.aidl
+++ b/audio/aidl/android/hardware/audio/effect/HapticGenerator.aidl
@@ -56,13 +56,51 @@
     @VintfStability
     parcelable HapticScale {
         /**
+         * Representation of undefined scale factor, applied by default for backwards compatibility.
+         */
+        const float UNDEFINED_SCALE_FACTOR = -1.0f;
+
+        /**
          * Audio track ID.
          */
         int id;
+
         /**
          * Haptic intensity.
+         *
+         * This represents haptics scale as fixed levels defined by VibrationScale. If the field
+         * scaleFactor is defined then this will be ignored in favor of scaleFactor, otherwise this
+         * will be used to define the intensity for the haptics.
          */
         VibratorScale scale = VibratorScale.MUTE;
+
+        /**
+         * Haptic scale factor.
+         *
+         * This is a continuous scale representation of VibratorScale, allowing flexible number of
+         * scale levels. If this field is defined then it will be used to define the intensity of
+         * the haptics, instead of the old VibratorScale field. If this field is undefined then the
+         * old VibratorScale field will be used.
+         *
+         * The value zero represents the same as VibratorScale.MUTE and the value one represents
+         * VibratorScale.NONE. Values in (0,1) should scale down, and values > 1 should scale up
+         * within hardware bounds. Negative values will be ignored.
+         */
+        float scaleFactor = -1.0f; // UNDEFINED_SCALE_FACTOR
+
+        /**
+         * Haptic adaptive scale factor.
+         *
+         * This is an additional scale value that should be applied on top of the vibrator scale to
+         * adapt to the device current state. This should be applied to linearly scale the haptic
+         * data after scale/scaleFactor is applied.
+         *
+         * The value zero mutes the haptics, even if the scale/scaleFactor are not set to MUTE/zero.
+         * The value one will not scale the haptics, and can be used as a constant for no-op.
+         * Values in (0,1) should scale down. Values > 1 should scale up within hardware bounds.
+         * Negative values will be ignored.
+         */
+        float adaptiveScaleFactor = -1.0f; // UNDEFINED_SCALE_FACTOR
     }
 
     /**
diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp
index 4a71be9..586c863 100644
--- a/audio/aidl/vts/Android.bp
+++ b/audio/aidl/vts/Android.bp
@@ -139,6 +139,9 @@
     name: "VtsHalHapticGeneratorTargetTest",
     defaults: ["VtsHalAudioEffectTargetTestDefaults"],
     srcs: ["VtsHalHapticGeneratorTargetTest.cpp"],
+    shared_libs: [
+        "libaudioutils",
+    ],
 }
 
 cc_test {
diff --git a/audio/aidl/vts/EffectHelper.h b/audio/aidl/vts/EffectHelper.h
index 570ecef..ace6a82 100644
--- a/audio/aidl/vts/EffectHelper.h
+++ b/audio/aidl/vts/EffectHelper.h
@@ -484,6 +484,17 @@
         mOutputSamples = common.output.frameCount * mOutputFrameSize / sizeof(float);
     }
 
+    void generateInput(std::vector<float>& input, float inputFrequency, float samplingFrequency,
+                       size_t inputSize = 0) {
+        if (inputSize == 0 || inputSize > input.size()) {
+            inputSize = input.size();
+        }
+
+        for (size_t i = 0; i < inputSize; i++) {
+            input[i] = sin(2 * M_PI * inputFrequency * i / samplingFrequency);
+        }
+    }
+
     bool mIsSpatializer;
     Descriptor mDescriptor;
     size_t mInputFrameSize, mOutputFrameSize;
diff --git a/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp b/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
index 2b6dc3d..2802bf9 100644
--- a/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
@@ -21,11 +21,13 @@
 #define LOG_TAG "VtsHalHapticGeneratorTargetTest"
 #include <android-base/logging.h>
 #include <android/binder_enums.h>
+#include <audio_utils/power.h>
 
 #include "EffectHelper.h"
 
 using namespace android;
 
+using aidl::android::hardware::audio::common::getChannelCount;
 using aidl::android::hardware::audio::effect::Descriptor;
 using aidl::android::hardware::audio::effect::getEffectTypeUuidHapticGenerator;
 using aidl::android::hardware::audio::effect::HapticGenerator;
@@ -34,406 +36,412 @@
 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_HAPTIC_SCALE_ID,
-    PARAM_HAPTIC_SCALE_VIBRATOR_SCALE,
-    PARAM_VIBRATION_INFORMATION_RESONANT_FREQUENCY,
-    PARAM_VIBRATION_INFORMATION_Q_FACTOR,
-    PARAM_VIBRATION_INFORMATION_MAX_AMPLITUDE,
-};
-using HapticGeneratorParamTestParam =
-        std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int,
-                   HapticGenerator::VibratorScale, float, float, float>;
-
-/*
- * Testing parameter range, assuming the parameter supported by effect is in this range.
- * Parameter should be within the valid range defined in the documentation,
- * for any supported value test expects EX_NONE from IEffect.setParameter(),
- * otherwise expect EX_ILLEGAL_ARGUMENT.
- */
-
-// TODO : Update the test values once range/capability is updated by implementation
 const int MIN_ID = std::numeric_limits<int>::min();
 const int MAX_ID = std::numeric_limits<int>::max();
 const float MIN_FLOAT = std::numeric_limits<float>::min();
 const float MAX_FLOAT = std::numeric_limits<float>::max();
 
-const std::vector<int> kHapticScaleIdValues = {MIN_ID, 0, MAX_ID};
-const std::vector<HapticGenerator::VibratorScale> kVibratorScaleValues = {
+std::vector<HapticGenerator::VibratorScale> kScaleValues = {
         ndk::enum_range<HapticGenerator::VibratorScale>().begin(),
         ndk::enum_range<HapticGenerator::VibratorScale>().end()};
 
+const std::vector<float> kScaleFactorValues = {HapticGenerator::HapticScale::UNDEFINED_SCALE_FACTOR,
+                                               0.0f, 0.5f, 1.0f, MAX_FLOAT};
+const std::vector<float> kAdaptiveScaleFactorValues = {
+        HapticGenerator::HapticScale::UNDEFINED_SCALE_FACTOR, 0.0f, 0.5f, 1.0f, MAX_FLOAT};
+
 const std::vector<float> kResonantFrequencyValues = {MIN_FLOAT, 100, MAX_FLOAT};
 const std::vector<float> kQFactorValues = {MIN_FLOAT, 100, MAX_FLOAT};
 const std::vector<float> kMaxAmplitude = {MIN_FLOAT, 100, MAX_FLOAT};
 
-class HapticGeneratorParamTest : public ::testing::TestWithParam<HapticGeneratorParamTestParam>,
-                                 public EffectHelper {
+constexpr int HAPTIC_SCALE_FACTORS_EFFECT_MIN_VERSION = 3;
+
+static const std::vector<int32_t> kHapticOutputLayouts = {
+        AudioChannelLayout::LAYOUT_MONO_HAPTIC_A, AudioChannelLayout::LAYOUT_MONO_HAPTIC_AB,
+        AudioChannelLayout::LAYOUT_STEREO_HAPTIC_A, AudioChannelLayout::LAYOUT_STEREO_HAPTIC_AB};
+
+class HapticGeneratorHelper : public EffectHelper {
   public:
-    HapticGeneratorParamTest()
-        : mParamHapticScaleId(std::get<PARAM_HAPTIC_SCALE_ID>(GetParam())),
-          mParamVibratorScale(std::get<PARAM_HAPTIC_SCALE_VIBRATOR_SCALE>(GetParam())),
-          mParamResonantFrequency(
-                  std::get<PARAM_VIBRATION_INFORMATION_RESONANT_FREQUENCY>(GetParam())),
-          mParamQFactor(std::get<PARAM_VIBRATION_INFORMATION_Q_FACTOR>(GetParam())),
-          mParamMaxAmplitude(std::get<PARAM_VIBRATION_INFORMATION_MAX_AMPLITUDE>(GetParam())) {
-        std::tie(mFactory, mDescriptor) = std::get<PARAM_INSTANCE_NAME>(GetParam());
-    }
-    void SetUp() override {
+    void SetUpHapticGenerator(int32_t chMask = AudioChannelLayout::CHANNEL_HAPTIC_A) {
         ASSERT_NE(nullptr, mFactory);
         ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
+        EXPECT_STATUS(EX_NONE, mEffect->getInterfaceVersion(&mEffectInterfaceVersion));
+
+        AudioChannelLayout layout =
+                AudioChannelLayout::make<AudioChannelLayout::layoutMask>(chMask);
 
         Parameter::Common common = createParamCommon(
-                0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */,
-                kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */);
-        IEffect::OpenEffectReturn ret;
+                0 /* session */, 1 /* ioHandle */, kSamplingFrequency /* iSampleRate */,
+                kSamplingFrequency /* oSampleRate */, kFrameCount /* iFrameCount */,
+                kFrameCount /* oFrameCount */, layout, layout);
         ASSERT_NO_FATAL_FAILURE(open(mEffect, common, std::nullopt, &ret, EX_NONE));
         ASSERT_NE(nullptr, mEffect);
     }
 
-    void TearDown() override {
+    void TearDownHapticGenerator() {
         ASSERT_NO_FATAL_FAILURE(close(mEffect));
         ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
+        ret = IEffect::OpenEffectReturn{};
     }
 
-    static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100;
+    Parameter createScaleParam(const std::vector<HapticGenerator::HapticScale>& hapticScales) {
+        return Parameter::make<Parameter::specific>(
+                Parameter::Specific::make<Parameter::Specific::hapticGenerator>(
+                        HapticGenerator::make<HapticGenerator::hapticScales>(hapticScales)));
+    }
+
+    Parameter createVibratorParam(HapticGenerator::VibratorInformation vibrationInfo) {
+        return Parameter::make<Parameter::specific>(
+                Parameter::Specific::make<Parameter::Specific::hapticGenerator>(
+                        HapticGenerator::make<HapticGenerator::vibratorInfo>(vibrationInfo)));
+    }
+
+    void setAndVerifyParameter(Parameter hapticParameter, HapticGenerator::Tag tag,
+                               binder_exception_t expected = EX_NONE) {
+        EXPECT_STATUS(expected, mEffect->setParameter(hapticParameter))
+                << hapticParameter.toString();
+        if (expected == EX_NONE) {
+            // get parameter
+            Parameter getParam;
+            auto second = Parameter::Id::make<Parameter::Id::hapticGeneratorTag>(
+                    HapticGenerator::Id::make<HapticGenerator::Id::commonTag>(
+                            HapticGenerator::Tag(tag)));
+            // If the set is successful, get param should match
+            EXPECT_STATUS(expected, mEffect->getParameter(second, &getParam));
+            EXPECT_EQ(hapticParameter, getParam) << "\nexpectedParam:" << hapticParameter.toString()
+                                                 << "\ngetParam:" << getParam.toString();
+        }
+    }
+
+    HapticGenerator::VibratorInformation createVibratorInfo(float resonantFrequency, float qFactor,
+                                                            float amplitude) {
+        return HapticGenerator::VibratorInformation(resonantFrequency, qFactor, amplitude);
+    }
+
+    static const long kFrameCount = 10000;
+    static constexpr int kSamplingFrequency = 44100;
+    static constexpr int kDefaultScaleID = 0;
+    static constexpr float kDefaultMaxAmp = 1;
+    static constexpr float kDefaultResonantFrequency = 150;
+    static constexpr float kDefaultQfactor = 8;
+    static constexpr HapticGenerator::VibratorScale kDefaultScale =
+            HapticGenerator::VibratorScale::NONE;
     std::shared_ptr<IFactory> mFactory;
     std::shared_ptr<IEffect> mEffect;
-    Descriptor mDescriptor;
-    int mParamHapticScaleId = 0;
-    HapticGenerator::VibratorScale mParamVibratorScale = HapticGenerator::VibratorScale::MUTE;
-    float mParamResonantFrequency = 0;
-    float mParamQFactor = 0;
-    float mParamMaxAmplitude = 0;
+    IEffect::OpenEffectReturn ret;
+    Parameter mHapticSpecificParameter;
+    Parameter::Id mHapticIdParameter;
+    int mEffectInterfaceVersion;
+};
 
-    void SetAndGetHapticGeneratorParameters() {
-        for (auto& it : mTags) {
-            auto& tag = std::get<ParamTestEnum::PARAM_TEST_TAG>(it);
-            auto& setHg = std::get<ParamTestEnum::PARAM_TEST_TARGET>(it);
+/**
+ *Tests do the following:
+ * -Testing parameter range supported by the effect.
+ * -For any supported value test expects EX_NONE from IEffect.setParameter(),
+ *  otherwise expect EX_ILLEGAL_ARGUMENT.
+ * -Validating the effect by comparing the output energies of the supported parameters.
+ **/
 
-            // set parameter
-            Parameter expectParam;
-            Parameter::Specific specific;
-            specific.set<Parameter::Specific::hapticGenerator>(setHg);
-            expectParam.set<Parameter::specific>(specific);
+using EffectInstance = std::pair<std::shared_ptr<IFactory>, Descriptor>;
 
-            const bool valid =
-                    isParameterValid<HapticGenerator, Range::hapticGenerator>(setHg, mDescriptor);
-            const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
-            EXPECT_STATUS(expected, mEffect->setParameter(expectParam)) << expectParam.toString();
+class HapticGeneratorScaleParamTest : public ::testing::TestWithParam<EffectInstance>,
+                                      public HapticGeneratorHelper {
+  public:
+    HapticGeneratorScaleParamTest() { std::tie(mFactory, mDescriptor) = GetParam(); }
+    void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpHapticGenerator()); }
+    void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownHapticGenerator()); }
+};
 
-            // only get if parameter in range and set success
-            if (expected == EX_NONE) {
-                Parameter getParam;
-                Parameter::Id id;
-                HapticGenerator::Id hgId;
-                hgId.set<HapticGenerator::Id::commonTag>(tag);
-                id.set<Parameter::Id::hapticGeneratorTag>(hgId);
-                EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam));
-                EXPECT_EQ(expectParam, getParam) << expectParam.toString() << "\n"
-                                                 << getParam.toString();
+TEST_P(HapticGeneratorScaleParamTest, SetAndGetScales) {
+    std::vector<HapticGenerator::HapticScale> hapticScales;
+    for (int i = 0; i < static_cast<int>(kScaleValues.size()); i++) {
+        hapticScales.push_back({.id = i, .scale = kScaleValues[i]});
+    }
+    ASSERT_NO_FATAL_FAILURE(
+            setAndVerifyParameter(createScaleParam(hapticScales), HapticGenerator::hapticScales));
+}
+
+TEST_P(HapticGeneratorScaleParamTest, SetAndGetScaleFactors) {
+    if (mEffectInterfaceVersion < HAPTIC_SCALE_FACTORS_EFFECT_MIN_VERSION) {
+        GTEST_SKIP() << "Skipping HapticGenerator ScaleFactors test for effect version "
+                     << std::to_string(mEffectInterfaceVersion);
+    }
+
+    std::vector<HapticGenerator::HapticScale> hapticScales;
+    for (int i = 0; i < static_cast<int>(kScaleFactorValues.size()); i++) {
+        hapticScales.push_back(
+                {.id = i, .scale = kScaleValues[0], .scaleFactor = kScaleFactorValues[i]});
+    }
+    ASSERT_NO_FATAL_FAILURE(
+            setAndVerifyParameter(createScaleParam(hapticScales), HapticGenerator::hapticScales));
+}
+
+TEST_P(HapticGeneratorScaleParamTest, SetAndGetAdaptiveScaleFactors) {
+    if (mEffectInterfaceVersion < HAPTIC_SCALE_FACTORS_EFFECT_MIN_VERSION) {
+        GTEST_SKIP() << "Skipping HapticGenerator AdaptiveScaleFactors test for effect version "
+                     << std::to_string(mEffectInterfaceVersion);
+    }
+
+    std::vector<HapticGenerator::HapticScale> hapticScales;
+    for (int i = 0; i < static_cast<int>(kAdaptiveScaleFactorValues.size()); i++) {
+        hapticScales.push_back({.id = i,
+                                .scale = kScaleValues[0],
+                                .scaleFactor = kScaleFactorValues[3],
+                                .adaptiveScaleFactor = kAdaptiveScaleFactorValues[i]});
+    }
+    ASSERT_NO_FATAL_FAILURE(
+            setAndVerifyParameter(createScaleParam(hapticScales), HapticGenerator::hapticScales));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        HapticGeneratorValidTest, HapticGeneratorScaleParamTest,
+        testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+                IFactory::descriptor, getEffectTypeUuidHapticGenerator())),
+        [](const testing::TestParamInfo<HapticGeneratorScaleParamTest::ParamType>& info) {
+            auto descriptor = info.param;
+            return getPrefix(descriptor.second);
+        });
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HapticGeneratorScaleParamTest);
+
+enum VibratorParamName {
+    VIBRATOR_PARAM_INSTANCE,
+    VIBRATOR_PARAM_RESONANT_FREQUENCY,
+    VIBRATOR_PARAM_Q_FACTOR,
+    VIBRATOR_PARAM_MAX_AMPLITUDE,
+};
+
+using HapticGeneratorVibratorInfoTestParam = std::tuple<EffectInstance, float, float, float>;
+
+class HapticGeneratorVibratorInfoParamTest
+    : public ::testing::TestWithParam<HapticGeneratorVibratorInfoTestParam>,
+      public HapticGeneratorHelper {
+  public:
+    HapticGeneratorVibratorInfoParamTest()
+        : mParamResonantFrequency(std::get<VIBRATOR_PARAM_RESONANT_FREQUENCY>(GetParam())),
+          mParamQFactor(std::get<VIBRATOR_PARAM_Q_FACTOR>(GetParam())),
+          mParamMaxAmplitude(std::get<VIBRATOR_PARAM_MAX_AMPLITUDE>(GetParam())) {
+        std::tie(mFactory, mDescriptor) = std::get<VIBRATOR_PARAM_INSTANCE>(GetParam());
+    }
+    void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpHapticGenerator()); }
+    void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownHapticGenerator()); }
+
+    float mParamResonantFrequency = kDefaultResonantFrequency;
+    float mParamQFactor = kDefaultQfactor;
+    float mParamMaxAmplitude = kDefaultMaxAmp;
+};
+
+TEST_P(HapticGeneratorVibratorInfoParamTest, SetAndGetVibratorInformation) {
+    auto vibratorInfo =
+            createVibratorInfo(mParamResonantFrequency, mParamQFactor, mParamMaxAmplitude);
+    if (isParameterValid<HapticGenerator, Range::hapticGenerator>(vibratorInfo, mDescriptor)) {
+        ASSERT_NO_FATAL_FAILURE(setAndVerifyParameter(createVibratorParam(vibratorInfo),
+                                                      HapticGenerator::vibratorInfo));
+    } else {
+        ASSERT_NO_FATAL_FAILURE(setAndVerifyParameter(createVibratorParam(vibratorInfo),
+                                                      HapticGenerator::vibratorInfo,
+                                                      EX_ILLEGAL_ARGUMENT));
+    }
+}
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HapticGeneratorVibratorInfoParamTest);
+
+INSTANTIATE_TEST_SUITE_P(
+        HapticGeneratorValidTest, HapticGeneratorVibratorInfoParamTest,
+        ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+                                   IFactory::descriptor, getEffectTypeUuidHapticGenerator())),
+                           testing::ValuesIn(kResonantFrequencyValues),
+                           testing::ValuesIn(kQFactorValues), testing::ValuesIn(kMaxAmplitude)),
+        [](const testing::TestParamInfo<HapticGeneratorVibratorInfoParamTest::ParamType>& info) {
+            auto descriptor = std::get<VIBRATOR_PARAM_INSTANCE>(info.param).second;
+            std::string resonantFrequency =
+                    std::to_string(std::get<VIBRATOR_PARAM_RESONANT_FREQUENCY>(info.param));
+            std::string qFactor = std::to_string(std::get<VIBRATOR_PARAM_Q_FACTOR>(info.param));
+            std::string maxAmplitude =
+                    std::to_string(std::get<VIBRATOR_PARAM_MAX_AMPLITUDE>(info.param));
+            std::string name = getPrefix(descriptor) + "_resonantFrequency" + resonantFrequency +
+                               "_qFactor" + qFactor + "_maxAmplitude" + maxAmplitude;
+            std::replace_if(
+                    name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
+            return name;
+        });
+
+/**
+ * The data tests do the following
+ * -Generate test input.
+ * -Check if the parameters are supported. Skip the unsupported parameter values.
+ * -Validate increase in haptic output energy energy.
+ **/
+
+enum DataTestParam { EFFECT_INSTANCE, LAYOUT };
+using HapticGeneratorDataTestParam = std::tuple<EffectInstance, int32_t>;
+
+class HapticGeneratorDataTest : public ::testing::TestWithParam<HapticGeneratorDataTestParam>,
+                                public HapticGeneratorHelper {
+  public:
+    HapticGeneratorDataTest() : mChMask(std::get<LAYOUT>(GetParam())) {
+        std::tie(mFactory, mDescriptor) = std::get<EFFECT_INSTANCE>(GetParam());
+        mAudioChannelCount =
+                getChannelCount(AudioChannelLayout::make<AudioChannelLayout::layoutMask>(mChMask),
+                                ~AudioChannelLayout::LAYOUT_HAPTIC_AB);
+        mHapticChannelCount =
+                getChannelCount(AudioChannelLayout::make<AudioChannelLayout::layoutMask>(mChMask),
+                                AudioChannelLayout::LAYOUT_HAPTIC_AB);
+
+        mAudioSamples = kFrameCount * mAudioChannelCount;
+        mHapticSamples = kFrameCount * mHapticChannelCount;
+        mInput.resize(mHapticSamples + mAudioSamples, 0);
+        mOutput.resize(mHapticSamples + mAudioSamples, 0);
+    }
+
+    void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpHapticGenerator(mChMask)); }
+    void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownHapticGenerator()); }
+
+    void generateSinePeriod() {
+        size_t cycleSize = kSamplingFrequency / kInputFrequency;
+        size_t startSize = 0;
+        while (startSize < mAudioSamples) {
+            for (size_t i = 0; i < cycleSize; i++) {
+                mInput[i + startSize] = sin(2 * M_PI * kInputFrequency * i / kSamplingFrequency);
+            }
+            startSize += mAudioSamples / 4;
+        }
+    }
+
+    void setBaseVibratorParam() {
+        auto vibratorInfo =
+                createVibratorInfo(kDefaultResonantFrequency, kDefaultQfactor, kDefaultMaxAmp);
+        if (isParameterValid<HapticGenerator, Range::hapticGenerator>(vibratorInfo, mDescriptor)) {
+            ASSERT_NO_FATAL_FAILURE(setAndVerifyParameter(createVibratorParam(vibratorInfo),
+                                                          HapticGenerator::vibratorInfo));
+        } else {
+            GTEST_SKIP() << "Invalid base vibrator values, skipping the test\n";
+        }
+    }
+
+    void setBaseScaleParam() {
+        ASSERT_NO_FATAL_FAILURE(setAndVerifyParameter(
+                createScaleParam({HapticGenerator::HapticScale(kDefaultScaleID, kDefaultScale)}),
+                HapticGenerator::hapticScales));
+    }
+
+    void validateIncreasingEnergy(HapticGenerator::Tag tag) {
+        float baseEnergy = -1;
+        for (auto param : mHapticParam) {
+            ASSERT_NO_FATAL_FAILURE(setAndVerifyParameter(param, tag));
+            SCOPED_TRACE("Param: " + param.toString());
+            ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput(mInput, mOutput, mEffect, &ret));
+            float hapticOutputEnergy = audio_utils_compute_energy_mono(
+                    mOutput.data() + mAudioSamples, AUDIO_FORMAT_PCM_FLOAT, mHapticSamples);
+            EXPECT_GT(hapticOutputEnergy, baseEnergy);
+            baseEnergy = hapticOutputEnergy;
+        }
+    }
+
+    float findAbsMax(auto begin, auto end) {
+        return *std::max_element(begin, end,
+                                 [](float a, float b) { return std::abs(a) < std::abs(b); });
+    }
+
+    void findMaxAmplitude() {
+        for (float amp = 0.1; amp <= 1; amp += 0.1) {
+            auto vibratorInfo = createVibratorInfo(kDefaultResonantFrequency, kDefaultQfactor, amp);
+            if (!isParameterValid<HapticGenerator, Range::hapticGenerator>(vibratorInfo,
+                                                                           mDescriptor)) {
+                continue;
+            }
+            ASSERT_NO_FATAL_FAILURE(setAndVerifyParameter(createVibratorParam(vibratorInfo),
+                                                          HapticGenerator::vibratorInfo));
+            ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput(mInput, mOutput, mEffect, &ret));
+            float outAmplitude = findAbsMax(mOutput.begin() + mAudioSamples, mOutput.end());
+            if (outAmplitude > mMaxAmplitude) {
+                mMaxAmplitude = outAmplitude;
+            } else {
+                break;
             }
         }
     }
 
-    void addHapticScaleParam(int id, HapticGenerator::VibratorScale scale) {
-        HapticGenerator setHg;
-        std::vector<HapticGenerator::HapticScale> hapticScales = {{.id = id, .scale = scale}};
-        setHg.set<HapticGenerator::hapticScales>(hapticScales);
-        mTags.push_back({HapticGenerator::hapticScales, setHg});
-    }
-
-    void addVibratorInformationParam(float resonantFrequencyHz, float qFactor, float maxAmplitude) {
-        HapticGenerator hg;
-        HapticGenerator::VibratorInformation vibrationInfo = {
-                .resonantFrequencyHz = resonantFrequencyHz,
-                .qFactor = qFactor,
-                .maxAmplitude = maxAmplitude};
-        hg.set<HapticGenerator::vibratorInfo>(vibrationInfo);
-        mTags.push_back({HapticGenerator::vibratorInfo, hg});
-    }
-
-  private:
-    enum ParamTestEnum { PARAM_TEST_TAG, PARAM_TEST_TARGET };
-    std::vector<std::tuple<HapticGenerator::Tag, HapticGenerator>> mTags;
-
-    void CleanUp() { mTags.clear(); }
+    const int kInputFrequency = 1000;
+    float mMaxAmplitude = 0;
+    size_t mHapticSamples;
+    int32_t mChMask;
+    int32_t mAudioChannelCount;
+    int32_t mHapticChannelCount;
+    size_t mAudioSamples;
+    float mBaseHapticOutputEnergy;
+    std::vector<Parameter> mHapticParam;
+    // both input and output buffer includes audio and haptic samples
+    std::vector<float> mInput;
+    std::vector<float> mOutput;
 };
 
-TEST_P(HapticGeneratorParamTest, SetAndGetHapticScale) {
-    EXPECT_NO_FATAL_FAILURE(addHapticScaleParam(mParamHapticScaleId, mParamVibratorScale));
-    SetAndGetHapticGeneratorParameters();
+TEST_P(HapticGeneratorDataTest, IncreasingVibratorScaleTest) {
+    generateInput(mInput, kInputFrequency, kSamplingFrequency, mAudioSamples);
+    ASSERT_NO_FATAL_FAILURE(setBaseVibratorParam());
+    for (HapticGenerator::VibratorScale scale : kScaleValues) {
+        mHapticParam.push_back(
+                createScaleParam({HapticGenerator::HapticScale(kDefaultScaleID, scale)}));
+    }
+    ASSERT_NO_FATAL_FAILURE(validateIncreasingEnergy(HapticGenerator::hapticScales));
 }
 
-TEST_P(HapticGeneratorParamTest, SetAndGetMultipleHapticScales) {
-    EXPECT_NO_FATAL_FAILURE(addHapticScaleParam(mParamHapticScaleId, mParamVibratorScale));
-    EXPECT_NO_FATAL_FAILURE(addHapticScaleParam(mParamHapticScaleId, mParamVibratorScale));
-    SetAndGetHapticGeneratorParameters();
+TEST_P(HapticGeneratorDataTest, IncreasingMaxAmplitudeTest) {
+    generateInput(mInput, kInputFrequency, kSamplingFrequency, mAudioSamples);
+    ASSERT_NO_FATAL_FAILURE(setBaseScaleParam());
+    findMaxAmplitude();
+    std::vector<float> increasingAmplitudeValues = {0.25f * mMaxAmplitude, 0.5f * mMaxAmplitude,
+                                                    0.75f * mMaxAmplitude, mMaxAmplitude};
+    for (float amplitude : increasingAmplitudeValues) {
+        auto vibratorInfo =
+                createVibratorInfo(kDefaultResonantFrequency, kDefaultQfactor, amplitude);
+        if (!isParameterValid<HapticGenerator, Range::hapticGenerator>(vibratorInfo, mDescriptor)) {
+            continue;
+        }
+        mHapticParam.push_back(createVibratorParam(vibratorInfo));
+    }
+    ASSERT_NO_FATAL_FAILURE(validateIncreasingEnergy(HapticGenerator::vibratorInfo));
 }
 
-TEST_P(HapticGeneratorParamTest, SetAndGetVibratorInformation) {
-    EXPECT_NO_FATAL_FAILURE(addVibratorInformationParam(mParamResonantFrequency, mParamQFactor,
-                                                        mParamMaxAmplitude));
-    SetAndGetHapticGeneratorParameters();
+TEST_P(HapticGeneratorDataTest, DescreasingResonantFrequencyTest) {
+    std::vector<float> descreasingResonantFrequency = {800, 600, 400, 200};
+    generateInput(mInput, kInputFrequency, kSamplingFrequency, mAudioSamples);
+    ASSERT_NO_FATAL_FAILURE(setBaseScaleParam());
+    for (float resonantFrequency : descreasingResonantFrequency) {
+        auto vibratorInfo = createVibratorInfo(resonantFrequency, kDefaultQfactor, kDefaultMaxAmp);
+        if (!isParameterValid<HapticGenerator, Range::hapticGenerator>(vibratorInfo, mDescriptor)) {
+            continue;
+        }
+        mHapticParam.push_back(createVibratorParam(vibratorInfo));
+    }
+    ASSERT_NO_FATAL_FAILURE(validateIncreasingEnergy(HapticGenerator::vibratorInfo));
+}
+
+TEST_P(HapticGeneratorDataTest, IncreasingQfactorTest) {
+    std::vector<float> increasingQfactor = {16, 24, 32, 40};
+    generateSinePeriod();
+    ASSERT_NO_FATAL_FAILURE(setBaseScaleParam());
+    for (float qFactor : increasingQfactor) {
+        auto vibratorInfo = createVibratorInfo(kDefaultResonantFrequency, qFactor, kDefaultMaxAmp);
+        if (!isParameterValid<HapticGenerator, Range::hapticGenerator>(vibratorInfo, mDescriptor)) {
+            continue;
+        }
+        mHapticParam.push_back(createVibratorParam(vibratorInfo));
+    }
+    ASSERT_NO_FATAL_FAILURE(validateIncreasingEnergy(HapticGenerator::vibratorInfo));
 }
 
 INSTANTIATE_TEST_SUITE_P(
-        HapticGeneratorValidTest, HapticGeneratorParamTest,
+        DataTest, HapticGeneratorDataTest,
         ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
                                    IFactory::descriptor, getEffectTypeUuidHapticGenerator())),
-                           testing::ValuesIn(kHapticScaleIdValues),
-                           testing::ValuesIn(kVibratorScaleValues),
-                           testing::ValuesIn(kResonantFrequencyValues),
-                           testing::ValuesIn(kQFactorValues), testing::ValuesIn(kMaxAmplitude)),
-        [](const testing::TestParamInfo<HapticGeneratorParamTest::ParamType>& info) {
-            auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
-            std::string hapticScaleID = std::to_string(std::get<PARAM_HAPTIC_SCALE_ID>(info.param));
-            std::string hapticScaleVibScale = std::to_string(
-                    static_cast<int>(std::get<PARAM_HAPTIC_SCALE_VIBRATOR_SCALE>(info.param)));
-            std::string resonantFrequency = std::to_string(
-                    std::get<PARAM_VIBRATION_INFORMATION_RESONANT_FREQUENCY>(info.param));
-            std::string qFactor =
-                    std::to_string(std::get<PARAM_VIBRATION_INFORMATION_Q_FACTOR>(info.param));
-            std::string maxAmplitude =
-                    std::to_string(std::get<PARAM_VIBRATION_INFORMATION_MAX_AMPLITUDE>(info.param));
-            std::string name = getPrefix(descriptor) + "_hapticScaleId" + hapticScaleID +
-                               "_hapticScaleVibScale" + hapticScaleVibScale + "_resonantFrequency" +
-                               resonantFrequency + "_qFactor" + qFactor + "_maxAmplitude" +
-                               maxAmplitude;
-            std::replace_if(
-                    name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
+                           testing::ValuesIn(kHapticOutputLayouts)),
+        [](const testing::TestParamInfo<HapticGeneratorDataTest::ParamType>& info) {
+            auto descriptor = std::get<EFFECT_INSTANCE>(info.param).second;
+            std::string layout = "0x" + std::format("{:x}", std::get<LAYOUT>(info.param));
+            std::string name = getPrefix(descriptor) + "_layout_" + layout;
             return name;
         });
-
-INSTANTIATE_TEST_SUITE_P(
-        HapticGeneratorInvalidTest, HapticGeneratorParamTest,
-        ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
-                                   IFactory::descriptor, getEffectTypeUuidHapticGenerator())),
-                           testing::Values(MIN_ID),
-                           testing::Values(HapticGenerator::VibratorScale::NONE),
-                           testing::Values(MIN_FLOAT), testing::Values(MIN_FLOAT),
-                           testing::Values(MIN_FLOAT)),
-        [](const testing::TestParamInfo<HapticGeneratorParamTest::ParamType>& info) {
-            auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
-            std::string hapticScaleID = std::to_string(std::get<PARAM_HAPTIC_SCALE_ID>(info.param));
-            std::string hapticScaleVibScale = std::to_string(
-                    static_cast<int>(std::get<PARAM_HAPTIC_SCALE_VIBRATOR_SCALE>(info.param)));
-            std::string resonantFrequency = std::to_string(
-                    std::get<PARAM_VIBRATION_INFORMATION_RESONANT_FREQUENCY>(info.param));
-            std::string qFactor =
-                    std::to_string(std::get<PARAM_VIBRATION_INFORMATION_Q_FACTOR>(info.param));
-            std::string maxAmplitude =
-                    std::to_string(std::get<PARAM_VIBRATION_INFORMATION_MAX_AMPLITUDE>(info.param));
-            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
-                               descriptor.common.name + "_UUID_" +
-                               toString(descriptor.common.id.uuid) + "_hapticScaleId" +
-                               hapticScaleID + "_hapticScaleVibScale" + hapticScaleVibScale +
-                               "_resonantFrequency" + resonantFrequency + "_qFactor" + qFactor +
-                               "_maxAmplitude" + maxAmplitude;
-            std::replace_if(
-                    name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
-            return name;
-        });
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HapticGeneratorParamTest);
-
-// Test HapticScale[] hapticScales parameter
-using HapticGeneratorScalesTestParam = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>>;
-class HapticGeneratorScalesTest : public ::testing::TestWithParam<HapticGeneratorScalesTestParam>,
-                                  public EffectHelper {
-  public:
-    HapticGeneratorScalesTest() {
-        std::tie(mFactory, mDescriptor) = std::get<PARAM_INSTANCE_NAME>(GetParam());
-    }
-
-    void SetUp() override {
-        ASSERT_NE(nullptr, mFactory);
-        ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
-
-        Parameter::Common common = createParamCommon(
-                0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */,
-                kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */);
-        IEffect::OpenEffectReturn ret;
-        ASSERT_NO_FATAL_FAILURE(open(mEffect, common, std::nullopt, &ret, EX_NONE));
-        ASSERT_NE(nullptr, mEffect);
-    }
-
-    void TearDown() override {
-        ASSERT_NO_FATAL_FAILURE(close(mEffect));
-        ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
-        CleanUp();
-    }
-
-    static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100;
-    std::shared_ptr<IFactory> mFactory;
-    std::shared_ptr<IEffect> mEffect;
-    Descriptor mDescriptor;
-
-    void addHapticScaleParam(std::vector<HapticGenerator::HapticScale> scales) {
-        mHapticScales.push_back(HapticGenerator::make<HapticGenerator::hapticScales>(scales));
-        for (const auto& scale : scales) {
-            expectMap.insert_or_assign(scale.id, scale.scale);
-        }
-    }
-
-    void SetHapticScaleParameters() {
-        // std::unordered_set<HapticGenerator::HapticScale> target;
-        for (auto& it : mHapticScales) {
-            Parameter::Specific specific =
-                    Parameter::Specific::make<Parameter::Specific::hapticGenerator>(it);
-            Parameter param = Parameter::make<Parameter::specific>(specific);
-            EXPECT_STATUS(EX_NONE, mEffect->setParameter(param)) << param.toString();
-        }
-    }
-
-    void checkHapticScaleParameter() {
-        // get parameter
-        Parameter targetParam;
-        HapticGenerator::Id hgId = HapticGenerator::Id::make<HapticGenerator::Id::commonTag>(
-                HapticGenerator::hapticScales);
-        Parameter::Id id = Parameter::Id::make<Parameter::Id::hapticGeneratorTag>(hgId);
-        EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &targetParam));
-        ASSERT_EQ(Parameter::specific, targetParam.getTag());
-        Parameter::Specific specific = targetParam.get<Parameter::specific>();
-        ASSERT_EQ(Parameter::Specific::hapticGenerator, specific.getTag());
-        HapticGenerator hg = specific.get<Parameter::Specific::hapticGenerator>();
-        ASSERT_EQ(HapticGenerator::hapticScales, hg.getTag());
-        std::vector<HapticGenerator::HapticScale> scales = hg.get<HapticGenerator::hapticScales>();
-        ASSERT_EQ(scales.size(), expectMap.size());
-        for (const auto& scale : scales) {
-            auto itor = expectMap.find(scale.id);
-            ASSERT_NE(expectMap.end(), itor);
-            ASSERT_EQ(scale.scale, itor->second);
-            expectMap.erase(scale.id);
-        }
-        ASSERT_EQ(0ul, expectMap.size());
-    }
-
-    const static HapticGenerator::HapticScale kHapticScaleWithMinId;
-    const static HapticGenerator::HapticScale kHapticScaleWithMinIdNew;
-    const static HapticGenerator::HapticScale kHapticScale;
-    const static HapticGenerator::HapticScale kHapticScaleNew;
-    const static HapticGenerator::HapticScale kHapticScaleWithMaxId;
-    const static HapticGenerator::HapticScale kHapticScaleWithMaxIdNew;
-
-    std::vector<HapticGenerator> mHapticScales;
-
-    void CleanUp() {
-        mHapticScales.clear();
-        expectMap.clear();
-    }
-
-  private:
-    std::map<int /* trackID */, HapticGenerator::VibratorScale> expectMap;
-};
-
-const HapticGenerator::HapticScale HapticGeneratorScalesTest::kHapticScaleWithMinId = {
-        .id = MIN_ID, .scale = HapticGenerator::VibratorScale::MUTE};
-const HapticGenerator::HapticScale HapticGeneratorScalesTest::kHapticScaleWithMinIdNew = {
-        .id = MIN_ID, .scale = HapticGenerator::VibratorScale::VERY_LOW};
-const HapticGenerator::HapticScale HapticGeneratorScalesTest::kHapticScale = {
-        .id = 1, .scale = HapticGenerator::VibratorScale::LOW};
-const HapticGenerator::HapticScale HapticGeneratorScalesTest::kHapticScaleNew = {
-        .id = 1, .scale = HapticGenerator::VibratorScale::NONE};
-const HapticGenerator::HapticScale HapticGeneratorScalesTest::kHapticScaleWithMaxId = {
-        .id = MAX_ID, .scale = HapticGenerator::VibratorScale::VERY_HIGH};
-const HapticGenerator::HapticScale HapticGeneratorScalesTest::kHapticScaleWithMaxIdNew = {
-        .id = MAX_ID, .scale = HapticGenerator::VibratorScale::MUTE};
-
-TEST_P(HapticGeneratorScalesTest, SetAndUpdateOne) {
-    EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScale}));
-    EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
-    EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleNew}));
-    EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
-
-    EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleWithMinId}));
-    EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
-    EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleWithMinIdNew}));
-    EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
-
-    EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleWithMaxId}));
-    EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
-    EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleWithMaxIdNew}));
-    EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
-
-    EXPECT_NO_FATAL_FAILURE(checkHapticScaleParameter());
-}
-
-TEST_P(HapticGeneratorScalesTest, SetAndUpdateVector) {
-    EXPECT_NO_FATAL_FAILURE(
-            addHapticScaleParam({kHapticScale, kHapticScaleWithMaxId, kHapticScaleWithMinId}));
-    EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
-    EXPECT_NO_FATAL_FAILURE(addHapticScaleParam(
-            {kHapticScaleNew, kHapticScaleWithMaxIdNew, kHapticScaleWithMinIdNew}));
-    EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
-
-    EXPECT_NO_FATAL_FAILURE(checkHapticScaleParameter());
-}
-
-TEST_P(HapticGeneratorScalesTest, SetAndUpdateMultipleVector) {
-    EXPECT_NO_FATAL_FAILURE(
-            addHapticScaleParam({kHapticScale, kHapticScaleWithMaxId, kHapticScaleWithMinId}));
-    EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
-    EXPECT_NO_FATAL_FAILURE(addHapticScaleParam(
-            {kHapticScaleNew, kHapticScaleWithMaxIdNew, kHapticScaleWithMinIdNew}));
-    EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
-    EXPECT_NO_FATAL_FAILURE(
-            addHapticScaleParam({kHapticScale, kHapticScaleWithMaxId, kHapticScaleWithMinId}));
-    EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
-
-    EXPECT_NO_FATAL_FAILURE(checkHapticScaleParameter());
-}
-
-TEST_P(HapticGeneratorScalesTest, SetOneAndAddMoreVector) {
-    EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScale}));
-    EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
-    EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleWithMaxId, kHapticScaleWithMinId}));
-    EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
-
-    EXPECT_NO_FATAL_FAILURE(checkHapticScaleParameter());
-}
-
-TEST_P(HapticGeneratorScalesTest, SetMultipleAndAddOneVector) {
-    EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScaleWithMaxId, kHapticScaleWithMinId}));
-    EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
-    EXPECT_NO_FATAL_FAILURE(addHapticScaleParam({kHapticScale}));
-    EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
-
-    EXPECT_NO_FATAL_FAILURE(checkHapticScaleParameter());
-}
-
-TEST_P(HapticGeneratorScalesTest, SetMultipleVectorRepeat) {
-    EXPECT_NO_FATAL_FAILURE(
-            addHapticScaleParam({kHapticScaleWithMaxId, kHapticScale, kHapticScaleWithMinId}));
-    EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
-    EXPECT_NO_FATAL_FAILURE(
-            addHapticScaleParam({kHapticScaleWithMaxId, kHapticScale, kHapticScaleWithMinId}));
-    EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
-    EXPECT_NO_FATAL_FAILURE(
-            addHapticScaleParam({kHapticScaleWithMaxId, kHapticScale, kHapticScaleWithMinId}));
-    EXPECT_NO_FATAL_FAILURE(SetHapticScaleParameters());
-
-    EXPECT_NO_FATAL_FAILURE(checkHapticScaleParameter());
-}
-
-INSTANTIATE_TEST_SUITE_P(
-        HapticGeneratorScalesTest, HapticGeneratorScalesTest,
-        ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
-                IFactory::descriptor, getEffectTypeUuidHapticGenerator()))),
-        [](const testing::TestParamInfo<HapticGeneratorScalesTest::ParamType>& info) {
-            auto descriptor = std::get<PARAM_INSTANCE_NAME>(info.param).second;
-            std::string name = "Implementor_" + descriptor.common.implementor + "_name_" +
-                               descriptor.common.name + "_UUID_" +
-                               toString(descriptor.common.id.uuid);
-            std::replace_if(
-                    name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
-            return name;
-        });
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HapticGeneratorScalesTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HapticGeneratorDataTest);
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
diff --git a/automotive/TEST_MAPPING b/automotive/TEST_MAPPING
index c64c880..483a85f 100644
--- a/automotive/TEST_MAPPING
+++ b/automotive/TEST_MAPPING
@@ -19,10 +19,58 @@
     	"name": "CtsCarBuiltinApiHostTestCases"
     },
     {
-      "name": "CarServiceTest"
+      "name": "CarServiceAudioTest"
+    },
+    {
+      "name": "CarServiceCarTest"
+    },
+    {
+      "name": "CarServiceClusterTest"
+    },
+    {
+      "name": "CarServiceDiagnosticTest"
+    },
+    {
+      "name": "CarServiceDrivingStateTest"
+    },
+    {
+      "name": "CarServiceEvsTest"
+    },
+    {
+      "name": "CarServiceGarageModeTest"
+    },
+    {
+      "name": "CarServiceInputTest"
+    },
+    {
+      "name": "CarServiceOsTest"
+    },
+    {
+      "name": "CarServicePmTest"
+    },
+    {
+      "name": "CarServicePowerTest"
+    },
+    {
+      "name": "CarServicePropertyTest"
+    },
+    {
+      "name": "CarServiceRemoteAccessTest"
+    },
+    {
+      "name": "CarServiceStorageMonitoringTest"
+    },
+    {
+      "name": "CarServiceTelemetryTest"
     },
     {
       "name": "CarServiceUnitTest"
+    },
+    {
+      "name": "CarServiceVmsTest"
+    },
+    {
+      "name": "CarServiceWatchdogTest"
     }
   ]
 }
\ No newline at end of file
diff --git a/automotive/evs/aidl/Android.bp b/automotive/evs/aidl/Android.bp
index 9f5ab97..75eb924 100644
--- a/automotive/evs/aidl/Android.bp
+++ b/automotive/evs/aidl/Android.bp
@@ -46,6 +46,9 @@
         ndk: {
             min_sdk_version: "29",
         },
+        rust: {
+            enabled: true,
+        },
     },
     versions_with_info: [
         {
diff --git a/automotive/evs/aidl/impl/default/manifest_evs-default-service.xml b/automotive/evs/aidl/impl/default/manifest_evs-default-service.xml
index 50692f7..9ddc6ad 100644
--- a/automotive/evs/aidl/impl/default/manifest_evs-default-service.xml
+++ b/automotive/evs/aidl/impl/default/manifest_evs-default-service.xml
@@ -2,6 +2,6 @@
     <hal format="aidl">
         <name>android.hardware.automotive.evs</name>
         <fqname>IEvsEnumerator/hw/0</fqname>
-        <version>1</version>
+        <version>2</version>
     </hal>
 </manifest>
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/AcquiredInfoAndVendorCode.aidl b/automotive/evs/aidl/rust_impl/Android.bp
similarity index 60%
copy from biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/AcquiredInfoAndVendorCode.aidl
copy to automotive/evs/aidl/rust_impl/Android.bp
index c7be950..ac8b90f 100644
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/AcquiredInfoAndVendorCode.aidl
+++ b/automotive/evs/aidl/rust_impl/Android.bp
@@ -14,22 +14,17 @@
  * limitations under the License.
  */
 
-package android.hardware.biometrics.fingerprint;
-
-import android.hardware.biometrics.fingerprint.AcquiredInfo;
-
-/**
- * @hide
- */
-@VintfStability
-union AcquiredInfoAndVendorCode {
-    /**
-     * Acquired info as specified in AcqauiredInfo.aidl
-     */
-    AcquiredInfo acquiredInfo = AcquiredInfo.UNKNOWN;
-
-    /**
-     * Vendor specific code
-     */
-    int vendorCode;
+rust_binary {
+    name: "android.hardware.automotive.evs-aidl-rust-service",
+    relative_install_path: "hw",
+    vendor: true,
+    srcs: ["src/*.rs"],
+    crate_root: "src/main.rs",
+    vintf_fragments: ["manifest_evs-rust-service.xml"],
+    init_rc: ["evs-rust-service.rc"],
+    rustlibs: [
+        "android.hardware.automotive.evs-V2-rust",
+        "libbinder_rs",
+        "liblog_rust",
+    ],
 }
diff --git a/automotive/evs/aidl/rust_impl/README.md b/automotive/evs/aidl/rust_impl/README.md
new file mode 100644
index 0000000..bf00aed
--- /dev/null
+++ b/automotive/evs/aidl/rust_impl/README.md
@@ -0,0 +1,21 @@
+# Rust Skeleton EVS HAL implementation.
+
+WARNING: This is not a reference EVS HAL implementation and therefore does not
+provide any actual functionality.
+
+This folder contains a skeleton EVS HAL implementation in Rust to demonstrate
+how vendors could implement their EVS HAL in Rust. To compile and run this
+implementation, please include below package to the device build script:
+
+* `android.hardware.automotive.evs-aidl-rust-service`
+
+Please note that this service will attempt to register the service as
+`IEvsEnumerator/rust/0` and therefore is also required to be declared in the
+service context by adding below line to a proper `service_contexts` file:
+
+> android.hardware.automotive.evs.IEvsEnumerator/rust/0 u:object_r:hal_evs_service:s0
+
+This implementation intentionally returns `binder::StatusCode::UNKNOWN_ERROR`
+for any API call except deprecated API for ultrasonics; the process will be
+panicked on these methods instead. Hence, this implementation does not comply
+with VTS tests and vendors must replace each method with actual implementation.
diff --git a/automotive/evs/aidl/rust_impl/evs-rust-service.rc b/automotive/evs/aidl/rust_impl/evs-rust-service.rc
new file mode 100644
index 0000000..3741b21
--- /dev/null
+++ b/automotive/evs/aidl/rust_impl/evs-rust-service.rc
@@ -0,0 +1,8 @@
+service vendor.evs-hal-rust-default /vendor/bin/hw/android.hardware.automotive.evs-aidl-rust-service
+    class early_hal
+    priority -20
+    user graphics
+    group automotive_evs camera
+    onrestart restart cardisplayproxyd
+    onrestart restart evsmanagerd
+    disabled
diff --git a/automotive/evs/aidl/rust_impl/manifest_evs-rust-service.xml b/automotive/evs/aidl/rust_impl/manifest_evs-rust-service.xml
new file mode 100644
index 0000000..813cbb2
--- /dev/null
+++ b/automotive/evs/aidl/rust_impl/manifest_evs-rust-service.xml
@@ -0,0 +1,7 @@
+<manifest version="2.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.automotive.evs</name>
+        <version>2</version>
+        <fqname>IEvsEnumerator/rust/0</fqname>
+    </hal>
+</manifest>
diff --git a/automotive/evs/aidl/rust_impl/src/default_evs_hal.rs b/automotive/evs/aidl/rust_impl/src/default_evs_hal.rs
new file mode 100644
index 0000000..72b2d53
--- /dev/null
+++ b/automotive/evs/aidl/rust_impl/src/default_evs_hal.rs
@@ -0,0 +1,113 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+use android_hardware_automotive_evs::aidl::android::hardware::automotive::evs::{
+    CameraDesc::CameraDesc, DisplayState::DisplayState, IEvsCamera::IEvsCamera,
+    IEvsDisplay::IEvsDisplay, IEvsEnumerator::IEvsEnumerator,
+    IEvsEnumeratorStatusCallback::IEvsEnumeratorStatusCallback,
+    IEvsUltrasonicsArray::IEvsUltrasonicsArray, Stream::Stream,
+    UltrasonicsArrayDesc::UltrasonicsArrayDesc,
+};
+
+pub struct DefaultEvsHal {}
+
+impl binder::Interface for DefaultEvsHal {}
+
+impl IEvsEnumerator for DefaultEvsHal {
+    fn closeCamera(
+        &self,
+        _: &binder::Strong<(dyn IEvsCamera + 'static)>,
+    ) -> std::result::Result<(), binder::Status> {
+        Err(binder::StatusCode::UNKNOWN_ERROR.into())
+    }
+
+    fn closeDisplay(
+        &self,
+        _: &binder::Strong<(dyn IEvsDisplay + 'static)>,
+    ) -> std::result::Result<(), binder::Status> {
+        Err(binder::StatusCode::UNKNOWN_ERROR.into())
+    }
+
+    fn closeUltrasonicsArray(
+        &self,
+        _: &binder::Strong<(dyn IEvsUltrasonicsArray + 'static)>,
+    ) -> std::result::Result<(), binder::Status> {
+        unimplemented!()
+    }
+
+    fn getCameraList(&self) -> std::result::Result<std::vec::Vec<CameraDesc>, binder::Status> {
+        Err(binder::StatusCode::UNKNOWN_ERROR.into())
+    }
+
+    fn getDisplayIdList(&self) -> std::result::Result<std::vec::Vec<u8>, binder::Status> {
+        Err(binder::StatusCode::UNKNOWN_ERROR.into())
+    }
+
+    fn getDisplayState(&self) -> std::result::Result<DisplayState, binder::Status> {
+        Err(binder::StatusCode::UNKNOWN_ERROR.into())
+    }
+
+    fn getStreamList(
+        &self,
+        _: &CameraDesc,
+    ) -> std::result::Result<std::vec::Vec<Stream>, binder::Status> {
+        Err(binder::StatusCode::UNKNOWN_ERROR.into())
+    }
+
+    fn getUltrasonicsArrayList(
+        &self,
+    ) -> std::result::Result<std::vec::Vec<UltrasonicsArrayDesc>, binder::Status> {
+        unimplemented!()
+    }
+
+    fn isHardware(&self) -> std::result::Result<bool, binder::Status> {
+        Err(binder::StatusCode::UNKNOWN_ERROR.into())
+    }
+
+    fn openCamera(
+        &self,
+        _: &str,
+        _: &Stream,
+    ) -> std::result::Result<binder::Strong<(dyn IEvsCamera + 'static)>, binder::Status> {
+        Err(binder::StatusCode::UNKNOWN_ERROR.into())
+    }
+
+    fn openDisplay(
+        &self,
+        _: i32,
+    ) -> std::result::Result<binder::Strong<(dyn IEvsDisplay + 'static)>, binder::Status> {
+        Err(binder::StatusCode::UNKNOWN_ERROR.into())
+    }
+
+    fn openUltrasonicsArray(
+        &self,
+        _: &str,
+    ) -> std::result::Result<binder::Strong<(dyn IEvsUltrasonicsArray + 'static)>, binder::Status>
+    {
+        unimplemented!()
+    }
+
+    fn registerStatusCallback(
+        &self,
+        _: &binder::Strong<(dyn IEvsEnumeratorStatusCallback + 'static)>,
+    ) -> std::result::Result<(), binder::Status> {
+        Err(binder::StatusCode::UNKNOWN_ERROR.into())
+    }
+
+    fn getDisplayStateById(&self, _: i32) -> std::result::Result<DisplayState, binder::Status> {
+        Err(binder::StatusCode::UNKNOWN_ERROR.into())
+    }
+}
diff --git a/automotive/evs/aidl/rust_impl/src/main.rs b/automotive/evs/aidl/rust_impl/src/main.rs
new file mode 100644
index 0000000..df312c0
--- /dev/null
+++ b/automotive/evs/aidl/rust_impl/src/main.rs
@@ -0,0 +1,42 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+mod default_evs_hal;
+
+use crate::default_evs_hal::DefaultEvsHal;
+
+use android_hardware_automotive_evs::aidl::android::hardware::automotive::evs::IEvsEnumerator::BnEvsEnumerator;
+
+use log::info;
+
+fn main() {
+    binder::ProcessState::start_thread_pool();
+
+    let service = DefaultEvsHal {};
+
+    // Register HAL implementation as rust/0 instance.
+    let service_name = "android.hardware.automotive.evs.IEvsEnumerator/rust/0";
+    let service_binder = BnEvsEnumerator::new_binder(service, binder::BinderFeatures::default());
+
+    binder::add_service(service_name, service_binder.as_binder())
+        .expect(format!("Failed to register {}.", service_name).as_str());
+    info!("EVS Hardware Enumerator is ready");
+
+    binder::ProcessState::join_thread_pool();
+
+    // In normal operation, we don't expect the thread pool to exit.
+    info!("EVS Hardware Enumerator is shutting down");
+}
diff --git a/automotive/ivn_android_device/impl/default/src/IvnAndroidDeviceService.cpp b/automotive/ivn_android_device/impl/default/src/IvnAndroidDeviceService.cpp
index 4166fdc..b284205 100644
--- a/automotive/ivn_android_device/impl/default/src/IvnAndroidDeviceService.cpp
+++ b/automotive/ivn_android_device/impl/default/src/IvnAndroidDeviceService.cpp
@@ -24,6 +24,7 @@
 #include <json/json.h>
 
 #include <fstream>
+#include <string>
 
 namespace android {
 namespace hardware {
diff --git a/automotive/vehicle/Android.bp b/automotive/vehicle/Android.bp
index e614937..606e108 100644
--- a/automotive/vehicle/Android.bp
+++ b/automotive/vehicle/Android.bp
@@ -22,7 +22,7 @@
     name: "VehicleHalInterfaceDefaults",
     static_libs: [
         "android.hardware.automotive.vehicle-V3-ndk",
-        "android.hardware.automotive.vehicle.property-V3-ndk",
+        "android.hardware.automotive.vehicle.property-V4-ndk",
     ],
 }
 
@@ -30,6 +30,14 @@
     name: "VehicleHalInterfaceRustDefaults",
     rustlibs: [
         "android.hardware.automotive.vehicle-V3-rust",
-        "android.hardware.automotive.vehicle.property-V3-rust",
+        "android.hardware.automotive.vehicle.property-V4-rust",
+    ],
+}
+
+aidl_interface_defaults {
+    name: "android.hardware.automotive.vehicle-latest-defaults",
+    imports: [
+        "android.hardware.automotive.vehicle-V3",
+        "android.hardware.automotive.vehicle.property-V4",
     ],
 }
diff --git a/automotive/vehicle/aidl/aidl_test/Android.bp b/automotive/vehicle/aidl/aidl_test/Android.bp
index f517df8..1e43070 100644
--- a/automotive/vehicle/aidl/aidl_test/Android.bp
+++ b/automotive/vehicle/aidl/aidl_test/Android.bp
@@ -40,7 +40,7 @@
 cc_test {
     name: "VehiclePropertyAnnotationCppTest",
     srcs: ["VehiclePropertyAnnotationCppTest.cpp"],
-    header_libs: ["IVehicleGeneratedHeaders-V3"],
+    header_libs: ["IVehicleGeneratedHeaders-V4"],
     defaults: ["VehicleHalInterfaceDefaults"],
     test_suites: ["general-tests"],
 }
@@ -49,11 +49,11 @@
     name: "VehiclePropertyAnnotationJavaTest",
     srcs: [
         "VehiclePropertyAnnotationJavaTest.java",
-        ":IVehicleGeneratedJavaFiles-V3",
+        ":IVehicleGeneratedJavaFiles-V4",
     ],
     static_libs: [
         "android.hardware.automotive.vehicle-V3-java",
-        "android.hardware.automotive.vehicle.property-V3-java",
+        "android.hardware.automotive.vehicle.property-V4-java",
         "androidx.test.runner",
         "truth",
     ],
diff --git a/automotive/vehicle/aidl/generated_lib/3/cpp/AccessForVehicleProperty.h b/automotive/vehicle/aidl/generated_lib/3/cpp/AccessForVehicleProperty.h
index 51a3025..6f6c91c 100644
--- a/automotive/vehicle/aidl/generated_lib/3/cpp/AccessForVehicleProperty.h
+++ b/automotive/vehicle/aidl/generated_lib/3/cpp/AccessForVehicleProperty.h
@@ -27,6 +27,10 @@
 #include <aidl/android/hardware/automotive/vehicle/VehicleProperty.h>
 #include <aidl/android/hardware/automotive/vehicle/VehiclePropertyAccess.h>
 
+// Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
+#include <PerDisplayMaxBrightness.h>
+// End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
+
 #include <unordered_map>
 
 namespace aidl {
@@ -302,6 +306,9 @@
         {VehicleProperty::CROSS_TRAFFIC_MONITORING_WARNING_STATE, VehiclePropertyAccess::READ},
         {VehicleProperty::LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_ENABLED, VehiclePropertyAccess::READ_WRITE},
         {VehicleProperty::LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyAccess::READ},
+        // Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
+        {PER_DISPLAY_MAX_BRIGHTNESS, VehiclePropertyAccess::READ},
+        // End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
 };
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/generated_lib/3/cpp/ChangeModeForVehicleProperty.h b/automotive/vehicle/aidl/generated_lib/3/cpp/ChangeModeForVehicleProperty.h
index 60e9a72..88f2f88 100644
--- a/automotive/vehicle/aidl/generated_lib/3/cpp/ChangeModeForVehicleProperty.h
+++ b/automotive/vehicle/aidl/generated_lib/3/cpp/ChangeModeForVehicleProperty.h
@@ -27,6 +27,10 @@
 #include <aidl/android/hardware/automotive/vehicle/VehicleProperty.h>
 #include <aidl/android/hardware/automotive/vehicle/VehiclePropertyChangeMode.h>
 
+// Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
+#include <PerDisplayMaxBrightness.h>
+// End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
+
 #include <unordered_map>
 
 namespace aidl {
@@ -302,6 +306,9 @@
         {VehicleProperty::CROSS_TRAFFIC_MONITORING_WARNING_STATE, VehiclePropertyChangeMode::ON_CHANGE},
         {VehicleProperty::LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
         {VehicleProperty::LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyChangeMode::ON_CHANGE},
+        // Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
+        {PER_DISPLAY_MAX_BRIGHTNESS, VehiclePropertyChangeMode::STATIC},
+        // End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
 };
 
 }  // namespace vehicle
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/AcquiredInfoAndVendorCode.aidl b/automotive/vehicle/aidl/generated_lib/3/cpp/PerDisplayMaxBrightness.h
similarity index 62%
copy from biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/AcquiredInfoAndVendorCode.aidl
copy to automotive/vehicle/aidl/generated_lib/3/cpp/PerDisplayMaxBrightness.h
index c7be950..2b50db3 100644
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/AcquiredInfoAndVendorCode.aidl
+++ b/automotive/vehicle/aidl/generated_lib/3/cpp/PerDisplayMaxBrightness.h
@@ -14,22 +14,13 @@
  * limitations under the License.
  */
 
-package android.hardware.biometrics.fingerprint;
+#pragma once
 
-import android.hardware.biometrics.fingerprint.AcquiredInfo;
+#include <aidl/android/hardware/automotive/vehicle/VehicleProperty.h>
 
-/**
- * @hide
- */
-@VintfStability
-union AcquiredInfoAndVendorCode {
-    /**
-     * Acquired info as specified in AcqauiredInfo.aidl
-     */
-    AcquiredInfo acquiredInfo = AcquiredInfo.UNKNOWN;
+namespace aidl::android::hardware::automotive::vehicle {
 
-    /**
-     * Vendor specific code
-     */
-    int vendorCode;
-}
+// Same as VehicleProperty::PER_DISPLAY_MAX_BRIGHTNESS as defined in v4.
+static constexpr VehicleProperty PER_DISPLAY_MAX_BRIGHTNESS = (VehicleProperty)0x11410F4E;
+
+}  // namespace aidl::android::hardware::automotive::vehicle
diff --git a/automotive/vehicle/aidl/generated_lib/3/cpp/VersionForVehicleProperty.h b/automotive/vehicle/aidl/generated_lib/3/cpp/VersionForVehicleProperty.h
index 0e80bd8..0d24273 100644
--- a/automotive/vehicle/aidl/generated_lib/3/cpp/VersionForVehicleProperty.h
+++ b/automotive/vehicle/aidl/generated_lib/3/cpp/VersionForVehicleProperty.h
@@ -26,6 +26,10 @@
 
 #include <aidl/android/hardware/automotive/vehicle/VehicleProperty.h>
 
+// Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
+#include <PerDisplayMaxBrightness.h>
+// End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
+
 #include <unordered_map>
 
 namespace aidl {
@@ -301,6 +305,9 @@
         {VehicleProperty::CROSS_TRAFFIC_MONITORING_WARNING_STATE, 3},
         {VehicleProperty::LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_ENABLED, 3},
         {VehicleProperty::LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_STATE, 3},
+        // Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
+        {PER_DISPLAY_MAX_BRIGHTNESS, 2},
+        // End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
 };
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/generated_lib/3/java/AccessForVehicleProperty.java b/automotive/vehicle/aidl/generated_lib/3/java/AccessForVehicleProperty.java
index afb6cab..f899df8 100644
--- a/automotive/vehicle/aidl/generated_lib/3/java/AccessForVehicleProperty.java
+++ b/automotive/vehicle/aidl/generated_lib/3/java/AccessForVehicleProperty.java
@@ -28,6 +28,10 @@
 
 public final class AccessForVehicleProperty {
 
+    // Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
+    private static final int PER_DISPLAY_MAX_BRIGHTNESS = 0x11410F4E;
+    // End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
+
     public static final Map<Integer, Integer> values = Map.ofEntries(
         Map.entry(VehicleProperty.INFO_VIN, VehiclePropertyAccess.READ),
         Map.entry(VehicleProperty.INFO_MAKE, VehiclePropertyAccess.READ),
@@ -294,7 +298,10 @@
         Map.entry(VehicleProperty.CROSS_TRAFFIC_MONITORING_ENABLED, VehiclePropertyAccess.READ_WRITE),
         Map.entry(VehicleProperty.CROSS_TRAFFIC_MONITORING_WARNING_STATE, VehiclePropertyAccess.READ),
         Map.entry(VehicleProperty.LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_ENABLED, VehiclePropertyAccess.READ_WRITE),
-        Map.entry(VehicleProperty.LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyAccess.READ)
+        Map.entry(VehicleProperty.LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyAccess.READ),
+        // Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
+        Map.entry(PER_DISPLAY_MAX_BRIGHTNESS, VehiclePropertyAccess.READ)
+        // End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
     );
 
 }
diff --git a/automotive/vehicle/aidl/generated_lib/3/java/ChangeModeForVehicleProperty.java b/automotive/vehicle/aidl/generated_lib/3/java/ChangeModeForVehicleProperty.java
index 12aff40..09989bf 100644
--- a/automotive/vehicle/aidl/generated_lib/3/java/ChangeModeForVehicleProperty.java
+++ b/automotive/vehicle/aidl/generated_lib/3/java/ChangeModeForVehicleProperty.java
@@ -28,6 +28,10 @@
 
 public final class ChangeModeForVehicleProperty {
 
+    // Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
+    private static final int PER_DISPLAY_MAX_BRIGHTNESS = 0x11410F4E;
+    // End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
+
     public static final Map<Integer, Integer> values = Map.ofEntries(
         Map.entry(VehicleProperty.INFO_VIN, VehiclePropertyChangeMode.STATIC),
         Map.entry(VehicleProperty.INFO_MAKE, VehiclePropertyChangeMode.STATIC),
@@ -294,7 +298,10 @@
         Map.entry(VehicleProperty.CROSS_TRAFFIC_MONITORING_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
         Map.entry(VehicleProperty.CROSS_TRAFFIC_MONITORING_WARNING_STATE, VehiclePropertyChangeMode.ON_CHANGE),
         Map.entry(VehicleProperty.LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
-        Map.entry(VehicleProperty.LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyChangeMode.ON_CHANGE)
+        Map.entry(VehicleProperty.LOW_SPEED_AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyChangeMode.ON_CHANGE),
+        // Start manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
+        Map.entry(PER_DISPLAY_MAX_BRIGHTNESS, VehiclePropertyChangeMode.STATIC)
+        // End manual edit: backport PER_DISPLAY_MAX_BRIGHTNESS.
     );
 
 }
diff --git a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/Android.bp b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/Android.bp
index 28c95ce..aef2909 100644
--- a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/Android.bp
+++ b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/Android.bp
@@ -27,7 +27,7 @@
     defaults: ["VehicleHalDefaults"],
     static_libs: ["VehicleHalUtils"],
     header_libs: [
-        "IVehicleGeneratedHeaders-V3",
+        "IVehicleGeneratedHeaders-V4",
     ],
     shared_libs: ["libjsoncpp"],
 }
@@ -44,7 +44,7 @@
     defaults: ["VehicleHalDefaults"],
     static_libs: ["VehicleHalUtils"],
     header_libs: [
-        "IVehicleGeneratedHeaders-V3",
+        "IVehicleGeneratedHeaders-V4",
         "libbinder_headers",
     ],
     cflags: ["-DENABLE_VEHICLE_HAL_TEST_PROPERTIES"],
@@ -60,7 +60,7 @@
     defaults: ["VehicleHalDefaults"],
     static_libs: ["VehicleHalUtils"],
     header_libs: [
-        "IVehicleGeneratedHeaders-V3",
+        "IVehicleGeneratedHeaders-V4",
     ],
     shared_libs: ["libjsoncpp"],
 }
diff --git a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/Android.bp b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/Android.bp
index abf15c5..90ea027 100644
--- a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/Android.bp
+++ b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/Android.bp
@@ -27,8 +27,6 @@
         "VehicleHalJsonConfigLoader",
         "VehicleHalUtils",
         "libgtest",
-    ],
-    shared_libs: [
         "libjsoncpp",
     ],
     defaults: ["VehicleHalDefaults"],
@@ -43,8 +41,6 @@
         "VehicleHalJsonConfigLoaderEnableTestProperties",
         "VehicleHalUtils",
         "libgtest",
-    ],
-    shared_libs: [
         "libjsoncpp",
     ],
     defaults: ["VehicleHalDefaults"],
diff --git a/automotive/vehicle/aidl/impl/default_config/TEST_MAPPING b/automotive/vehicle/aidl/impl/default_config/TEST_MAPPING
new file mode 100644
index 0000000..15ac9cb
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/default_config/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+    "ravenwood-presubmit": [
+        {
+            "name": "CarServiceHostUnitTest",
+            "host": true
+        }
+    ]
+}
diff --git a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
index 2d1e9ab..489d638 100644
--- a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
+++ b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
@@ -3195,19 +3195,22 @@
             }
         },
         {
-            "property": "VehicleProperty::DISPLAY_BRIGHTNESS",
+            "property": "VehicleProperty::PER_DISPLAY_BRIGHTNESS"
+        },
+        {
+            "property": "VehicleProperty::PER_DISPLAY_MAX_BRIGHTNESS",
             "defaultValue": {
                 "int32Values": [
+                    0,
+                    100,
+                    1,
+                    100,
+                    2,
+                    100,
+                    3,
                     100
                 ]
-            },
-            "areas": [
-                {
-                    "areaId": 0,
-                    "minInt32Value": 0,
-                    "maxInt32Value": 100
-                }
-            ]
+            }
         },
         {
             "property": "VehicleProperty::VALET_MODE_ENABLED",
diff --git a/automotive/vehicle/aidl/impl/default_config/test/Android.bp b/automotive/vehicle/aidl/impl/default_config/test/Android.bp
index 70933be..a88913e 100644
--- a/automotive/vehicle/aidl/impl/default_config/test/Android.bp
+++ b/automotive/vehicle/aidl/impl/default_config/test/Android.bp
@@ -29,12 +29,10 @@
         "VehicleHalUtils",
         "libgmock",
         "libgtest",
+        "libjsoncpp",
     ],
     header_libs: [
-        "IVehicleGeneratedHeaders-V3",
-    ],
-    shared_libs: [
-        "libjsoncpp",
+        "IVehicleGeneratedHeaders-V4",
     ],
     data: [
         ":VehicleHalDefaultProperties_JSON",
@@ -52,15 +50,13 @@
         "VehicleHalUtils",
         "libgmock",
         "libgtest",
+        "libjsoncpp",
     ],
     cflags: [
         "-DENABLE_VEHICLE_HAL_TEST_PROPERTIES",
     ],
     header_libs: [
-        "IVehicleGeneratedHeaders-V3",
-    ],
-    shared_libs: [
-        "libjsoncpp",
+        "IVehicleGeneratedHeaders-V4",
     ],
     data: [
         ":VehicleHalDefaultProperties_JSON",
diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/test/Android.bp b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/test/Android.bp
index 4bc0b12..0d814ea 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/test/Android.bp
+++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/test/Android.bp
@@ -28,8 +28,6 @@
         "VehicleHalUtils",
         "FakeVehicleHalValueGenerators",
         "FakeObd2Frame",
-    ],
-    shared_libs: [
         "libjsoncpp",
     ],
     data: [
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 b301557..a6247a7 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -1056,6 +1056,10 @@
     VhalResult<void> isAdasPropertyAvailableResult;
     VhalResult<bool> isCruiseControlTypeStandardResult;
     switch (propId) {
+        case toInt(VehicleProperty::DISPLAY_BRIGHTNESS):
+        case toInt(VehicleProperty::PER_DISPLAY_BRIGHTNESS):
+            ALOGD("DISPLAY_BRIGHTNESS: %s", value.toString().c_str());
+            return {};
         case toInt(VehicleProperty::AP_POWER_STATE_REPORT):
             *isSpecialValue = true;
             return setApPowerStateReport(value);
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp
index 9f002dd..62c1147 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp
@@ -40,10 +40,10 @@
         "FakeUserHal",
         "libgtest",
         "libgmock",
+        "libjsoncpp",
     ],
     shared_libs: [
         "libgrpc++",
-        "libjsoncpp",
         "libprotobuf-cpp-full",
     ],
     data: [
diff --git a/automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.cpp b/automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.cpp
index 73bb521..8750375 100644
--- a/automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.cpp
@@ -83,6 +83,17 @@
     return configs;
 }
 
+std::optional<aidlvhal::VehiclePropConfig> GRPCVehicleHardware::getPropertyConfig(
+        int32_t propId) const {
+    // TODO(b/354055835): Use GRPC call to get one config instead of getting all the configs.
+    for (const auto& config : getAllPropertyConfigs()) {
+        if (config.prop == propId) {
+            return config;
+        }
+    }
+    return std::nullopt;
+}
+
 aidlvhal::StatusCode GRPCVehicleHardware::setValues(
         std::shared_ptr<const SetValuesCallback> callback,
         const std::vector<aidlvhal::SetValueRequest>& requests) {
@@ -265,6 +276,7 @@
     return {
             .callerShouldDumpState = protoDumpResult.caller_should_dump_state(),
             .buffer = protoDumpResult.buffer(),
+            .refreshPropertyConfigs = protoDumpResult.refresh_property_configs(),
     };
 }
 
diff --git a/automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.h b/automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.h
index 1edf658..15f473c 100644
--- a/automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/grpc/GRPCVehicleHardware.h
@@ -50,6 +50,10 @@
     // Get all the property configs.
     std::vector<aidlvhal::VehiclePropConfig> getAllPropertyConfigs() const override;
 
+    // Get the config for the specified propId.
+    std::optional<aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
+    getPropertyConfig(int32_t propId) const override;
+
     // Set property values asynchronously. Server could return before the property set requests
     // are sent to vehicle bus or before property set confirmation is received. The callback is
     // safe to be called after the function returns and is safe to be called in a different thread.
diff --git a/automotive/vehicle/aidl/impl/grpc/GRPCVehicleProxyServer.cpp b/automotive/vehicle/aidl/impl/grpc/GRPCVehicleProxyServer.cpp
index d7cbe1b..7697c03 100644
--- a/automotive/vehicle/aidl/impl/grpc/GRPCVehicleProxyServer.cpp
+++ b/automotive/vehicle/aidl/impl/grpc/GRPCVehicleProxyServer.cpp
@@ -226,6 +226,7 @@
     auto dumpResult = mHardware->dump(dumpOptionStrings);
     result->set_caller_should_dump_state(dumpResult.callerShouldDumpState);
     result->set_buffer(dumpResult.buffer);
+    result->set_refresh_property_configs(dumpResult.refreshPropertyConfigs);
     return ::grpc::Status::OK;
 }
 
diff --git a/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h b/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h
index f49d91b..0684655 100644
--- a/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h
@@ -20,6 +20,7 @@
 #include <VehicleHalTypes.h>
 
 #include <memory>
+#include <optional>
 #include <vector>
 
 namespace android {
@@ -46,33 +47,53 @@
     int32_t areaId;
 };
 
+namespace aidlvhal = ::aidl::android::hardware::automotive::vehicle;
+
 // An abstract interface to access vehicle hardware.
 // For virtualized VHAL, GrpcVehicleHardware would communicate with a VehicleHardware
 // implementation in another VM through GRPC. For non-virtualzied VHAL, VHAL directly communicates
 // with a VehicleHardware through this interface.
 class IVehicleHardware {
   public:
-    using SetValuesCallback = std::function<void(
-            std::vector<aidl::android::hardware::automotive::vehicle::SetValueResult>)>;
-    using GetValuesCallback = std::function<void(
-            std::vector<aidl::android::hardware::automotive::vehicle::GetValueResult>)>;
-    using PropertyChangeCallback = std::function<void(
-            std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue>)>;
+    using SetValuesCallback = std::function<void(std::vector<aidlvhal::SetValueResult>)>;
+    using GetValuesCallback = std::function<void(std::vector<aidlvhal::GetValueResult>)>;
+    using PropertyChangeCallback = std::function<void(std::vector<aidlvhal::VehiclePropValue>)>;
     using PropertySetErrorCallback = std::function<void(std::vector<SetValueErrorEvent>)>;
 
     virtual ~IVehicleHardware() = default;
 
     // Get all the property configs.
-    virtual std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
-    getAllPropertyConfigs() const = 0;
+    virtual std::vector<aidlvhal::VehiclePropConfig> getAllPropertyConfigs() const = 0;
+
+    // Get the property configs for the specified propId. This is used for early-boot
+    // native VHAL clients to access certain property configs when not all property configs are
+    // available. For example, a config discovery process might be required to determine the
+    // property config for HVAC. However, for early boot properties, e.g. VHAL_HEARTBEAT, it
+    // could return before the config discovery process.
+    //
+    // Currently Android system may try to access the following properties during early boot:
+    // STORAGE_ENCRYPTION_BINDING_SEED, WATCHDOG_ALIVE, WATCHDOG_TERMINATE_PROCESS, VHAL_HEARTBEAT,
+    // CURRENT_POWER_POLICY, POWER_POLICY_REQ, POWER_POLICY_GROUP_REQ. They should return
+    // quickly otherwise the whole bootup process might be blocked.
+    virtual std::optional<aidlvhal::VehiclePropConfig> getPropertyConfig(int32_t propId) const {
+        // The default implementation is to use getAllPropertyConfigs(). This should be
+        // overridden if getAllPropertyConfigs() takes a while to return for initial boot or
+        // relies on ethernet or other communication channel that is not available during early
+        // boot.
+        for (const auto& config : getAllPropertyConfigs()) {
+            if (config.prop == propId) {
+                return config;
+            }
+        }
+        return std::nullopt;
+    }
 
     // Set property values asynchronously. Server could return before the property set requests
     // are sent to vehicle bus or before property set confirmation is received. The callback is
     // safe to be called after the function returns and is safe to be called in a different thread.
-    virtual aidl::android::hardware::automotive::vehicle::StatusCode setValues(
+    virtual aidlvhal::StatusCode setValues(
             std::shared_ptr<const SetValuesCallback> callback,
-            const std::vector<aidl::android::hardware::automotive::vehicle::SetValueRequest>&
-                    requests) = 0;
+            const std::vector<aidlvhal::SetValueRequest>& requests) = 0;
 
     // Get property values asynchronously. Server could return before the property values are ready.
     // The callback is safe to be called after the function returns and is safe to be called in a
@@ -86,7 +107,7 @@
     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;
+    virtual aidlvhal::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.
@@ -179,16 +200,14 @@
     // 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;
+    virtual aidlvhal::StatusCode subscribe([[maybe_unused]] aidlvhal::SubscribeOptions options) {
+        return aidlvhal::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;
+    virtual aidlvhal::StatusCode unsubscribe([[maybe_unused]] int32_t propId,
+                                             [[maybe_unused]] int32_t areaId) {
+        return aidlvhal::StatusCode::OK;
     }
 
     // This function is deprecated, subscribe/unsubscribe should be used instead.
@@ -216,10 +235,10 @@
     //
     // If the impl is always polling at {@code maxSampleRate} as specified in config, then this
     // function can be a no-op.
-    virtual aidl::android::hardware::automotive::vehicle::StatusCode updateSampleRate(
-            [[maybe_unused]] int32_t propId, [[maybe_unused]] int32_t areaId,
-            [[maybe_unused]] float sampleRate) {
-        return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
+    virtual aidlvhal::StatusCode updateSampleRate([[maybe_unused]] int32_t propId,
+                                                  [[maybe_unused]] int32_t areaId,
+                                                  [[maybe_unused]] float sampleRate) {
+        return aidlvhal::StatusCode::OK;
     }
 };
 
diff --git a/automotive/vehicle/aidl/impl/proto/Android.bp b/automotive/vehicle/aidl/impl/proto/Android.bp
index b2edf75..0d3df49 100644
--- a/automotive/vehicle/aidl/impl/proto/Android.bp
+++ b/automotive/vehicle/aidl/impl/proto/Android.bp
@@ -106,3 +106,21 @@
         "-Wno-unused-parameter",
     ],
 }
+
+rust_protobuf {
+    name: "libvehicle_hal_property_protos",
+    crate_name: "vehicle_hal_property_protos",
+    protos: [":VehicleHalProtoFiles"],
+    source_stem: "vehicle_hal_property_protos",
+    host_supported: true,
+    vendor_available: true,
+    product_available: true,
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    exported_include_dirs: ["."],
+    proto_flags: [
+        "-I external/protobuf/src",
+    ],
+}
diff --git a/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/DumpResult.proto b/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/DumpResult.proto
index 25bb7d4..fbfb505 100644
--- a/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/DumpResult.proto
+++ b/automotive/vehicle/aidl/impl/proto/android/hardware/automotive/vehicle/DumpResult.proto
@@ -25,4 +25,6 @@
     bool caller_should_dump_state = 1;
     /* The dumped information for the caller to print. */
     string buffer = 2;
+    /* To pass if DefaultVehicleHal should refresh the property configs. */
+    bool refresh_property_configs = 3;
 }
diff --git a/automotive/vehicle/aidl/impl/vhal/Android.bp b/automotive/vehicle/aidl/impl/vhal/Android.bp
index 5cc071d..54d148e 100644
--- a/automotive/vehicle/aidl/impl/vhal/Android.bp
+++ b/automotive/vehicle/aidl/impl/vhal/Android.bp
@@ -66,7 +66,7 @@
     ],
     header_libs: [
         "IVehicleHardware",
-        "IVehicleGeneratedHeaders-V3",
+        "IVehicleGeneratedHeaders-V4",
     ],
     shared_libs: [
         "libbinder_ndk",
diff --git a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
index b58d0f5..932a2e2 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
@@ -199,6 +199,8 @@
 
     bool checkDumpPermission();
 
+    bool isConfigSupportedForCurrentVhalVersion(const aidlvhal::VehiclePropConfig& config) const;
+
     bool getAllPropConfigsFromHardwareLocked() const EXCLUDES(mConfigLock);
 
     // The looping handler function to process all onBinderDied or onBinderUnlinked events in
diff --git a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
index e062a28..0ead819 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
@@ -340,32 +340,37 @@
     return myVersion;
 }
 
+bool DefaultVehicleHal::isConfigSupportedForCurrentVhalVersion(
+        const VehiclePropConfig& config) const {
+    int32_t myVersion = getVhalInterfaceVersion();
+    if (!isSystemProp(config.prop)) {
+        return true;
+    }
+    VehicleProperty property = static_cast<VehicleProperty>(config.prop);
+    std::string propertyName = aidl::android::hardware::automotive::vehicle::toString(property);
+    auto it = VersionForVehicleProperty.find(property);
+    if (it == VersionForVehicleProperty.end()) {
+        ALOGE("The property: %s is not a supported system property, ignore", propertyName.c_str());
+        return false;
+    }
+    int requiredVersion = it->second;
+    if (myVersion < requiredVersion) {
+        ALOGE("The property: %s is not supported for current client VHAL version, "
+              "require %d, current version: %d, ignore",
+              propertyName.c_str(), requiredVersion, myVersion);
+        return false;
+    }
+    return true;
+}
+
 bool DefaultVehicleHal::getAllPropConfigsFromHardwareLocked() const {
     ALOGD("Get all property configs from hardware");
     auto configs = mVehicleHardware->getAllPropertyConfigs();
     std::vector<VehiclePropConfig> filteredConfigs;
-    int32_t myVersion = getVhalInterfaceVersion();
-    for (auto& config : configs) {
-        if (!isSystemProp(config.prop)) {
+    for (const auto& config : configs) {
+        if (isConfigSupportedForCurrentVhalVersion(config)) {
             filteredConfigs.push_back(std::move(config));
-            continue;
         }
-        VehicleProperty property = static_cast<VehicleProperty>(config.prop);
-        std::string propertyName = aidl::android::hardware::automotive::vehicle::toString(property);
-        auto it = VersionForVehicleProperty.find(property);
-        if (it == VersionForVehicleProperty.end()) {
-            ALOGE("The property: %s is not a supported system property, ignore",
-                  propertyName.c_str());
-            continue;
-        }
-        int requiredVersion = it->second;
-        if (myVersion < requiredVersion) {
-            ALOGE("The property: %s is not supported for current client VHAL version, "
-                  "require %d, current version: %d, ignore",
-                  propertyName.c_str(), requiredVersion, myVersion);
-            continue;
-        }
-        filteredConfigs.push_back(std::move(config));
     }
 
     {
@@ -431,6 +436,19 @@
 
 Result<VehiclePropConfig> DefaultVehicleHal::getConfig(int32_t propId) const {
     Result<VehiclePropConfig> result;
+
+    if (!mConfigInit) {
+        std::optional<VehiclePropConfig> config = mVehicleHardware->getPropertyConfig(propId);
+        if (!config.has_value()) {
+            return Error() << "no config for property, ID: " << propId;
+        }
+        if (!isConfigSupportedForCurrentVhalVersion(config.value())) {
+            return Error() << "property not supported for current VHAL interface, ID: " << propId;
+        }
+
+        return config.value();
+    }
+
     getConfigsByPropId([this, &result, propId](const auto& configsByPropId) {
         SharedScopedLockAssertion lockAssertion(mConfigLock);
 
@@ -685,6 +703,22 @@
 ScopedAStatus DefaultVehicleHal::getPropConfigs(const std::vector<int32_t>& props,
                                                 VehiclePropConfigs* output) {
     std::vector<VehiclePropConfig> configs;
+
+    if (!mConfigInit) {
+        for (int32_t prop : props) {
+            auto maybeConfig = mVehicleHardware->getPropertyConfig(prop);
+            if (!maybeConfig.has_value() ||
+                !isConfigSupportedForCurrentVhalVersion(maybeConfig.value())) {
+                return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+                        toInt(StatusCode::INVALID_ARG),
+                        StringPrintf("no config for property, ID: %" PRId32, prop).c_str());
+            }
+            configs.push_back(maybeConfig.value());
+        }
+
+        return vectorToStableLargeParcelable(std::move(configs), output);
+    }
+
     ScopedAStatus status = ScopedAStatus::ok();
     getConfigsByPropId([this, &configs, &status, &props](const auto& configsByPropId) {
         SharedScopedLockAssertion lockAssertion(mConfigLock);
diff --git a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
index 11a8fc7..4891bf5 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
@@ -650,6 +650,8 @@
 
     auto hardware = std::make_unique<MockVehicleHardware>();
     hardware->setPropertyConfigs(testConfigs);
+    // Store the pointer for testing. We are sure it is valid.
+    MockVehicleHardware* hardwarePtr = hardware.get();
     auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
     std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
 
@@ -658,6 +660,7 @@
 
     ASSERT_TRUE(status.isOk()) << "getPropConfigs failed: " << status.getMessage();
     ASSERT_EQ(output.payloads, testConfigs);
+    ASSERT_FALSE(hardwarePtr->getAllPropertyConfigsCalled());
 }
 
 TEST_F(DefaultVehicleHalTest, testGetPropConfigsInvalidArg) {
@@ -704,6 +707,34 @@
     ASSERT_TRUE(maybeGetValueResults.has_value()) << "no results in callback";
     EXPECT_EQ(maybeGetValueResults.value().payloads, expectedResults) << "results mismatch";
     EXPECT_EQ(countClients(), static_cast<size_t>(1));
+    ASSERT_FALSE(getHardware()->getAllPropertyConfigsCalled());
+}
+
+TEST_F(DefaultVehicleHalTest, testGetValuesSmall_AfterGetAllPropConfigs) {
+    GetValueRequests requests;
+    std::vector<GetValueResult> expectedResults;
+    std::vector<GetValueRequest> expectedHardwareRequests;
+
+    // If we already called getAllPropConfigs, the configs will be cached.
+    VehiclePropConfigs output;
+    getClient()->getAllPropConfigs(&output);
+
+    ASSERT_TRUE(getValuesTestCases(10, requests, expectedResults, expectedHardwareRequests).ok());
+
+    getHardware()->addGetValueResponses(expectedResults);
+
+    auto status = getClient()->getValues(getCallbackClient(), requests);
+
+    ASSERT_TRUE(status.isOk()) << "getValues failed: " << status.getMessage();
+
+    EXPECT_EQ(getHardware()->nextGetValueRequests(), expectedHardwareRequests)
+            << "requests to hardware mismatch";
+
+    auto maybeGetValueResults = getCallback()->nextGetValueResults();
+    ASSERT_TRUE(maybeGetValueResults.has_value()) << "no results in callback";
+    EXPECT_EQ(maybeGetValueResults.value().payloads, expectedResults) << "results mismatch";
+    EXPECT_EQ(countClients(), static_cast<size_t>(1));
+    ASSERT_TRUE(getHardware()->getAllPropertyConfigsCalled());
 }
 
 TEST_F(DefaultVehicleHalTest, testGetValuesLarge) {
@@ -1016,6 +1047,34 @@
     ASSERT_TRUE(maybeSetValueResults.has_value()) << "no results in callback";
     ASSERT_EQ(maybeSetValueResults.value().payloads, expectedResults) << "results mismatch";
     EXPECT_EQ(countClients(), static_cast<size_t>(1));
+    ASSERT_FALSE(getHardware()->getAllPropertyConfigsCalled());
+}
+
+TEST_F(DefaultVehicleHalTest, testSetValuesSmall_AfterGetAllPropConfigs) {
+    SetValueRequests requests;
+    std::vector<SetValueResult> expectedResults;
+    std::vector<SetValueRequest> expectedHardwareRequests;
+
+    // If we already called getAllPropConfigs, the configs will be cached.
+    VehiclePropConfigs output;
+    getClient()->getAllPropConfigs(&output);
+
+    ASSERT_TRUE(setValuesTestCases(10, requests, expectedResults, expectedHardwareRequests).ok());
+
+    getHardware()->addSetValueResponses(expectedResults);
+
+    auto status = getClient()->setValues(getCallbackClient(), requests);
+
+    ASSERT_TRUE(status.isOk()) << "setValues failed: " << status.getMessage();
+
+    EXPECT_EQ(getHardware()->nextSetValueRequests(), expectedHardwareRequests)
+            << "requests to hardware mismatch";
+
+    auto maybeSetValueResults = getCallback()->nextSetValueResults();
+    ASSERT_TRUE(maybeSetValueResults.has_value()) << "no results in callback";
+    ASSERT_EQ(maybeSetValueResults.value().payloads, expectedResults) << "results mismatch";
+    EXPECT_EQ(countClients(), static_cast<size_t>(1));
+    ASSERT_TRUE(getHardware()->getAllPropertyConfigsCalled());
 }
 
 TEST_F(DefaultVehicleHalTest, testSetValuesLarge) {
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
index db15c89..e796ce5 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
@@ -45,9 +45,20 @@
 
 std::vector<VehiclePropConfig> MockVehicleHardware::getAllPropertyConfigs() const {
     std::scoped_lock<std::mutex> lockGuard(mLock);
+    mGetAllPropertyConfigsCalled = true;
     return mPropertyConfigs;
 }
 
+std::optional<VehiclePropConfig> MockVehicleHardware::getPropertyConfig(int32_t propId) const {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    for (const auto& config : mPropertyConfigs) {
+        if (config.prop == propId) {
+            return config;
+        }
+    }
+    return std::nullopt;
+}
+
 StatusCode MockVehicleHardware::setValues(std::shared_ptr<const SetValuesCallback> callback,
                                           const std::vector<SetValueRequest>& requests) {
     std::scoped_lock<std::mutex> lockGuard(mLock);
@@ -336,6 +347,11 @@
     (*mPropertySetErrorCallback)(errorEvents);
 }
 
+bool MockVehicleHardware::getAllPropertyConfigsCalled() {
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+    return mGetAllPropertyConfigsCalled;
+}
+
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
index eeca582..06e01a8 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
@@ -47,6 +47,8 @@
 
     std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
     getAllPropertyConfigs() const override;
+    std::optional<aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
+    getPropertyConfig(int32_t propId) const override;
     aidl::android::hardware::automotive::vehicle::StatusCode setValues(
             std::shared_ptr<const SetValuesCallback> callback,
             const std::vector<aidl::android::hardware::automotive::vehicle::SetValueRequest>&
@@ -98,6 +100,9 @@
     std::vector<aidl::android::hardware::automotive::vehicle::SubscribeOptions>
     getSubscribeOptions();
     void clearSubscribeOptions();
+    // Whether getAllPropertyConfigs() has been called, which blocks all all property configs
+    // being ready.
+    bool getAllPropertyConfigsCalled();
 
   private:
     mutable std::mutex mLock;
@@ -143,6 +148,8 @@
 
     DumpResult mDumpResult;
 
+    mutable bool mGetAllPropertyConfigsCalled GUARDED_BY(mLock) = false;
+
     // RecurrentTimer is thread-safe.
     std::shared_ptr<RecurrentTimer> mRecurrentTimer;
     std::unordered_map<int32_t, std::unordered_map<int32_t, std::shared_ptr<std::function<void()>>>>
diff --git a/automotive/vehicle/tools/generate_emu_metadata/src/com/android/car/tool/EmuMetadataGenerator.java b/automotive/vehicle/tools/generate_emu_metadata/src/com/android/car/tool/EmuMetadataGenerator.java
index bea5951..7f4ceb8 100644
--- a/automotive/vehicle/tools/generate_emu_metadata/src/com/android/car/tool/EmuMetadataGenerator.java
+++ b/automotive/vehicle/tools/generate_emu_metadata/src/com/android/car/tool/EmuMetadataGenerator.java
@@ -79,17 +79,18 @@
             + "either this or input_files must be specified\n" + INPUT_FILES_OPTION
             + ": one or more Java files, this is used to decide the input "
             + "directory\n" + PACKAGE_NAME_OPTION
-            + ": the optional package name for the interface, by default is " + DEFAULT_PACKAGE_NAME
-            + "\n" + OUTPUT_JSON_OPTION + ": The output JSON file\n" + OUTPUT_EMPTY_FILE_OPTION
-            + ": Only used for check_mode, this file will be created if "
+            + ": the optional package name for the interface, by default is "
+            + DEFAULT_PACKAGE_NAME + "\n" + OUTPUT_JSON_OPTION + ": The output JSON file\n"
+            + OUTPUT_EMPTY_FILE_OPTION + ": Only used for check_mode, this file will be created if "
             + "check  passed\n" + CHECK_AGAINST_OPTION
             + ": An optional JSON file to check against. If specified, the "
-            + "generated output file will be checked against this file, if they are not the same, "
+            + ("generated output file will be checked against this file, if they are not the "
+                    + "same, ")
             + "the script will fail, otherwise, the output_empty_file will be created\n"
             + "For example: \n"
             + "EnumMetadataGenerator --input_dir out/soong/.intermediates/hardware/"
             + "interfaces/automotive/vehicle/aidl_property/android.hardware.automotive.vehicle."
-            + "property-V3-java-source/gen/ --package_name android.hardware.automotive.vehicle "
+            + "property-V4-java-source/gen/ --package_name android.hardware.automotive.vehicle "
             + "--output_json /tmp/android.hardware.automotive.vehicle-types-meta.json";
     private static final String VEHICLE_PROPERTY_FILE = "VehicleProperty.java";
     private static final String CHECK_FILE_PATH =
diff --git a/automotive/vehicle/vhal_static_cpp_lib.mk b/automotive/vehicle/vhal_static_cpp_lib.mk
index 6b3d486..9371453 100644
--- a/automotive/vehicle/vhal_static_cpp_lib.mk
+++ b/automotive/vehicle/vhal_static_cpp_lib.mk
@@ -17,4 +17,4 @@
 
 LOCAL_STATIC_LIBRARIES += \
     android.hardware.automotive.vehicle-V3-ndk \
-    android.hardware.automotive.vehicle.property-V3-ndk
+    android.hardware.automotive.vehicle.property-V4-ndk
diff --git a/automotive/vehicle/vts/Android.bp b/automotive/vehicle/vts/Android.bp
index 303e5a6..d55dc33 100644
--- a/automotive/vehicle/vts/Android.bp
+++ b/automotive/vehicle/vts/Android.bp
@@ -44,7 +44,7 @@
         "vhalclient_defaults",
     ],
     header_libs: [
-        "IVehicleGeneratedHeaders-V3",
+        "IVehicleGeneratedHeaders-V4",
     ],
     test_suites: [
         "general-tests",
diff --git a/biometrics/common/aidl/Android.bp b/biometrics/common/aidl/Android.bp
index 4afecb4..8c9a357 100644
--- a/biometrics/common/aidl/Android.bp
+++ b/biometrics/common/aidl/Android.bp
@@ -22,6 +22,12 @@
         cpp: {
             enabled: false,
         },
+        ndk: {
+            apex_available: [
+                "//apex_available:anyapex",
+                "//apex_available:platform",
+            ],
+        },
         rust: {
             enabled: true,
         },
diff --git a/biometrics/common/config/Android.bp b/biometrics/common/config/Android.bp
index d38ffe8..b86aafa 100644
--- a/biometrics/common/config/Android.bp
+++ b/biometrics/common/config/Android.bp
@@ -22,7 +22,7 @@
     //   SPDX-license-identifier-Apache-2.0
     name: "android.hardware.biometrics.common.config",
     export_include_dirs: ["include"],
-    vendor: true,
+    vendor_available: true,
     srcs: [
         "Config.cpp",
     ],
@@ -30,6 +30,10 @@
         "libbase",
         "libbinder_ndk",
     ],
+    apex_available: [
+        "//apex_available:anyapex",
+        "//apex_available:platform",
+    ],
 }
 
 cc_test_host {
diff --git a/biometrics/common/config/include/config/Config.h b/biometrics/common/config/include/config/Config.h
index 0367832..b1affdc 100644
--- a/biometrics/common/config/include/config/Config.h
+++ b/biometrics/common/config/include/config/Config.h
@@ -100,7 +100,11 @@
         } else if (std::holds_alternative<OptIntVec>(v)) {
             for (auto x : std::get<OptIntVec>(v))
                 if (x.has_value()) os << x.value() << " ";
+        } else if (std::holds_alternative<OptString>(v)) {
+            OptString ov = std::get<OptString>(v);
+            if (ov.has_value()) os << ov.value();
         }
+
         return os.str();
     }
     std::string toString() const {
diff --git a/biometrics/common/thread/Android.bp b/biometrics/common/thread/Android.bp
index e7a7e4c..c1ebe3b 100644
--- a/biometrics/common/thread/Android.bp
+++ b/biometrics/common/thread/Android.bp
@@ -10,10 +10,14 @@
     //   SPDX-license-identifier-Apache-2.0
     name: "android.hardware.biometrics.common.thread",
     export_include_dirs: ["include"],
-    vendor: true,
+    vendor_available: true,
     srcs: [
         "WorkerThread.cpp",
     ],
+    apex_available: [
+        "//apex_available:anyapex",
+        "//apex_available:platform",
+    ],
 }
 
 cc_test_host {
diff --git a/biometrics/common/util/Android.bp b/biometrics/common/util/Android.bp
index 599c491..a0bd211 100644
--- a/biometrics/common/util/Android.bp
+++ b/biometrics/common/util/Android.bp
@@ -6,7 +6,7 @@
     //   SPDX-license-identifier-Apache-2.0
     name: "android.hardware.biometrics.common.util",
     export_include_dirs: ["include"],
-    vendor: true,
+    vendor_available: true,
     srcs: [
         "CancellationSignal.cpp",
     ],
@@ -15,4 +15,8 @@
         "libbinder_ndk",
         "android.hardware.biometrics.common-V4-ndk",
     ],
+    apex_available: [
+        "//apex_available:anyapex",
+        "//apex_available:platform",
+    ],
 }
diff --git a/biometrics/face/aidl/Android.bp b/biometrics/face/aidl/Android.bp
index fadcde7..54d01a7 100644
--- a/biometrics/face/aidl/Android.bp
+++ b/biometrics/face/aidl/Android.bp
@@ -11,7 +11,7 @@
     name: "android.hardware.biometrics.face",
     vendor_available: true,
     srcs: [
-        "android/hardware/biometrics/face/**/*.aidl",
+        "android/hardware/biometrics/face/*.aidl",
     ],
     imports: [
         "android.hardware.biometrics.common-V4",
@@ -36,6 +36,10 @@
             additional_shared_libraries: [
                 "libnativewindow",
             ],
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.hardware.biometrics.face.virtual",
+            ],
         },
     },
     versions_with_info: [
@@ -74,5 +78,39 @@
 
     ],
     frozen: true,
+}
 
+aidl_interface {
+    name: "android.hardware.biometrics.face.virtualhal",
+    srcs: [
+        "android/hardware/biometrics/face/virtualhal/*.aidl",
+    ],
+    imports: [
+        "android.hardware.biometrics.common-V4",
+        "android.hardware.keymaster-V4",
+        "android.hardware.biometrics.face-V4",
+    ],
+    vendor_available: true,
+    unstable: true,
+    backend: {
+        java: {
+            platform_apis: true,
+        },
+        rust: {
+            enabled: false,
+        },
+        cpp: {
+            enabled: false,
+        },
+        ndk: {
+            additional_shared_libraries: [
+                "libnativewindow",
+            ],
+            apex_available: [
+                "com.android.hardware.biometrics.face.virtual",
+                "//apex_available:platform",
+            ],
+        },
+    },
+    frozen: false,
 }
diff --git a/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl b/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl
index 26cb361..0dbf052 100644
--- a/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl
+++ b/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl
@@ -73,7 +73,7 @@
      * Note that this interface allows multiple in-flight challenges. Invoking generateChallenge
      * twice does not invalidate the first challenge. The challenge is invalidated only when:
      *   1) Its lifespan exceeds the challenge timeout defined in the TEE.
-     *   2) IFingerprint#revokeChallenge is invoked
+     *   2) IFace#revokeChallenge is invoked
      *
      * For example, the following is a possible table of valid challenges:
      * ----------------------------------------------
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/AcquiredInfoAndVendorCode.aidl b/biometrics/face/aidl/android/hardware/biometrics/face/virtualhal/AcquiredInfoAndVendorCode.aidl
similarity index 86%
copy from biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/AcquiredInfoAndVendorCode.aidl
copy to biometrics/face/aidl/android/hardware/biometrics/face/virtualhal/AcquiredInfoAndVendorCode.aidl
index c7be950..a254120 100644
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/AcquiredInfoAndVendorCode.aidl
+++ b/biometrics/face/aidl/android/hardware/biometrics/face/virtualhal/AcquiredInfoAndVendorCode.aidl
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-package android.hardware.biometrics.fingerprint;
+package android.hardware.biometrics.face.virtualhal;
 
-import android.hardware.biometrics.fingerprint.AcquiredInfo;
+import android.hardware.biometrics.face.AcquiredInfo;
 
 /**
  * @hide
  */
-@VintfStability
 union AcquiredInfoAndVendorCode {
     /**
      * Acquired info as specified in AcqauiredInfo.aidl
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/EnrollmentProgressStep.aidl b/biometrics/face/aidl/android/hardware/biometrics/face/virtualhal/EnrollmentProgressStep.aidl
similarity index 82%
copy from biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/EnrollmentProgressStep.aidl
copy to biometrics/face/aidl/android/hardware/biometrics/face/virtualhal/EnrollmentProgressStep.aidl
index bf038f6..7fbcf5d 100644
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/EnrollmentProgressStep.aidl
+++ b/biometrics/face/aidl/android/hardware/biometrics/face/virtualhal/EnrollmentProgressStep.aidl
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-package android.hardware.biometrics.fingerprint;
+package android.hardware.biometrics.face.virtualhal;
 
-import android.hardware.biometrics.fingerprint.AcquiredInfoAndVendorCode;
+import android.hardware.biometrics.face.virtualhal.AcquiredInfoAndVendorCode;
 
 /**
  * @hide
  */
-@VintfStability
 parcelable EnrollmentProgressStep {
     /**
      * The duration of the enrollment step in milli-seconds
@@ -30,7 +29,7 @@
 
     /**
      * The sequence of acquired info and vendor code to be issued by HAL during the step.
-     * The codes are evenly spreaded over the duration
+     * The codes are evenly spread over the duration
      */
     AcquiredInfoAndVendorCode[] acquiredInfoAndVendorCodes;
 }
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/IVirtualHal.aidl b/biometrics/face/aidl/android/hardware/biometrics/face/virtualhal/IVirtualHal.aidl
similarity index 71%
copy from biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/IVirtualHal.aidl
copy to biometrics/face/aidl/android/hardware/biometrics/face/virtualhal/IVirtualHal.aidl
index cb9135e..1d3d934 100644
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/IVirtualHal.aidl
+++ b/biometrics/face/aidl/android/hardware/biometrics/face/virtualhal/IVirtualHal.aidl
@@ -14,19 +14,18 @@
  * limitations under the License.
  */
 
-package android.hardware.biometrics.fingerprint;
+package android.hardware.biometrics.face.virtualhal;
 
 import android.hardware.biometrics.common.SensorStrength;
-import android.hardware.biometrics.fingerprint.AcquiredInfoAndVendorCode;
-import android.hardware.biometrics.fingerprint.FingerprintSensorType;
-import android.hardware.biometrics.fingerprint.NextEnrollment;
-import android.hardware.biometrics.fingerprint.SensorLocation;
+import android.hardware.biometrics.face.FaceSensorType;
+import android.hardware.biometrics.face.IFace;
+import android.hardware.biometrics.face.virtualhal.AcquiredInfoAndVendorCode;
+import android.hardware.biometrics.face.virtualhal.NextEnrollment;
 
 /**
  * @hide
  */
-@VintfStability
-oneway interface IVirtualHal {
+interface IVirtualHal {
     /**
      * The operation failed due to invalid input parameters, the error messages should
      * gives more details
@@ -34,13 +33,13 @@
     const int STATUS_INVALID_PARAMETER = 1;
 
     /**
-     * Set Fingerprint Virtual HAL behavior parameters
+     * Set Face Virtual HAL behavior parameters
      */
 
     /**
      * setEnrollments
      *
-     * Set the ids of the fingerprints that were currently enrolled in the Virtual HAL,
+     * Set the ids of the faces that were currently enrolled in the Virtual HAL,
      *
      * @param ids ids can contain 1 or more ids, each must be larger than 0
      */
@@ -49,7 +48,7 @@
     /**
      * setEnrollmentHit
      *
-     * Set current fingerprint enrollment ids in Fingerprint Virtual HAL,
+     * Set current face enrollment ids in Face Virtual HAL,
      *
      * @param ids ids can contain 1 or more ids, each must be larger than 0
      */
@@ -67,7 +66,7 @@
     /**
      * setAuthenticatorId
      *
-     * Set authenticator id in virtual HAL, the id is returned in ISession#getAuthenticatorId() call
+     * Set authenticator id in virtual HAL, the id is returned in ISession#AuthenticatorId() call
      *
      * @param id authenticator id value, only applied to the sensor with SensorStrength::STRONG.
      */
@@ -116,7 +115,7 @@
      * setOperationAuthenticateDuration
      *
      * Set authentication duration covering the HAL authetication from start to end, including
-     * fingerprint capturing, and matching, acquired info reporting. In case a sequence of acquired
+     * face capturing, and matching, acquired info reporting. In case a sequence of acquired
      * info code are specified via setOperationAuthenticateAcquired(), the reporting is evenly
      * distributed over the duration.
      *
@@ -130,7 +129,9 @@
      * setOperationAuthenticateError
      *
      * Force authentication to error out for non-zero error
-     * Check hardware/interfaces/biometrics/fingerprint/aidl/default/README.md for valid error codes
+     * Check
+     * hardware/interfaces/biometrics/face/aidl/default/aidl/android/hardware/biometrics/face/Error.aidl
+     * for valid error codes
      *
      * @param error if error < 1000
      *                  non-vendor error
@@ -143,27 +144,15 @@
      * setOperationAuthenticateAcquired
      *
      * Set one of more acquired info codes for the virtual hal to report during authentication
-     * Check hardware/interfaces/biometrics/fingerprint/aidl/default/README.md for valid acquired
-     * info codes
+     * Check
+     * hardware/interfaces/biometrics/face/aidl/aidl/android/hardware/biometrics/face/AcquiredInfo.aidl
+     * for valid acquired info codes
      *
      * @param acquired[], one or more acquired info codes
      */
     void setOperationAuthenticateAcquired(in AcquiredInfoAndVendorCode[] acquired);
 
     /**
-     * setOperationEnrollError
-     *
-     * Force enrollment operation to error out for non-zero error
-     * Check hardware/interfaces/biometrics/fingerprint/aidl/default/README.md for valid error codes
-     *
-     * @param error if error < 1000
-     *                  non-vendor error
-     *              else
-     *                  vendor error
-     */
-    void setOperationEnrollError(in int error);
-
-    /**
      * setOperationEnrollLatency
      *
      * Set enrollment latency in the virtual hal in a fixed value (single element) or random
@@ -202,42 +191,11 @@
     void setOperationDetectInteractionLatency(in int[] latencyMs);
 
     /**
-     * setOperationDetectInteractionError
+     * setOperationDetectInteractionFails
      *
-     * Force detect interaction operation to error out for non-zero error
-     * Check hardware/interfaces/biometrics/fingerprint/aidl/default/README.md for valid error codes
-     *
-     * @param error if error < 1000
-     *                  non-vendor error
-     *              else
-     *                  vendor error
+     * Force detect interaction operation to fail
      */
-    void setOperationDetectInteractionError(in int error);
-
-    /**
-     * setOperationDetectInteractionDuration
-     *
-     * Set detect interaction duration covering the HAL authetication from start to end, including
-     * fingerprint detect and acquired info reporting. In case a sequence of acquired info code are
-     * specified via setOperationDetectInteractionAcquired(), the reporting is evenly distributed
-     * over the duration.
-     *
-     * This method fails with STATUS_INVALID_PARAMETERS if the passed-in value is negative
-     *
-     * @param duration  value is in milli-seconds
-     */
-    void setOperationDetectInteractionDuration(in int durationMs);
-
-    /**
-     * setOperationDetectInteractionAcquired
-     *
-     * Set one of more acquired info codes for the virtual hal to report during detect interaction
-     * Check hardware/interfaces/biometrics/fingerprint/aidl/default/README.md for valid acquired
-     * info codes
-     *
-     * @param acquired[], one or more acquired info codes
-     */
-    void setOperationDetectInteractionAcquired(in AcquiredInfoAndVendorCode[] acquired);
+    void setOperationDetectInteractionFails(in boolean error);
 
     /**
      * setLockout
@@ -261,6 +219,15 @@
     void setLockoutEnable(in boolean enable);
 
     /**
+     * setLockoutTimedEnable
+     *
+     * Whether to enable authentication-fail-based time-based-lockout tracking or not.
+     *
+     * @param enable, set true to enable the time-basedlockout tracking
+     */
+    void setLockoutTimedEnable(in boolean enable);
+
+    /**
      * setLockoutTimedThreshold
      *
      * Set the number of consecutive authentication failures that triggers the timed-based lock to
@@ -303,16 +270,28 @@
     void resetConfigurations();
 
     /**
-     * The following functions are used to configure Fingerprint Virtual HAL sensor properties
-     *  refer to SensorProps.aidl and CommonProps.aidl for details of each property
+     * setType
+     *
+     * Configure virtual face sensor type
+     *
+     * @param type, sensor type as specified in FaceSensorType.aidl
+     *
      */
-    void setType(in FingerprintSensorType type);
-    void setSensorId(in int id);
+    void setType(in FaceSensorType type);
+
+    /**
+     *  setSensorStrength
+     *
+     *  Configure virtual face sensor strength
+     *
+     * @param sensor strength as specified in common/SensorStrength.aidl
+     */
     void setSensorStrength(in SensorStrength strength);
-    void setMaxEnrollmentPerUser(in int max);
-    void setSensorLocation(in SensorLocation loc);
-    void setNavigationGuesture(in boolean v);
-    void setDetectInteraction(in boolean v);
-    void setDisplayTouch(in boolean v);
-    void setControlIllumination(in boolean v);
+
+    /**
+     * getFaceHal
+     *
+     * @return IFace interface associated with IVirtualHal instance
+     */
+    IFace getFaceHal();
 }
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/NextEnrollment.aidl b/biometrics/face/aidl/android/hardware/biometrics/face/virtualhal/NextEnrollment.aidl
similarity index 87%
rename from biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/NextEnrollment.aidl
rename to biometrics/face/aidl/android/hardware/biometrics/face/virtualhal/NextEnrollment.aidl
index 4b50850..d3547a8 100644
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/NextEnrollment.aidl
+++ b/biometrics/face/aidl/android/hardware/biometrics/face/virtualhal/NextEnrollment.aidl
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 
-package android.hardware.biometrics.fingerprint;
+package android.hardware.biometrics.face.virtualhal;
 
 /**
  * @hide
  */
-@VintfStability
 parcelable NextEnrollment {
     /**
      *  Identifier of the next enrollment if successful
@@ -31,7 +30,7 @@
      *  and sequence of acquired info codes to be generated by HAL.
      *  See EnrollmentProgressStep.aidl for more details
      */
-    android.hardware.biometrics.fingerprint.EnrollmentProgressStep[] progressSteps;
+    android.hardware.biometrics.face.virtualhal.EnrollmentProgressStep[] progressSteps;
 
     /**
      * Success or failure of the next enrollment
diff --git a/biometrics/face/aidl/android/hardware/biometrics/face/virtualhal/README.md b/biometrics/face/aidl/android/hardware/biometrics/face/virtualhal/README.md
new file mode 100644
index 0000000..bf1a4b1
--- /dev/null
+++ b/biometrics/face/aidl/android/hardware/biometrics/face/virtualhal/README.md
@@ -0,0 +1,3 @@
+The aidl files in this directory are used to control/configure face virtual hal
+via IVirtualHal interface
+
diff --git a/biometrics/face/aidl/default/Android.bp b/biometrics/face/aidl/default/Android.bp
index 685639c..bed0405 100644
--- a/biometrics/face/aidl/default/Android.bp
+++ b/biometrics/face/aidl/default/Android.bp
@@ -9,21 +9,13 @@
 }
 
 filegroup {
-    name: "face-example.rc",
-    srcs: ["face-example.rc"],
+    name: "face-virtual.rc",
+    srcs: ["face-virtual.rc"],
 }
 
-filegroup {
-    name: "face-example.xml",
-    srcs: ["face-example.xml"],
-}
-
-cc_binary {
-    name: "android.hardware.biometrics.face-service.example",
-    relative_install_path: "hw",
-    init_rc: [":face-example.rc"],
-    vintf_fragments: [":face-example.xml"],
-    vendor: true,
+cc_library_static {
+    name: "android.hardware.biometrics.face-service.lib",
+    vendor_available: true,
 
     shared_libs: [
         "libbinder_ndk",
@@ -32,32 +24,80 @@
     ],
     srcs: [
         "FakeLockoutTracker.cpp",
-        "main.cpp",
         "Face.cpp",
         "FakeFaceEngine.cpp",
         "Session.cpp",
+        "FaceConfig.cpp",
+        "VirtualHal.cpp",
+        "main.cpp",
     ],
     include_dirs: [
         "frameworks/native/aidl/gui",
     ],
     stl: "c++_static",
-    static_libs: [
+    whole_static_libs: [
         "android.hardware.biometrics.common-V4-ndk",
+        "android.hardware.biometrics.common.config",
         "android.hardware.biometrics.common.thread",
         "android.hardware.biometrics.common.util",
+        "android.hardware.biometrics.face.virtualhal-ndk",
         "android.hardware.biometrics.face-V4-ndk",
         "android.hardware.common-V2-ndk",
         "android.hardware.keymaster-V4-ndk",
         "libandroid.hardware.biometrics.face.VirtualProps",
         "libbase",
     ],
+    apex_available: [
+        "com.android.hardware.biometrics.face.virtual",
+        "//apex_available:platform",
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.biometrics.face-service.example",
+    system_ext_specific: true,
+    relative_install_path: "hw",
+
+    shared_libs: [
+        "libbinder_ndk",
+        "liblog",
+        "libnativewindow",
+    ],
+    whole_static_libs: [
+        "android.hardware.biometrics.face-service.lib",
+    ],
+    installable: false, // install APEX instead
+    apex_available: [
+        "com.android.hardware.biometrics.face.virtual",
+        "//apex_available:platform",
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.biometrics.face-service.default",
+    vendor: true,
+    relative_install_path: "hw",
+    init_rc: ["face-default.rc"],
+    vintf_fragments: ["face-default.xml"],
+    shared_libs: [
+        "libbinder_ndk",
+        "liblog",
+        "libnativewindow",
+    ],
+    whole_static_libs: [
+        "android.hardware.biometrics.face-service.lib",
+    ],
 }
 
 sysprop_library {
     name: "android.hardware.biometrics.face.VirtualProps",
     srcs: ["face.sysprop"],
-    property_owner: "Vendor",
-    vendor: true,
+    property_owner: "Platform",
+    vendor_available: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.hardware.biometrics.face.virtual",
+    ],
 }
 
 cc_test {
@@ -66,6 +106,7 @@
         "tests/FakeFaceEngineTest.cpp",
         "FakeFaceEngine.cpp",
         "FakeLockoutTracker.cpp",
+        "FaceConfig.cpp",
     ],
     shared_libs: [
         "libbase",
@@ -81,6 +122,8 @@
         "android.hardware.biometrics.common-V4-ndk",
         "android.hardware.keymaster-V4-ndk",
         "android.hardware.biometrics.common.util",
+        "android.hardware.biometrics.common.config",
+        "android.hardware.biometrics.common.thread",
     ],
     vendor: true,
     test_suites: ["general-tests"],
@@ -92,6 +135,7 @@
     srcs: [
         "tests/FakeLockoutTrackerTest.cpp",
         "FakeLockoutTracker.cpp",
+        "FaceConfig.cpp",
     ],
     shared_libs: [
         "libbase",
@@ -107,8 +151,45 @@
         "android.hardware.biometrics.common-V4-ndk",
         "android.hardware.keymaster-V4-ndk",
         "android.hardware.biometrics.common.util",
+        "android.hardware.biometrics.common.config",
+        "android.hardware.biometrics.common.thread",
     ],
     vendor: true,
     test_suites: ["general-tests"],
     require_root: true,
 }
+
+cc_test {
+    name: "android.hardware.biometrics.face.VirtualHalTest",
+    srcs: [
+        "tests/VirtualHalTest.cpp",
+        "FakeLockoutTracker.cpp",
+        "Face.cpp",
+        "FakeFaceEngine.cpp",
+        "Session.cpp",
+        "VirtualHal.cpp",
+        "FaceConfig.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "libnativewindow",
+        "liblog",
+    ],
+    include_dirs: [
+        "frameworks/native/aidl/gui",
+    ],
+    static_libs: [
+        "android.hardware.biometrics.common-V4-ndk",
+        "android.hardware.biometrics.common.config",
+        "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",
+        "android.hardware.biometrics.face.virtualhal-ndk",
+    ],
+    test_suites: ["general-tests"],
+    require_root: true,
+}
diff --git a/biometrics/face/aidl/default/Face.cpp b/biometrics/face/aidl/default/Face.cpp
index 5ae0df6..1543007 100644
--- a/biometrics/face/aidl/default/Face.cpp
+++ b/biometrics/face/aidl/default/Face.cpp
@@ -34,9 +34,7 @@
 namespace aidl::android::hardware::biometrics::face {
 
 const int kSensorId = 4;
-const common::SensorStrength kSensorStrength = FakeFaceEngine::GetSensorStrength();
 const int kMaxEnrollmentsPerUser = 5;
-const FaceSensorType kSensorType = FakeFaceEngine::GetSensorType();
 const bool kHalControlsPreview = true;
 const std::string kHwComponentId = "faceSensor";
 const std::string kHardwareVersion = "vendor/model/revision";
@@ -62,13 +60,13 @@
 
     common::CommonProps commonProps;
     commonProps.sensorId = kSensorId;
-    commonProps.sensorStrength = kSensorStrength;
+    commonProps.sensorStrength = FakeFaceEngine::GetSensorStrength();
     commonProps.maxEnrollmentsPerUser = kMaxEnrollmentsPerUser;
     commonProps.componentInfo = {std::move(hw_component_info), std::move(sw_component_info)};
 
     SensorProps props;
     props.commonProps = std::move(commonProps);
-    props.sensorType = kSensorType;
+    props.sensorType = FakeFaceEngine::GetSensorType();
     props.halControlsPreview = kHalControlsPreview;
     props.enrollPreviewWidth = 1080;
     props.enrollPreviewHeight = 1920;
@@ -141,6 +139,30 @@
     return STATUS_OK;
 }
 
+const char* Face::type2String(FaceSensorType type) {
+    switch (type) {
+        case FaceSensorType::RGB:
+            return "rgb";
+        case FaceSensorType::IR:
+            return "ir";
+        default:
+            return "unknown";
+    }
+}
+
+const char* Face::strength2String(common::SensorStrength strength) {
+    switch (strength) {
+        case common::SensorStrength::STRONG:
+            return "STRONG";
+        case common::SensorStrength::WEAK:
+            return "WEAK";
+        case common::SensorStrength::CONVENIENCE:
+            return "CONVENIENCE";
+        default:
+            return "unknown";
+    }
+}
+
 void Face::onHelp(int fd) {
     dprintf(fd, "Virtual Face HAL commands:\n");
     dprintf(fd, "         help: print this help\n");
@@ -167,7 +189,6 @@
     RESET_CONFIG_O(lockout);
     RESET_CONFIG_O(operation_authenticate_fails);
     RESET_CONFIG_O(operation_detect_interaction_fails);
-    RESET_CONFIG_O(operation_enroll_fails);
     RESET_CONFIG_V(operation_authenticate_latency);
     RESET_CONFIG_V(operation_detect_interaction_latency);
     RESET_CONFIG_V(operation_enroll_latency);
diff --git a/biometrics/face/aidl/default/Face.h b/biometrics/face/aidl/default/Face.h
index 93fddb0..dbe6341 100644
--- a/biometrics/face/aidl/default/Face.h
+++ b/biometrics/face/aidl/default/Face.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <aidl/android/hardware/biometrics/face/BnFace.h>
+#include "FaceConfig.h"
 #include "Session.h"
 
 namespace aidl::android::hardware::biometrics::face {
@@ -33,9 +34,20 @@
     binder_status_t dump(int fd, const char** args, uint32_t numArgs);
     binder_status_t handleShellCommand(int in, int out, int err, const char** argv, uint32_t argc);
 
+    static FaceConfig& cfg() {
+        static FaceConfig* cfg = nullptr;
+        if (cfg == nullptr) {
+            cfg = new FaceConfig();
+            cfg->init();
+        }
+        return *cfg;
+    }
+    void resetConfigToDefault();
+    static const char* type2String(FaceSensorType type);
+    static const char* strength2String(common::SensorStrength strength);
+
   private:
     std::shared_ptr<Session> mSession;
-    void resetConfigToDefault();
     void onHelp(int);
 };
 
diff --git a/biometrics/face/aidl/default/FaceConfig.cpp b/biometrics/face/aidl/default/FaceConfig.cpp
new file mode 100644
index 0000000..a91d7cc
--- /dev/null
+++ b/biometrics/face/aidl/default/FaceConfig.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "FaceConfig"
+
+#include "FaceConfig.h"
+
+#include <android-base/logging.h>
+
+#include <face.sysprop.h>
+
+using namespace ::android::face::virt;
+
+namespace aidl::android::hardware::biometrics::face {
+
+// Wrapper to system property access functions
+#define CREATE_GETTER_SETTER_WRAPPER(_NAME_, _T_)           \
+    ConfigValue _NAME_##Getter() {                          \
+        return FaceHalProperties::_NAME_();                 \
+    }                                                       \
+    bool _NAME_##Setter(const ConfigValue& v) {             \
+        return FaceHalProperties::_NAME_(std::get<_T_>(v)); \
+    }
+
+CREATE_GETTER_SETTER_WRAPPER(type, OptString)
+CREATE_GETTER_SETTER_WRAPPER(enrollments, OptIntVec)
+CREATE_GETTER_SETTER_WRAPPER(enrollment_hit, OptInt32)
+CREATE_GETTER_SETTER_WRAPPER(next_enrollment, OptString)
+CREATE_GETTER_SETTER_WRAPPER(authenticator_id, OptInt64)
+CREATE_GETTER_SETTER_WRAPPER(challenge, OptInt64)
+CREATE_GETTER_SETTER_WRAPPER(strength, OptString)
+CREATE_GETTER_SETTER_WRAPPER(operation_authenticate_fails, OptBool)
+CREATE_GETTER_SETTER_WRAPPER(operation_authenticate_latency, OptIntVec)
+CREATE_GETTER_SETTER_WRAPPER(operation_authenticate_duration, OptInt32)
+CREATE_GETTER_SETTER_WRAPPER(operation_authenticate_error, OptInt32)
+CREATE_GETTER_SETTER_WRAPPER(operation_authenticate_acquired, OptString)
+CREATE_GETTER_SETTER_WRAPPER(operation_enroll_latency, OptIntVec)
+CREATE_GETTER_SETTER_WRAPPER(operation_detect_interaction_fails, OptBool)
+CREATE_GETTER_SETTER_WRAPPER(operation_detect_interaction_latency, OptIntVec)
+CREATE_GETTER_SETTER_WRAPPER(lockout, OptBool)
+CREATE_GETTER_SETTER_WRAPPER(lockout_enable, OptBool)
+CREATE_GETTER_SETTER_WRAPPER(lockout_timed_enable, OptBool)
+CREATE_GETTER_SETTER_WRAPPER(lockout_timed_threshold, OptInt32)
+CREATE_GETTER_SETTER_WRAPPER(lockout_timed_duration, OptInt32)
+CREATE_GETTER_SETTER_WRAPPER(lockout_permanent_threshold, OptInt32)
+CREATE_GETTER_SETTER_WRAPPER(features, OptIntVec)
+
+// Name, Getter, Setter, Parser and default value
+#define NGS(_NAME_) #_NAME_, _NAME_##Getter, _NAME_##Setter
+static Config::Data configData[] = {
+        {NGS(type), &Config::parseString, "rgb"},
+        {NGS(enrollments), &Config::parseIntVec, ""},
+        {NGS(enrollment_hit), &Config::parseInt32, "0"},
+        {NGS(next_enrollment), &Config::parseString,
+         "1:1000-[21,7,1,1103],1500-[1108,1],2000-[1113,1],2500-[1118,1]:true"},
+        {NGS(authenticator_id), &Config::parseInt64, "0"},
+        {NGS(challenge), &Config::parseInt64, ""},
+        {NGS(strength), &Config::parseString, "strong"},
+        {NGS(operation_authenticate_fails), &Config::parseBool, "false"},
+        {NGS(operation_authenticate_latency), &Config::parseIntVec, ""},
+        {NGS(operation_authenticate_duration), &Config::parseInt32, "500"},
+        {NGS(operation_authenticate_error), &Config::parseInt32, "0"},
+        {NGS(operation_authenticate_acquired), &Config::parseString, ""},
+        {NGS(operation_enroll_latency), &Config::parseIntVec, ""},
+        {NGS(operation_detect_interaction_latency), &Config::parseIntVec, ""},
+        {NGS(operation_detect_interaction_fails), &Config::parseBool, "false"},
+        {NGS(lockout), &Config::parseBool, "false"},
+        {NGS(lockout_enable), &Config::parseBool, "false"},
+        {NGS(lockout_timed_enable), &Config::parseBool, "false"},
+        {NGS(lockout_timed_threshold), &Config::parseInt32, "3"},
+        {NGS(lockout_timed_duration), &Config::parseInt32, "10000"},
+        {NGS(lockout_permanent_threshold), &Config::parseInt32, "5"},
+        {NGS(features), &Config::parseIntVec, ""}};
+
+Config::Data* FaceConfig::getConfigData(int* size) {
+    *size = sizeof(configData) / sizeof(configData[0]);
+    return configData;
+}
+
+}  // namespace aidl::android::hardware::biometrics::face
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/AcquiredInfoAndVendorCode.aidl b/biometrics/face/aidl/default/FaceConfig.h
similarity index 62%
copy from biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/AcquiredInfoAndVendorCode.aidl
copy to biometrics/face/aidl/default/FaceConfig.h
index c7be950..64b62e0 100644
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/AcquiredInfoAndVendorCode.aidl
+++ b/biometrics/face/aidl/default/FaceConfig.h
@@ -14,22 +14,14 @@
  * limitations under the License.
  */
 
-package android.hardware.biometrics.fingerprint;
+#pragma once
 
-import android.hardware.biometrics.fingerprint.AcquiredInfo;
+#include "config/Config.h"
 
-/**
- * @hide
- */
-@VintfStability
-union AcquiredInfoAndVendorCode {
-    /**
-     * Acquired info as specified in AcqauiredInfo.aidl
-     */
-    AcquiredInfo acquiredInfo = AcquiredInfo.UNKNOWN;
+namespace aidl::android::hardware::biometrics::face {
 
-    /**
-     * Vendor specific code
-     */
-    int vendorCode;
-}
+class FaceConfig : public Config {
+    Config::Data* getConfigData(int* size) override;
+};
+
+}  // namespace aidl::android::hardware::biometrics::face
diff --git a/biometrics/face/aidl/default/FakeFaceEngine.cpp b/biometrics/face/aidl/default/FakeFaceEngine.cpp
index bf75874..70d9f2d 100644
--- a/biometrics/face/aidl/default/FakeFaceEngine.cpp
+++ b/biometrics/face/aidl/default/FakeFaceEngine.cpp
@@ -23,6 +23,7 @@
 
 #include <face.sysprop.h>
 
+#include "Face.h"
 #include "util/CancellationSignal.h"
 #include "util/Util.h"
 
@@ -31,23 +32,23 @@
 namespace aidl::android::hardware::biometrics::face {
 
 FaceSensorType FakeFaceEngine::GetSensorType() {
-    std::string type = FaceHalProperties::type().value_or("");
+    std::string type = Face::cfg().get<std::string>("type");
     if (type == "IR") {
         return FaceSensorType::IR;
     } else {
-        FaceHalProperties::type("RGB");
+        Face::cfg().set<std::string>("type", "RGB");
         return FaceSensorType::RGB;
     }
 }
 
 common::SensorStrength FakeFaceEngine::GetSensorStrength() {
-    std::string strength = FaceHalProperties::strength().value_or("");
+    std::string strength = Face::cfg().get<std::string>("strength");
     if (strength == "convenience") {
         return common::SensorStrength::CONVENIENCE;
     } else if (strength == "weak") {
         return common::SensorStrength::WEAK;
     } else {
-        FaceHalProperties::strength("strong");
+        // Face::cfg().set<std::string>("strength", "strong");
         return common::SensorStrength::STRONG;
     }
 }
@@ -56,13 +57,13 @@
     BEGIN_OP(0);
     std::uniform_int_distribution<int64_t> dist;
     auto challenge = dist(mRandom);
-    FaceHalProperties::challenge(challenge);
+    Face::cfg().set<int64_t>("challenge", challenge);
     cb->onChallengeGenerated(challenge);
 }
 
 void FakeFaceEngine::revokeChallengeImpl(ISessionCallback* cb, int64_t challenge) {
     BEGIN_OP(0);
-    FaceHalProperties::challenge({});
+    Face::cfg().set<int64_t>("challenge", 0);
     cb->onChallengeRevoked(challenge);
 }
 void FakeFaceEngine::getEnrollmentConfigImpl(ISessionCallback* /*cb*/,
@@ -71,7 +72,7 @@
                                 EnrollmentType /*enrollmentType*/,
                                 const std::vector<Feature>& /*features*/,
                                 const std::future<void>& cancel) {
-    BEGIN_OP(getLatency(FaceHalProperties::operation_enroll_latency()));
+    BEGIN_OP(getLatency(Face::cfg().getopt<OptIntVec>("operation_enroll_latency")));
 
     // Do proper HAT verification in the real implementation.
     if (hat.mac.empty()) {
@@ -80,18 +81,19 @@
         return;
     }
 
-    // Format: <id>:<progress_ms-[acquiredInfo,...],...:<success>
-    // ------:-----------------------------------------:--------------
-    //          |           |                              |--->enrollment success (true/false)
-    //          |           |--> progress_steps
+    // Format:
+    //    <id>:<progress_ms-[acquiredInfo,...],...:<success>
+    //    -------:--------------------------------------------------:--------------
+    //          |           |                                                   |--->enrollment
+    //          success (true/false) |           |--> progress_steps
     //          |
     //          |-->enrollment id
     //
     //
-    //   progress_steps
+    //   progress_steps:
     //        <progress_duration>-[acquiredInfo,...]+
     //        ----------------------------  ---------------------
-    //                 |                            |-> sequence of acquiredInfo code
+    //                 |                              |-> sequence of acquiredInfo code
     //                 | --> time duration of the step in ms
     //
     //        E.g.   1:2000-[21,1108,5,6,1],1000-[1113,4,1]:true
@@ -101,7 +103,7 @@
     //
     std::string defaultNextEnrollment =
             "1:1000-[21,7,1,1103],1500-[1108,1],2000-[1113,1],2500-[1118,1]:true";
-    auto nextEnroll = FaceHalProperties::next_enrollment().value_or(defaultNextEnrollment);
+    auto nextEnroll = Face::cfg().get<std::string>("next_enrollment");
     auto parts = Util::split(nextEnroll, ":");
     if (parts.size() != 3) {
         LOG(ERROR) << "Fail: invalid next_enrollment:" << nextEnroll;
@@ -137,19 +139,19 @@
 
         if (left == 0 && !IS_TRUE(parts[2])) {  // end and failed
             LOG(ERROR) << "Fail: requested by caller: " << nextEnroll;
-            FaceHalProperties::next_enrollment({});
+            Face::cfg().setopt<OptString>("next_enrollment", std::nullopt);
             cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
         } else {  // progress and update props if last time
             LOG(INFO) << "onEnroll: " << enrollmentId << " left: " << left;
             if (left == 0) {
-                auto enrollments = FaceHalProperties::enrollments();
+                auto enrollments = Face::cfg().getopt<OptIntVec>("enrollments");
                 enrollments.emplace_back(enrollmentId);
-                FaceHalProperties::enrollments(enrollments);
-                FaceHalProperties::next_enrollment({});
+                Face::cfg().setopt<OptIntVec>("enrollments", enrollments);
+                Face::cfg().setopt<OptString>("next_enrollment", std::nullopt);
                 // change authenticatorId after new enrollment
-                auto id = FaceHalProperties::authenticator_id().value_or(0);
+                auto id = Face::cfg().get<std::int64_t>("authenticator_id");
                 auto newId = id + 1;
-                FaceHalProperties::authenticator_id(newId);
+                Face::cfg().set<std::int64_t>("authenticator_id", newId);
                 LOG(INFO) << "Enrolled: " << enrollmentId;
             }
             cb->onEnrollmentProgress(enrollmentId, left);
@@ -159,10 +161,12 @@
 
 void FakeFaceEngine::authenticateImpl(ISessionCallback* cb, int64_t /*operationId*/,
                                       const std::future<void>& cancel) {
-    BEGIN_OP(getLatency(FaceHalProperties::operation_authenticate_latency()));
+    BEGIN_OP(getLatency(Face::cfg().getopt<OptIntVec>("operation_authenticate_latency")));
 
-    auto id = FaceHalProperties::enrollment_hit().value_or(0);
-    auto enrolls = FaceHalProperties::enrollments();
+    // SLEEP_MS(3000);  //emulate hw HAL
+
+    auto id = Face::cfg().get<std::int32_t>("enrollment_hit");
+    auto enrolls = Face::cfg().getopt<OptIntVec>("enrollments");
     auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();
 
     auto vec2str = [](std::vector<AcquiredInfo> va) {
@@ -192,10 +196,12 @@
     }
 
     int64_t now = Util::getSystemNanoTime();
-    int64_t duration =
-            FaceHalProperties::operation_authenticate_duration().value_or(defaultAuthDuration);
-    auto acquired =
-            FaceHalProperties::operation_authenticate_acquired().value_or(defaultAcquiredInfo);
+    int64_t duration = Face::cfg().get<std::int32_t>("operation_authenticate_duration");
+    auto acquired = Face::cfg().get<std::string>("operation_authenticate_acquired");
+    if (acquired.empty()) {
+        Face::cfg().set<std::string>("operation_authenticate_acquired", defaultAcquiredInfo);
+        acquired = defaultAcquiredInfo;
+    }
     auto acquiredInfos = Util::parseIntSequence(acquired);
     int N = acquiredInfos.size();
 
@@ -211,21 +217,21 @@
 
     int i = 0;
     do {
-        if (FaceHalProperties::lockout().value_or(false)) {
+        if (Face::cfg().get<bool>("lockout")) {
             LOG(ERROR) << "Fail: lockout";
             cb->onLockoutPermanent();
             cb->onError(Error::HW_UNAVAILABLE, 0 /* vendorError */);
             return;
         }
 
-        if (FaceHalProperties::operation_authenticate_fails().value_or(false)) {
+        if (Face::cfg().get<bool>("operation_authenticate_fails")) {
             LOG(ERROR) << "Fail: operation_authenticate_fails";
             mLockoutTracker.addFailedAttempt(cb);
             cb->onAuthenticationFailed();
             return;
         }
 
-        auto err = FaceHalProperties::operation_authenticate_error().value_or(0);
+        auto err = Face::cfg().get<std::int32_t>("operation_authenticate_error");
         if (err != 0) {
             LOG(ERROR) << "Fail: operation_authenticate_error";
             auto ec = convertError(err);
@@ -249,6 +255,15 @@
             LOG(INFO) << "AcquiredInfo:" << i << ": (" << (int)ac.first << "," << (int)ac.second
                       << ")";
             i++;
+
+            // the captured face id may change during authentication period
+            auto idnew = Face::cfg().get<std::int32_t>("enrollment_hit");
+            if (id != idnew) {
+                isEnrolled = std::find(enrolls.begin(), enrolls.end(), idnew) != enrolls.end();
+                LOG(INFO) << "enrollment_hit changed from " << id << " to " << idnew;
+                id = idnew;
+                break;
+            }
         }
 
         SLEEP_MS(duration / N);
@@ -292,9 +307,9 @@
 }
 
 void FakeFaceEngine::detectInteractionImpl(ISessionCallback* cb, const std::future<void>& cancel) {
-    BEGIN_OP(getLatency(FaceHalProperties::operation_detect_interaction_latency()));
+    BEGIN_OP(getLatency(Face::cfg().getopt<OptIntVec>("operation_detect_interaction_latency")));
 
-    if (FaceHalProperties::operation_detect_interaction_fails().value_or(false)) {
+    if (Face::cfg().get<bool>("operation_detect_interaction_fails")) {
         LOG(ERROR) << "Fail: operation_detect_interaction_fails";
         cb->onError(Error::VENDOR, 0 /* vendorError */);
         return;
@@ -306,8 +321,8 @@
         return;
     }
 
-    auto id = FaceHalProperties::enrollment_hit().value_or(0);
-    auto enrolls = FaceHalProperties::enrollments();
+    auto id = Face::cfg().get<std::int32_t>("enrollment_hit");
+    auto enrolls = Face::cfg().getopt<OptIntVec>("enrollments");
     auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();
     if (id <= 0 || !isEnrolled) {
         LOG(ERROR) << "Fail: not enrolled";
@@ -321,7 +336,7 @@
 void FakeFaceEngine::enumerateEnrollmentsImpl(ISessionCallback* cb) {
     BEGIN_OP(0);
     std::vector<int32_t> enrollments;
-    for (const auto& enrollmentId : FaceHalProperties::enrollments()) {
+    for (const auto& enrollmentId : Face::cfg().getopt<OptIntVec>("enrollments")) {
         if (enrollmentId) {
             enrollments.push_back(*enrollmentId);
         }
@@ -334,20 +349,20 @@
     BEGIN_OP(0);
 
     std::vector<std::optional<int32_t>> newEnrollments;
-    for (const auto& enrollment : FaceHalProperties::enrollments()) {
+    for (const auto& enrollment : Face::cfg().getopt<OptIntVec>("enrollments")) {
         auto id = enrollment.value_or(0);
         if (std::find(enrollmentIds.begin(), enrollmentIds.end(), id) == enrollmentIds.end()) {
             newEnrollments.emplace_back(id);
         }
     }
-    FaceHalProperties::enrollments(newEnrollments);
+    Face::cfg().setopt<OptIntVec>("enrollments", newEnrollments);
     cb->onEnrollmentsRemoved(enrollmentIds);
 }
 
 void FakeFaceEngine::getFeaturesImpl(ISessionCallback* cb) {
     BEGIN_OP(0);
 
-    if (FaceHalProperties::enrollments().empty()) {
+    if (Face::cfg().getopt<OptIntVec>("enrollments").empty()) {
         cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
         return;
     }
@@ -365,7 +380,7 @@
                                     Feature feature, bool enabled) {
     BEGIN_OP(0);
 
-    if (FaceHalProperties::enrollments().empty()) {
+    if (Face::cfg().getopt<OptIntVec>("enrollments").empty()) {
         LOG(ERROR) << "Unable to set feature, enrollments are empty";
         cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
         return;
@@ -377,7 +392,7 @@
         return;
     }
 
-    auto features = FaceHalProperties::features();
+    auto features = Face::cfg().getopt<OptIntVec>("features");
 
     auto itr = std::find_if(features.begin(), features.end(), [feature](const auto& theFeature) {
         return *theFeature == (int)feature;
@@ -389,7 +404,7 @@
         features.push_back((int)feature);
     }
 
-    FaceHalProperties::features(features);
+    Face::cfg().setopt<OptIntVec>("features", features);
     cb->onFeatureSet(feature);
 }
 
@@ -399,22 +414,22 @@
     if (GetSensorStrength() != common::SensorStrength::STRONG) {
         cb->onAuthenticatorIdRetrieved(0);
     } else {
-        cb->onAuthenticatorIdRetrieved(FaceHalProperties::authenticator_id().value_or(0));
+        cb->onAuthenticatorIdRetrieved(Face::cfg().get<std::int64_t>("authenticator_id"));
     }
 }
 
 void FakeFaceEngine::invalidateAuthenticatorIdImpl(ISessionCallback* cb) {
     BEGIN_OP(0);
-    int64_t authenticatorId = FaceHalProperties::authenticator_id().value_or(0);
+    int64_t authenticatorId = Face::cfg().get<std::int64_t>("authenticator_id");
     int64_t newId = authenticatorId + 1;
-    FaceHalProperties::authenticator_id(newId);
+    Face::cfg().set<std::int64_t>("authenticator_id", newId);
     cb->onAuthenticatorIdInvalidated(newId);
 }
 
 void FakeFaceEngine::resetLockoutImpl(ISessionCallback* cb,
                                       const keymaster::HardwareAuthToken& /*hat*/) {
     BEGIN_OP(0);
-    FaceHalProperties::lockout(false);
+    Face::cfg().set<bool>("lockout", false);
     mLockoutTracker.reset();
     cb->onLockoutCleared();
 }
diff --git a/biometrics/face/aidl/default/FakeLockoutTracker.cpp b/biometrics/face/aidl/default/FakeLockoutTracker.cpp
index 70bf08e..35d7c28 100644
--- a/biometrics/face/aidl/default/FakeLockoutTracker.cpp
+++ b/biometrics/face/aidl/default/FakeLockoutTracker.cpp
@@ -19,6 +19,7 @@
 #include "FakeLockoutTracker.h"
 #include <android-base/logging.h>
 #include <face.sysprop.h>
+#include "Face.h"
 #include "util/Util.h"
 
 using namespace ::android::face::virt;
@@ -36,15 +37,15 @@
 }
 
 void FakeLockoutTracker::addFailedAttempt(ISessionCallback* cb) {
-    bool lockoutEnabled = FaceHalProperties::lockout_enable().value_or(false);
-    bool timedLockoutenabled = FaceHalProperties::lockout_timed_enable().value_or(false);
+    bool lockoutEnabled = Face::cfg().get<bool>("lockout_enable");
+    bool timedLockoutenabled = Face::cfg().get<bool>("lockout_timed_enable");
     if (lockoutEnabled) {
         mFailedCount++;
         mTimedFailedCount++;
         mLastFailedTime = Util::getSystemNanoTime();
-        int32_t lockoutTimedThreshold = FaceHalProperties::lockout_timed_threshold().value_or(3);
+        int32_t lockoutTimedThreshold = Face::cfg().get<std::int32_t>("lockout_timed_threshold");
         int32_t lockoutPermanetThreshold =
-                FaceHalProperties::lockout_permanent_threshold().value_or(5);
+                Face::cfg().get<std::int32_t>("lockout_permanent_threshold");
         if (mFailedCount >= lockoutPermanetThreshold) {
             mCurrentMode = LockoutMode::kPermanent;
             LOG(ERROR) << "FakeLockoutTracker: lockoutPermanent";
@@ -68,7 +69,7 @@
 }
 
 int32_t FakeLockoutTracker::getTimedLockoutDuration() {
-    return FaceHalProperties::lockout_timed_duration().value_or(10 * 1000);
+    return Face::cfg().get<std::int32_t>("lockout_timed_duration");
 }
 
 int64_t FakeLockoutTracker::getLockoutTimeLeft() {
diff --git a/biometrics/face/aidl/default/VirtualHal.cpp b/biometrics/face/aidl/default/VirtualHal.cpp
new file mode 100644
index 0000000..52ac23b
--- /dev/null
+++ b/biometrics/face/aidl/default/VirtualHal.cpp
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unordered_map>
+
+#include "VirtualHal.h"
+
+#include <android-base/logging.h>
+
+#include "util/CancellationSignal.h"
+
+#undef LOG_TAG
+#define LOG_TAG "FaceVirtualHalAidl"
+
+namespace aidl::android::hardware::biometrics::face {
+using AcquiredInfoAndVendorCode = virtualhal::AcquiredInfoAndVendorCode;
+using Tag = AcquiredInfoAndVendorCode::Tag;
+
+::ndk::ScopedAStatus VirtualHal::setEnrollments(const std::vector<int32_t>& enrollments) {
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().setopt<OptIntVec>("enrollments", intVec2OptIntVec(enrollments));
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setEnrollmentHit(int32_t enrollment_hit) {
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().set<std::int32_t>("enrollment_hit", enrollment_hit);
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setNextEnrollment(
+        const ::aidl::android::hardware::biometrics::face::NextEnrollment& next_enrollment) {
+    Face::cfg().sourcedFromAidl();
+    std::ostringstream os;
+    os << next_enrollment.id << ":";
+
+    int stepSize = next_enrollment.progressSteps.size();
+    for (int i = 0; i < stepSize; i++) {
+        auto& step = next_enrollment.progressSteps[i];
+        os << step.durationMs;
+        int acSize = step.acquiredInfoAndVendorCodes.size();
+        for (int j = 0; j < acSize; j++) {
+            if (j == 0) os << "-[";
+            auto& acquiredInfoAndVendorCode = step.acquiredInfoAndVendorCodes[j];
+            if (acquiredInfoAndVendorCode.getTag() == AcquiredInfoAndVendorCode::vendorCode)
+                os << acquiredInfoAndVendorCode.get<Tag::vendorCode>();
+            else if (acquiredInfoAndVendorCode.getTag() == AcquiredInfoAndVendorCode::acquiredInfo)
+                os << (int)acquiredInfoAndVendorCode.get<Tag::acquiredInfo>();
+            else
+                LOG(FATAL) << "ERROR: wrong AcquiredInfoAndVendorCode union tag";
+            if (j == acSize - 1)
+                os << "]";
+            else
+                os << ",";
+        }
+        if (i == stepSize - 1)
+            os << ":";
+        else
+            os << ",";
+    }
+
+    os << (next_enrollment.result ? "true" : "false");
+    Face::cfg().set<std::string>("next_enrollment", os.str());
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setAuthenticatorId(int64_t in_id) {
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().set<int64_t>("authenticator_id", in_id);
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setChallenge(int64_t in_challenge) {
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().set<int64_t>("challenge", in_challenge);
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setOperationAuthenticateFails(bool in_fail) {
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().set<bool>("operation_authenticate_fails", in_fail);
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setOperationAuthenticateLatency(
+        const std::vector<int32_t>& in_latency) {
+    ndk::ScopedAStatus status = sanityCheckLatency(in_latency);
+    if (!status.isOk()) {
+        return status;
+    }
+
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().setopt<OptIntVec>("operation_authenticate_latency", intVec2OptIntVec(in_latency));
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setOperationAuthenticateDuration(int32_t in_duration) {
+    if (in_duration < 0) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IVirtualHal::STATUS_INVALID_PARAMETER, "Error: duration can not be negative"));
+    }
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().set<int32_t>("operation_authenticate_duration", in_duration);
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setOperationAuthenticateError(int32_t in_error) {
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().set<int32_t>("operation_authenticate_error", in_error);
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setOperationAuthenticateAcquired(
+        const std::vector<AcquiredInfoAndVendorCode>& in_acquired) {
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().setopt<OptIntVec>("operation_authenticate_acquired",
+                                  acquiredInfoVec2OptIntVec(in_acquired));
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setOperationEnrollLatency(const std::vector<int32_t>& in_latency) {
+    ndk::ScopedAStatus status = sanityCheckLatency(in_latency);
+    if (!status.isOk()) {
+        return status;
+    }
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().setopt<OptIntVec>("operation_enroll_latency", intVec2OptIntVec(in_latency));
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setOperationDetectInteractionLatency(
+        const std::vector<int32_t>& in_latency) {
+    ndk::ScopedAStatus status = sanityCheckLatency(in_latency);
+    if (!status.isOk()) {
+        return status;
+    }
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().setopt<OptIntVec>("operation_detect_interact_latency",
+                                  intVec2OptIntVec(in_latency));
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setOperationDetectInteractionFails(bool in_fails) {
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().set<bool>("operation_detect_interaction_fails", in_fails);
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setLockout(bool in_lockout) {
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().set<bool>("lockout", in_lockout);
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setLockoutEnable(bool in_enable) {
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().set<bool>("lockout_enable", in_enable);
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setLockoutTimedEnable(bool in_enable) {
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().set<bool>("lockout_timed_enable", in_enable);
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setLockoutTimedThreshold(int32_t in_threshold) {
+    if (in_threshold < 0) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IVirtualHal::STATUS_INVALID_PARAMETER, "Error: threshold can not be negative"));
+    }
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().set<int32_t>("lockout_timed_threshold", in_threshold);
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setLockoutTimedDuration(int32_t in_duration) {
+    if (in_duration < 0) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IVirtualHal::STATUS_INVALID_PARAMETER, "Error: duration can not be negative"));
+    }
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().set<int32_t>("lockout_timed_duration", in_duration);
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setLockoutPermanentThreshold(int32_t in_threshold) {
+    if (in_threshold < 0) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IVirtualHal::STATUS_INVALID_PARAMETER, "Error: threshold can not be negative"));
+    }
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().set<int32_t>("lockout_permanent_threshold", in_threshold);
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::resetConfigurations() {
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().init();
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setType(
+        ::aidl::android::hardware::biometrics::face::FaceSensorType in_type) {
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().set<std::string>("type", Face::type2String(in_type));
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::setSensorStrength(common::SensorStrength in_strength) {
+    Face::cfg().sourcedFromAidl();
+    Face::cfg().set<std::string>("strength", Face::strength2String(in_strength));
+    return ndk::ScopedAStatus::ok();
+}
+
+OptIntVec VirtualHal::intVec2OptIntVec(const std::vector<int32_t>& in_vec) {
+    OptIntVec optIntVec;
+    std::transform(in_vec.begin(), in_vec.end(), std::back_inserter(optIntVec),
+                   [](int value) { return std::optional<int>(value); });
+    return optIntVec;
+}
+
+OptIntVec VirtualHal::acquiredInfoVec2OptIntVec(
+        const std::vector<AcquiredInfoAndVendorCode>& in_vec) {
+    OptIntVec optIntVec;
+    std::transform(in_vec.begin(), in_vec.end(), std::back_inserter(optIntVec),
+                   [](AcquiredInfoAndVendorCode ac) {
+                       int value;
+                       if (ac.getTag() == AcquiredInfoAndVendorCode::acquiredInfo)
+                           value = (int)ac.get<Tag::acquiredInfo>();
+                       else if (ac.getTag() == AcquiredInfoAndVendorCode::vendorCode)
+                           value = ac.get<Tag::vendorCode>();
+                       else
+                           LOG(FATAL) << "ERROR: wrong AcquiredInfoAndVendorCode tag";
+                       return std::optional<int>(value);
+                   });
+    return optIntVec;
+}
+
+::ndk::ScopedAStatus VirtualHal::sanityCheckLatency(const std::vector<int32_t>& in_latency) {
+    if (in_latency.size() == 0 || in_latency.size() > 2) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IVirtualHal::STATUS_INVALID_PARAMETER,
+                "Error: input input array must contain 1 or 2 elements"));
+    }
+
+    for (auto x : in_latency) {
+        if (x < 0) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IVirtualHal::STATUS_INVALID_PARAMETER,
+                    "Error: input data must not be negative"));
+        }
+    }
+
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus VirtualHal::getFaceHal(std::shared_ptr<IFace>* pFace) {
+    *pFace = mFp;
+    return ndk::ScopedAStatus::ok();
+}
+}  // namespace aidl::android::hardware::biometrics::face
diff --git a/biometrics/face/aidl/default/VirtualHal.h b/biometrics/face/aidl/default/VirtualHal.h
new file mode 100644
index 0000000..f2ac552
--- /dev/null
+++ b/biometrics/face/aidl/default/VirtualHal.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/biometrics/face/virtualhal/BnVirtualHal.h>
+
+#include "Face.h"
+
+namespace aidl::android::hardware::biometrics::face {
+using namespace virtualhal;
+class VirtualHal : public BnVirtualHal {
+  public:
+    VirtualHal(std::shared_ptr<Face> fp) : mFp(fp) {}
+
+    ::ndk::ScopedAStatus setEnrollments(const std::vector<int32_t>& in_id) override;
+    ::ndk::ScopedAStatus setEnrollmentHit(int32_t in_hit_id) override;
+    ::ndk::ScopedAStatus setNextEnrollment(
+            const ::aidl::android::hardware::biometrics::face::NextEnrollment& in_next_enrollment)
+            override;
+    ::ndk::ScopedAStatus setAuthenticatorId(int64_t in_id) override;
+    ::ndk::ScopedAStatus setChallenge(int64_t in_challenge) override;
+    ::ndk::ScopedAStatus setOperationAuthenticateFails(bool in_fail) override;
+    ::ndk::ScopedAStatus setOperationAuthenticateLatency(
+            const std::vector<int32_t>& in_latency) override;
+    ::ndk::ScopedAStatus setOperationAuthenticateDuration(int32_t in_duration) override;
+    ::ndk::ScopedAStatus setOperationAuthenticateError(int32_t in_error) override;
+    ::ndk::ScopedAStatus setOperationAuthenticateAcquired(
+            const std::vector<AcquiredInfoAndVendorCode>& in_acquired) override;
+    ::ndk::ScopedAStatus setOperationEnrollLatency(const std::vector<int32_t>& in_latency) override;
+    ::ndk::ScopedAStatus setOperationDetectInteractionLatency(
+            const std::vector<int32_t>& in_latency) override;
+    ::ndk::ScopedAStatus setOperationDetectInteractionFails(bool in_fails) override;
+    ::ndk::ScopedAStatus setLockout(bool in_lockout) override;
+    ::ndk::ScopedAStatus setLockoutEnable(bool in_enable) override;
+    ::ndk::ScopedAStatus setLockoutTimedEnable(bool in_enable) override;
+    ::ndk::ScopedAStatus setLockoutTimedThreshold(int32_t in_threshold) override;
+    ::ndk::ScopedAStatus setLockoutTimedDuration(int32_t in_duration) override;
+    ::ndk::ScopedAStatus setLockoutPermanentThreshold(int32_t in_threshold) override;
+    ::ndk::ScopedAStatus resetConfigurations() override;
+    ::ndk::ScopedAStatus setType(
+            ::aidl::android::hardware::biometrics::face::FaceSensorType in_type) override;
+    ::ndk::ScopedAStatus setSensorStrength(common::SensorStrength in_strength) override;
+    ::ndk::ScopedAStatus getFaceHal(std::shared_ptr<IFace>* _aidl_return);
+
+  private:
+    OptIntVec intVec2OptIntVec(const std::vector<int32_t>& intVec);
+    OptIntVec acquiredInfoVec2OptIntVec(const std::vector<AcquiredInfoAndVendorCode>& intVec);
+    ::ndk::ScopedAStatus sanityCheckLatency(const std::vector<int32_t>& in_latency);
+    std::shared_ptr<Face> mFp;
+};
+
+}  // namespace aidl::android::hardware::biometrics::face
diff --git a/biometrics/face/aidl/default/apex/Android.bp b/biometrics/face/aidl/default/apex/Android.bp
index 86c4e12..c4632d4 100644
--- a/biometrics/face/aidl/default/apex/Android.bp
+++ b/biometrics/face/aidl/default/apex/Android.bp
@@ -23,7 +23,7 @@
     key: "com.android.hardware.key",
     certificate: ":com.android.hardware.certificate",
     updatable: false,
-    vendor: true,
+    system_ext_specific: true,
 
     binaries: [
         // hal
@@ -31,9 +31,7 @@
     ],
     prebuilts: [
         // init_rc
-        "face-example-apex.rc",
-        // vintf_fragment
-        "face-example-apex.xml",
+        "face-virtual-apex.rc",
     ],
 
     overrides: [
@@ -42,21 +40,7 @@
 }
 
 prebuilt_etc {
-    name: "face-example-apex.rc",
-    src: ":gen-face-example-apex.rc",
-    installable: false,
-}
-
-genrule {
-    name: "gen-face-example-apex.rc",
-    srcs: [":face-example.rc"],
-    out: ["face-example-apex.rc"],
-    cmd: "sed -e 's@/vendor/bin/@/apex/com.android.hardware.biometrics.face.virtual/bin/@' $(in) > $(out)",
-}
-
-prebuilt_etc {
-    name: "face-example-apex.xml",
-    src: ":face-example.xml",
-    sub_dir: "vintf",
+    name: "face-virtual-apex.rc",
+    src: ":face-virtual.rc",
     installable: false,
 }
diff --git a/biometrics/face/aidl/default/api/android.hardware.biometrics.face.VirtualProps-current.txt b/biometrics/face/aidl/default/api/android.hardware.biometrics.face.VirtualProps-current.txt
index e69de29..6ad579c 100644
--- a/biometrics/face/aidl/default/api/android.hardware.biometrics.face.VirtualProps-current.txt
+++ b/biometrics/face/aidl/default/api/android.hardware.biometrics.face.VirtualProps-current.txt
@@ -0,0 +1,133 @@
+props {
+  owner: Vendor
+  module: "android.face.virt.FaceHalProperties"
+  prop {
+    api_name: "authenticator_id"
+    type: Long
+    access: ReadWrite
+    prop_name: "vendor.face.virtual.authenticator_id"
+  }
+  prop {
+    api_name: "challenge"
+    type: Long
+    access: ReadWrite
+    prop_name: "vendor.face.virtual.challenge"
+  }
+  prop {
+    api_name: "enrollment_hit"
+    type: Integer
+    access: ReadWrite
+    prop_name: "vendor.face.virtual.enrollment_hit"
+  }
+  prop {
+    api_name: "enrollments"
+    type: IntegerList
+    access: ReadWrite
+    prop_name: "persist.vendor.face.virtual.enrollments"
+  }
+  prop {
+    api_name: "features"
+    type: IntegerList
+    access: ReadWrite
+    prop_name: "persist.vendor.face.virtual.features"
+  }
+  prop {
+    api_name: "lockout"
+    access: ReadWrite
+    prop_name: "vendor.face.virtual.lockout"
+  }
+  prop {
+    api_name: "lockout_enable"
+    access: ReadWrite
+    prop_name: "persist.vendor.face.virtual.lockout_enable"
+  }
+  prop {
+    api_name: "lockout_permanent_threshold"
+    type: Integer
+    access: ReadWrite
+    prop_name: "persist.vendor.face.virtual.lockout_permanent_threshold"
+  }
+  prop {
+    api_name: "lockout_timed_duration"
+    type: Integer
+    access: ReadWrite
+    prop_name: "persist.vendor.face.virtual.lockout_timed_duration"
+  }
+  prop {
+    api_name: "lockout_timed_enable"
+    access: ReadWrite
+    prop_name: "persist.vendor.face.virtual.lockout_timed_enable"
+  }
+  prop {
+    api_name: "lockout_timed_threshold"
+    type: Integer
+    access: ReadWrite
+    prop_name: "persist.vendor.face.virtual.lockout_timed_threshold"
+  }
+  prop {
+    api_name: "next_enrollment"
+    type: String
+    access: ReadWrite
+    prop_name: "vendor.face.virtual.next_enrollment"
+  }
+  prop {
+    api_name: "operation_authenticate_acquired"
+    type: String
+    access: ReadWrite
+    prop_name: "vendor.face.virtual.operation_authenticate_acquired"
+  }
+  prop {
+    api_name: "operation_authenticate_duration"
+    type: Integer
+    access: ReadWrite
+    prop_name: "vendor.face.virtual.operation_authenticate_duration"
+  }
+  prop {
+    api_name: "operation_authenticate_error"
+    type: Integer
+    access: ReadWrite
+    prop_name: "vendor.face.virtual.operation_authenticate_error"
+  }
+  prop {
+    api_name: "operation_authenticate_fails"
+    access: ReadWrite
+    prop_name: "vendor.face.virtual.operation_authenticate_fails"
+  }
+  prop {
+    api_name: "operation_authenticate_latency"
+    type: IntegerList
+    access: ReadWrite
+    prop_name: "vendor.face.virtual.operation_authenticate_latency"
+  }
+  prop {
+    api_name: "operation_detect_interaction_fails"
+    access: ReadWrite
+    prop_name: "vendor.face.virtual.operation_detect_interaction_fails"
+  }
+  prop {
+    api_name: "operation_detect_interaction_latency"
+    type: IntegerList
+    access: ReadWrite
+    prop_name: "vendor.face.virtual.operation_detect_interaction_latency"
+  }
+  prop {
+    api_name: "operation_enroll_latency"
+    type: IntegerList
+    access: ReadWrite
+    prop_name: "vendor.face.virtual.operation_enroll_latency"
+  }
+  prop {
+    api_name: "strength"
+    type: String
+    access: ReadWrite
+    prop_name: "persist.vendor.face.virtual.strength"
+    enum_values: "convenience|weak|strong"
+  }
+  prop {
+    api_name: "type"
+    type: String
+    access: ReadWrite
+    prop_name: "persist.vendor.face.virtual.type"
+    enum_values: "IR|RGB"
+  }
+}
diff --git a/biometrics/face/aidl/default/face-default.rc b/biometrics/face/aidl/default/face-default.rc
new file mode 100644
index 0000000..7ce4249
--- /dev/null
+++ b/biometrics/face/aidl/default/face-default.rc
@@ -0,0 +1,8 @@
+service vendor.face-default /vendor/bin/hw/android.hardware.biometrics.face-service.default default
+    class hal
+    user nobody
+    group nobody
+    interface aidl android.hardware.biometrics.face.IFace/default
+    oneshot
+    disabled
+
diff --git a/biometrics/face/aidl/default/face-example.xml b/biometrics/face/aidl/default/face-default.xml
similarity index 81%
rename from biometrics/face/aidl/default/face-example.xml
rename to biometrics/face/aidl/default/face-default.xml
index 2b39b3d..94569de 100644
--- a/biometrics/face/aidl/default/face-example.xml
+++ b/biometrics/face/aidl/default/face-default.xml
@@ -2,6 +2,6 @@
     <hal format="aidl">
         <name>android.hardware.biometrics.face</name>
         <version>4</version>
-        <fqname>IFace/virtual</fqname>
+        <fqname>IFace/default</fqname>
     </hal>
 </manifest>
diff --git a/biometrics/face/aidl/default/face-example.rc b/biometrics/face/aidl/default/face-example.rc
deleted file mode 100644
index b0d82c6..0000000
--- a/biometrics/face/aidl/default/face-example.rc
+++ /dev/null
@@ -1,8 +0,0 @@
-service vendor.face-example /vendor/bin/hw/android.hardware.biometrics.face-service.example
-    class hal
-    user nobody
-    group nobody
-    interface aidl android.hardware.biometrics.face.IFace/virtual
-    oneshot
-    disabled
-
diff --git a/biometrics/face/aidl/default/face-virtual.rc b/biometrics/face/aidl/default/face-virtual.rc
new file mode 100644
index 0000000..8fb0a7b
--- /dev/null
+++ b/biometrics/face/aidl/default/face-virtual.rc
@@ -0,0 +1,8 @@
+service face-virtual /apex/com.android.hardware.biometrics.face.virtual/bin/hw/android.hardware.biometrics.face-service.example virtual
+    class hal
+    user nobody
+    group nobody
+    interface aidl android.hardware.biometrics.face.virtualhal.IVirtualHal/virtual
+    oneshot
+    disabled
+
diff --git a/biometrics/face/aidl/default/face.sysprop b/biometrics/face/aidl/default/face.sysprop
index 997fd67..ec2b92b 100644
--- a/biometrics/face/aidl/default/face.sysprop
+++ b/biometrics/face/aidl/default/face.sysprop
@@ -7,7 +7,7 @@
 prop {
     prop_name: "persist.vendor.face.virtual.type"
     type: String
-    scope: Internal
+    scope: Public
     access: ReadWrite
     enum_values: "IR|RGB"
     api_name: "type"
@@ -17,7 +17,7 @@
 prop {
     prop_name: "persist.vendor.face.virtual.strength"
     type: String
-    scope: Internal
+    scope: Public
     access: ReadWrite
     enum_values: "convenience|weak|strong"
     api_name: "strength"
@@ -27,7 +27,7 @@
 prop {
     prop_name: "persist.vendor.face.virtual.enrollments"
     type: IntegerList
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "enrollments"
 }
@@ -36,7 +36,7 @@
 prop {
     prop_name: "persist.vendor.face.virtual.features"
     type: IntegerList
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "features"
 }
@@ -46,7 +46,7 @@
 prop {
     prop_name: "vendor.face.virtual.enrollment_hit"
     type: Integer
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "enrollment_hit"
 }
@@ -60,7 +60,7 @@
 prop {
     prop_name: "vendor.face.virtual.next_enrollment"
     type: String
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "next_enrollment"
 }
@@ -69,7 +69,7 @@
 prop {
     prop_name: "vendor.face.virtual.authenticator_id"
     type: Long
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "authenticator_id"
 }
@@ -78,7 +78,7 @@
 prop {
     prop_name: "vendor.face.virtual.challenge"
     type: Long
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "challenge"
 }
@@ -87,7 +87,7 @@
 prop {
     prop_name: "vendor.face.virtual.lockout"
     type: Boolean
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "lockout"
 }
@@ -96,7 +96,7 @@
 prop {
     prop_name: "vendor.face.virtual.operation_authenticate_fails"
     type: Boolean
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "operation_authenticate_fails"
 }
@@ -105,27 +105,18 @@
 prop {
     prop_name: "vendor.face.virtual.operation_detect_interaction_fails"
     type: Boolean
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "operation_detect_interaction_fails"
 }
 
-# force all enroll operations to fail
-prop {
-    prop_name: "vendor.face.virtual.operation_enroll_fails"
-    type: Boolean
-    scope: Internal
-    access: ReadWrite
-    api_name: "operation_enroll_fails"
-}
-
 # add a latency to authentication operations
 # Note that this latency is the initial authentication latency that occurs before
 # the HAL will send AcquiredInfo::START and AcquiredInfo::FIRST_FRAME_RECEIVED
 prop {
     prop_name: "vendor.face.virtual.operation_authenticate_latency"
     type: IntegerList
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "operation_authenticate_latency"
 }
@@ -134,7 +125,7 @@
 prop {
     prop_name: "vendor.face.virtual.operation_detect_interaction_latency"
     type: IntegerList
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "operation_detect_interaction_latency"
 }
@@ -143,7 +134,7 @@
 prop {
     prop_name: "vendor.face.virtual.operation_enroll_latency"
     type: IntegerList
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "operation_enroll_latency"
 }
@@ -153,7 +144,7 @@
 prop {
     prop_name: "vendor.face.virtual.operation_authenticate_duration"
     type: Integer
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "operation_authenticate_duration"
 }
@@ -162,7 +153,7 @@
 prop {
     prop_name: "vendor.face.virtual.operation_authenticate_error"
     type: Integer
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "operation_authenticate_error"
 }
@@ -171,7 +162,7 @@
 prop {
     prop_name: "vendor.face.virtual.operation_authenticate_acquired"
     type: String
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "operation_authenticate_acquired"
 }
@@ -180,7 +171,7 @@
 prop {
     prop_name: "persist.vendor.face.virtual.lockout_enable"
     type: Boolean
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "lockout_enable"
 }
@@ -189,7 +180,7 @@
 prop {
     prop_name: "persist.vendor.face.virtual.lockout_timed_enable"
     type: Boolean
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "lockout_timed_enable"
 }
@@ -198,7 +189,7 @@
 prop {
     prop_name: "persist.vendor.face.virtual.lockout_timed_threshold"
     type: Integer
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "lockout_timed_threshold"
 }
@@ -207,7 +198,7 @@
 prop {
     prop_name: "persist.vendor.face.virtual.lockout_timed_duration"
     type: Integer
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "lockout_timed_duration"
 }
@@ -216,7 +207,7 @@
 prop {
     prop_name: "persist.vendor.face.virtual.lockout_permanent_threshold"
     type: Integer
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "lockout_permanent_threshold"
 }
diff --git a/biometrics/face/aidl/default/main.cpp b/biometrics/face/aidl/default/main.cpp
index 38e1c63..75a4479 100644
--- a/biometrics/face/aidl/default/main.cpp
+++ b/biometrics/face/aidl/default/main.cpp
@@ -14,25 +14,49 @@
  * limitations under the License.
  */
 
+#undef LOG_TAG
+#define LOG_TAG "FaceVirtualHal"
+
 #include "Face.h"
+#include "VirtualHal.h"
 
 #include <android-base/logging.h>
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
 
 using aidl::android::hardware::biometrics::face::Face;
+using aidl::android::hardware::biometrics::face::VirtualHal;
 
-int main() {
-    LOG(INFO) << "Face HAL started";
+int main(int argc, char** argv) {
+    if (argc < 2) {
+        LOG(ERROR) << "Missing argument -> exiting, Valid arguments:[default|virtual]";
+        return EXIT_FAILURE;
+    }
+    LOG(INFO) << "Face HAL started: " << argv[1];
     ABinderProcess_setThreadPoolMaxThreadCount(0);
     std::shared_ptr<Face> hal = ndk::SharedRefBase::make<Face>();
+    std::shared_ptr<VirtualHal> hal_vhal = ndk::SharedRefBase::make<VirtualHal>(hal);
 
-    const std::string instance = std::string(Face::descriptor) + "/virtual";
-    binder_status_t status =
-            AServiceManager_registerLazyService(hal->asBinder().get(), instance.c_str());
-    CHECK_EQ(status, STATUS_OK);
+    if (strcmp(argv[1], "default") == 0) {
+        const std::string instance = std::string(Face::descriptor) + "/default";
+        auto binder = hal->asBinder();
+        binder_status_t status =
+                AServiceManager_registerLazyService(binder.get(), instance.c_str());
+        CHECK_EQ(status, STATUS_OK);
+        LOG(INFO) << "started IFace/default";
+    } else if (strcmp(argv[1], "virtual") == 0) {
+        const std::string instance = std::string(VirtualHal::descriptor) + "/virtual";
+        auto binder = hal_vhal->asBinder();
+        binder_status_t status =
+                AServiceManager_registerLazyService(binder.get(), instance.c_str());
+        CHECK_EQ(status, STATUS_OK);
+        LOG(INFO) << "started IVirtualHal/virtual";
+    } else {
+        LOG(ERROR) << "Unexpected argument: " << argv[1];
+        return EXIT_FAILURE;
+    }
     AServiceManager_forceLazyServicesPersist(true);
 
     ABinderProcess_joinThreadPool();
-    return EXIT_FAILURE;  // should not reach
+    return EXIT_FAILURE;  // should not reach here
 }
diff --git a/biometrics/face/aidl/default/tests/FakeFaceEngineTest.cpp b/biometrics/face/aidl/default/tests/FakeFaceEngineTest.cpp
index 8c39b58..d448532 100644
--- a/biometrics/face/aidl/default/tests/FakeFaceEngineTest.cpp
+++ b/biometrics/face/aidl/default/tests/FakeFaceEngineTest.cpp
@@ -21,6 +21,7 @@
 #include <aidl/android/hardware/biometrics/face/BnSessionCallback.h>
 #include <android-base/logging.h>
 
+#include "Face.h"
 #include "FakeFaceEngine.h"
 #include "util/Util.h"
 
@@ -141,12 +142,12 @@
     }
 
     void TearDown() override {
-        FaceHalProperties::enrollments({});
-        FaceHalProperties::challenge({});
-        FaceHalProperties::features({});
-        FaceHalProperties::authenticator_id({});
-        FaceHalProperties::strength("");
-        FaceHalProperties::operation_detect_interaction_latency({});
+        Face::cfg().setopt<OptIntVec>("enrollments", {});
+        Face::cfg().set<std::int64_t>("challenge", 0);
+        Face::cfg().setopt<OptIntVec>("features", {});
+        Face::cfg().set<std::int64_t>("authenticator_id", 0);
+        Face::cfg().set<std::string>("strength", "");
+        Face::cfg().setopt<OptIntVec>("operation_detect_interaction_latency", {});
     }
 
     FakeFaceEngine mEngine;
@@ -160,81 +161,83 @@
 
 TEST_F(FakeFaceEngineTest, GenerateChallenge) {
     mEngine.generateChallengeImpl(mCallback.get());
-    ASSERT_EQ(FaceHalProperties::challenge().value(), mCallback->mLastChallenge);
+    ASSERT_EQ(Face::cfg().get<std::int64_t>("challenge"), mCallback->mLastChallenge);
 }
 
 TEST_F(FakeFaceEngineTest, RevokeChallenge) {
-    auto challenge = FaceHalProperties::challenge().value_or(10);
+    auto challenge = Face::cfg().get<std::int64_t>("challenge");
     mEngine.revokeChallengeImpl(mCallback.get(), challenge);
-    ASSERT_FALSE(FaceHalProperties::challenge().has_value());
+    ASSERT_FALSE(Face::cfg().get<std::int64_t>("challenge"));
     ASSERT_EQ(challenge, mCallback->mLastChallengeRevoked);
 }
 
 TEST_F(FakeFaceEngineTest, ResetLockout) {
-    FaceHalProperties::lockout(true);
+    Face::cfg().set<bool>("lockout", true);
     mEngine.resetLockoutImpl(mCallback.get(), {});
     ASSERT_FALSE(mCallback->mLockoutPermanent);
-    ASSERT_FALSE(FaceHalProperties::lockout().value_or(true));
+    ASSERT_FALSE(Face::cfg().get<bool>("lockout"));
 }
 
 TEST_F(FakeFaceEngineTest, AuthenticatorId) {
-    FaceHalProperties::authenticator_id(50);
+    Face::cfg().set<std::int64_t>("authenticator_id", 50);
     mEngine.getAuthenticatorIdImpl(mCallback.get());
     ASSERT_EQ(50, mCallback->mLastAuthenticatorId);
     ASSERT_FALSE(mCallback->mAuthenticatorIdInvalidated);
 }
 
 TEST_F(FakeFaceEngineTest, GetAuthenticatorIdWeakReturnsZero) {
-    FaceHalProperties::strength("weak");
-    FaceHalProperties::authenticator_id(500);
+    Face::cfg().set<std::string>("strength", "weak");
+    Face::cfg().set<std::int64_t>("authenticator_id", 500);
     mEngine.getAuthenticatorIdImpl(mCallback.get());
     ASSERT_EQ(0, mCallback->mLastAuthenticatorId);
     ASSERT_FALSE(mCallback->mAuthenticatorIdInvalidated);
 }
 
 TEST_F(FakeFaceEngineTest, AuthenticatorIdInvalidate) {
-    FaceHalProperties::authenticator_id(500);
+    Face::cfg().set<std::int64_t>("authenticator_id", 500);
     mEngine.invalidateAuthenticatorIdImpl(mCallback.get());
-    ASSERT_NE(500, FaceHalProperties::authenticator_id().value());
+    ASSERT_NE(500, Face::cfg().get<std::int64_t>("authenticator_id"));
     ASSERT_TRUE(mCallback->mAuthenticatorIdInvalidated);
 }
 
 TEST_F(FakeFaceEngineTest, Enroll) {
-    FaceHalProperties::next_enrollment("1,0:1000-[21,5,6,7,1],1100-[1118,1108,1]:true");
+    Face::cfg().set<std::string>("next_enrollment",
+                                 "1,0:1000-[21,5,6,7,1],1100-[1118,1108,1]:true");
     keymaster::HardwareAuthToken hat{.mac = {2, 4}};
     mEngine.enrollImpl(mCallback.get(), hat, {} /*enrollmentType*/, {} /*features*/,
                        mCancel.get_future());
-    ASSERT_FALSE(FaceHalProperties::next_enrollment().has_value());
-    ASSERT_EQ(1, FaceHalProperties::enrollments().size());
-    ASSERT_EQ(1, FaceHalProperties::enrollments()[0].value());
+    ASSERT_FALSE(Face::cfg().getopt<OptString>("next_enrollment").has_value());
+    ASSERT_EQ(1, Face::cfg().getopt<OptIntVec>("enrollments").size());
+    ASSERT_EQ(1, Face::cfg().getopt<OptIntVec>("enrollments")[0].value());
     ASSERT_EQ(1, mCallback->mLastEnrolled);
     ASSERT_EQ(0, mCallback->mRemaining);
 }
 
 TEST_F(FakeFaceEngineTest, EnrollFails) {
-    FaceHalProperties::next_enrollment("1,0:1000-[21,5,6,7,1],1100-[1118,1108,1]:false");
+    Face::cfg().set<std::string>("next_enrollment",
+                                 "1,0:1000-[21,5,6,7,1],1100-[1118,1108,1]:false");
     keymaster::HardwareAuthToken hat{.mac = {2, 4}};
     mEngine.enrollImpl(mCallback.get(), hat, {} /*enrollmentType*/, {} /*features*/,
                        mCancel.get_future());
-    ASSERT_FALSE(FaceHalProperties::next_enrollment().has_value());
-    ASSERT_EQ(0, FaceHalProperties::enrollments().size());
+    ASSERT_FALSE(Face::cfg().getopt<OptString>("next_enrollment").has_value());
+    ASSERT_EQ(0, Face::cfg().getopt<OptIntVec>("enrollments").size());
 }
 
 TEST_F(FakeFaceEngineTest, EnrollCancel) {
-    FaceHalProperties::next_enrollment("1:2000-[21,8,9],300:false");
+    Face::cfg().set<std::string>("next_enrollment", "1:2000-[21,8,9],300:false");
     keymaster::HardwareAuthToken hat{.mac = {2, 4}};
     mCancel.set_value();
     mEngine.enrollImpl(mCallback.get(), hat, {} /*enrollmentType*/, {} /*features*/,
                        mCancel.get_future());
     ASSERT_EQ(Error::CANCELED, mCallback->mError);
     ASSERT_EQ(-1, mCallback->mLastEnrolled);
-    ASSERT_EQ(0, FaceHalProperties::enrollments().size());
-    ASSERT_TRUE(FaceHalProperties::next_enrollment().has_value());
+    ASSERT_EQ(0, Face::cfg().getopt<OptIntVec>("enrollments").size());
+    ASSERT_FALSE(Face::cfg().get<std::string>("next_enrollment").empty());
 }
 
 TEST_F(FakeFaceEngineTest, Authenticate) {
-    FaceHalProperties::enrollments({100});
-    FaceHalProperties::enrollment_hit(100);
+    Face::cfg().setopt<OptIntVec>("enrollments", {100});
+    Face::cfg().set<std::int32_t>("enrollment_hit", 100);
     mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, mCancel.get_future());
 
     ASSERT_EQ(100, mCallback->mLastAuthenticated);
@@ -242,32 +245,32 @@
 }
 
 TEST_F(FakeFaceEngineTest, AuthenticateCancel) {
-    FaceHalProperties::enrollments({100});
-    FaceHalProperties::enrollment_hit(100);
+    Face::cfg().setopt<OptIntVec>("enrollments", {100});
+    Face::cfg().set<std::int32_t>("enrollment_hit", 100);
     mCancel.set_value();
     mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, mCancel.get_future());
     ASSERT_EQ(Error::CANCELED, mCallback->mError);
 }
 
 TEST_F(FakeFaceEngineTest, AuthenticateFailedForUnEnrolled) {
-    FaceHalProperties::enrollments({3});
-    FaceHalProperties::enrollment_hit(100);
+    Face::cfg().setopt<OptIntVec>("enrollments", {3});
+    Face::cfg().set<std::int32_t>("enrollment_hit", 100);
     mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, mCancel.get_future());
     ASSERT_EQ(Error::TIMEOUT, mCallback->mError);
     ASSERT_TRUE(mCallback->mAuthenticateFailed);
 }
 
 TEST_F(FakeFaceEngineTest, DetectInteraction) {
-    FaceHalProperties::enrollments({100});
-    FaceHalProperties::enrollment_hit(100);
+    Face::cfg().setopt<OptIntVec>("enrollments", {100});
+    Face::cfg().set<std::int32_t>("enrollment_hit", 100);
     ASSERT_EQ(0, mCallback->mInteractionDetectedCount);
     mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
     ASSERT_EQ(1, mCallback->mInteractionDetectedCount);
 }
 
 TEST_F(FakeFaceEngineTest, DetectInteractionCancel) {
-    FaceHalProperties::enrollments({100});
-    FaceHalProperties::enrollment_hit(100);
+    Face::cfg().setopt<OptIntVec>("enrollments", {100});
+    Face::cfg().set<std::int32_t>("enrollment_hit", 100);
     mCancel.set_value();
     mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
     ASSERT_EQ(Error::CANCELED, mCallback->mError);
@@ -279,7 +282,7 @@
 }
 
 TEST_F(FakeFaceEngineTest, SetFeature) {
-    FaceHalProperties::enrollments({1});
+    Face::cfg().setopt<OptIntVec>("enrollments", {1});
     keymaster::HardwareAuthToken hat{.mac = {2, 4}};
     mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, true);
     auto features = mCallback->mFeatures;
@@ -294,7 +297,7 @@
 }
 
 TEST_F(FakeFaceEngineTest, ToggleFeature) {
-    FaceHalProperties::enrollments({1});
+    Face::cfg().setopt<OptIntVec>("enrollments", {1});
     keymaster::HardwareAuthToken hat{.mac = {2, 4}};
     mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, true);
     mEngine.getFeaturesImpl(mCallback.get());
@@ -310,7 +313,7 @@
 }
 
 TEST_F(FakeFaceEngineTest, TurningOffNonExistentFeatureDoesNothing) {
-    FaceHalProperties::enrollments({1});
+    Face::cfg().setopt<OptIntVec>("enrollments", {1});
     keymaster::HardwareAuthToken hat{.mac = {2, 4}};
     mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, false);
     mEngine.getFeaturesImpl(mCallback.get());
@@ -319,7 +322,7 @@
 }
 
 TEST_F(FakeFaceEngineTest, SetMultipleFeatures) {
-    FaceHalProperties::enrollments({1});
+    Face::cfg().setopt<OptIntVec>("enrollments", {1});
     keymaster::HardwareAuthToken hat{.mac = {2, 4}};
     mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, true);
     mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_DIVERSE_POSES, true);
@@ -335,7 +338,7 @@
 }
 
 TEST_F(FakeFaceEngineTest, SetMultipleFeaturesAndTurnOffSome) {
-    FaceHalProperties::enrollments({1});
+    Face::cfg().setopt<OptIntVec>("enrollments", {1});
     keymaster::HardwareAuthToken hat{.mac = {2, 4}};
     mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, true);
     mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_DIVERSE_POSES, true);
@@ -352,7 +355,7 @@
 }
 
 TEST_F(FakeFaceEngineTest, Enumerate) {
-    FaceHalProperties::enrollments({120, 3});
+    Face::cfg().setopt<OptIntVec>("enrollments", {120, 3});
     mEngine.enumerateEnrollmentsImpl(mCallback.get());
     auto enrolls = mCallback->mLastEnrollmentsEnumerated;
     ASSERT_FALSE(enrolls.empty());
@@ -361,7 +364,7 @@
 }
 
 TEST_F(FakeFaceEngineTest, RemoveEnrollments) {
-    FaceHalProperties::enrollments({120, 3, 100});
+    Face::cfg().setopt<OptIntVec>("enrollments", {120, 3, 100});
     mEngine.removeEnrollmentsImpl(mCallback.get(), {120, 100});
     mEngine.enumerateEnrollmentsImpl(mCallback.get());
     auto enrolls = mCallback->mLastEnrollmentsEnumerated;
@@ -372,9 +375,9 @@
 }
 
 TEST_F(FakeFaceEngineTest, ResetLockoutWithAuth) {
-    FaceHalProperties::lockout(true);
-    FaceHalProperties::enrollments({33});
-    FaceHalProperties::enrollment_hit(33);
+    Face::cfg().set<bool>("lockout", true);
+    Face::cfg().setopt<OptIntVec>("enrollments", {33});
+    Face::cfg().set<std::int32_t>("enrollment_hit", 33);
     auto cancelFuture = mCancel.get_future();
     mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, cancelFuture);
 
@@ -382,28 +385,30 @@
 
     mEngine.resetLockoutImpl(mCallback.get(), {} /* hat */);
     ASSERT_FALSE(mCallback->mLockoutPermanent);
-    FaceHalProperties::enrollment_hit(33);
+    Face::cfg().set<std::int32_t>("enrollment_hit", 33);
     mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, cancelFuture);
     ASSERT_EQ(33, mCallback->mLastAuthenticated);
     ASSERT_FALSE(mCallback->mAuthenticateFailed);
 }
 
 TEST_F(FakeFaceEngineTest, LatencyDefault) {
-    FaceHalProperties::operation_detect_interaction_latency({});
-    ASSERT_EQ(DEFAULT_LATENCY,
-              mEngine.getLatency(FaceHalProperties::operation_detect_interaction_latency()));
+    Face::cfg().setopt<OptIntVec>("operation_detect_interaction_latency", {});
+    ASSERT_EQ(DEFAULT_LATENCY, mEngine.getLatency(Face::cfg().getopt<OptIntVec>(
+                                       "operation_detect_interaction_latency")));
 }
 
 TEST_F(FakeFaceEngineTest, LatencyFixed) {
-    FaceHalProperties::operation_detect_interaction_latency({10});
-    ASSERT_EQ(10, mEngine.getLatency(FaceHalProperties::operation_detect_interaction_latency()));
+    Face::cfg().setopt<OptIntVec>("operation_detect_interaction_latency", {10});
+    ASSERT_EQ(10, mEngine.getLatency(
+                          Face::cfg().getopt<OptIntVec>("operation_detect_interaction_latency")));
 }
 
 TEST_F(FakeFaceEngineTest, LatencyRandom) {
-    FaceHalProperties::operation_detect_interaction_latency({1, 1000});
+    Face::cfg().setopt<OptIntVec>("operation_detect_interaction_latency", {1, 1000});
     std::set<int32_t> latencySet;
     for (int i = 0; i < 100; i++) {
-        auto x = mEngine.getLatency(FaceHalProperties::operation_detect_interaction_latency());
+        auto x = mEngine.getLatency(
+                Face::cfg().getopt<OptIntVec>("operation_detect_interaction_latency"));
         ASSERT_TRUE(x >= 1 && x <= 1000);
         latencySet.insert(x);
     }
diff --git a/biometrics/face/aidl/default/tests/FakeLockoutTrackerTest.cpp b/biometrics/face/aidl/default/tests/FakeLockoutTrackerTest.cpp
index fa07d1d..8564f6b 100644
--- a/biometrics/face/aidl/default/tests/FakeLockoutTrackerTest.cpp
+++ b/biometrics/face/aidl/default/tests/FakeLockoutTrackerTest.cpp
@@ -21,6 +21,7 @@
 
 #include <android-base/logging.h>
 
+#include "Face.h"
 #include "FakeLockoutTracker.h"
 #include "util/Util.h"
 
@@ -103,19 +104,21 @@
     static constexpr int32_t LOCKOUT_TIMED_DURATION = 100;
 
     void SetUp() override {
-        FaceHalProperties::lockout_timed_threshold(LOCKOUT_TIMED_THRESHOLD);
-        FaceHalProperties::lockout_timed_duration(LOCKOUT_TIMED_DURATION);
-        FaceHalProperties::lockout_permanent_threshold(LOCKOUT_PERMANENT_THRESHOLD);
+        Face::cfg().set<std::int32_t>("lockout_timed_threshold", LOCKOUT_TIMED_THRESHOLD);
+        Face::cfg().set<std::int32_t>("lockout_timed_duration", LOCKOUT_TIMED_DURATION);
+        Face::cfg().set<std::int32_t>("lockout_permanent_threshold", LOCKOUT_PERMANENT_THRESHOLD);
+        Face::cfg().set<bool>("lockout_enable", false);
+        Face::cfg().set<bool>("lockout", false);
         mCallback = ndk::SharedRefBase::make<TestSessionCallback>();
     }
 
     void TearDown() override {
         // reset to default
-        FaceHalProperties::lockout_timed_threshold(5);
-        FaceHalProperties::lockout_timed_duration(20);
-        FaceHalProperties::lockout_permanent_threshold(10000);
-        FaceHalProperties::lockout_enable(false);
-        FaceHalProperties::lockout(false);
+        Face::cfg().set<std::int32_t>("lockout_timed_threshold", 5);
+        Face::cfg().set<std::int32_t>("lockout_timed_duration", 20);
+        Face::cfg().set<std::int32_t>("lockout_permanent_threshold", 10000);
+        Face::cfg().set<bool>("lockout_enable", false);
+        Face::cfg().set<bool>("lockout", false);
     }
 
     FakeLockoutTracker mLockoutTracker;
@@ -123,7 +126,7 @@
 };
 
 TEST_F(FakeLockoutTrackerTest, addFailedAttemptDisable) {
-    FaceHalProperties::lockout_enable(false);
+    Face::cfg().set<bool>("lockout_enable", false);
     for (int i = 0; i < LOCKOUT_TIMED_THRESHOLD + 1; i++)
         mLockoutTracker.addFailedAttempt(mCallback.get());
     ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kNone);
@@ -131,7 +134,7 @@
 }
 
 TEST_F(FakeLockoutTrackerTest, addFailedAttemptPermanent) {
-    FaceHalProperties::lockout_enable(true);
+    Face::cfg().set<bool>("lockout_enable", true);
     ASSERT_FALSE(mLockoutTracker.checkIfLockout(mCallback.get()));
     for (int i = 0; i < LOCKOUT_PERMANENT_THRESHOLD - 1; i++)
         mLockoutTracker.addFailedAttempt(mCallback.get());
@@ -145,8 +148,8 @@
 }
 
 TEST_F(FakeLockoutTrackerTest, addFailedAttemptLockoutTimed) {
-    FaceHalProperties::lockout_enable(true);
-    FaceHalProperties::lockout_timed_enable(true);
+    Face::cfg().set<bool>("lockout_enable", true);
+    Face::cfg().set<bool>("lockout_timed_enable", true);
     ASSERT_FALSE(mLockoutTracker.checkIfLockout(mCallback.get()));
     for (int i = 0; i < LOCKOUT_TIMED_THRESHOLD; i++)
         mLockoutTracker.addFailedAttempt(mCallback.get());
@@ -168,8 +171,8 @@
 }
 
 TEST_F(FakeLockoutTrackerTest, addFailedAttemptLockout_TimedThenPermanent) {
-    FaceHalProperties::lockout_enable(true);
-    FaceHalProperties::lockout_timed_enable(true);
+    Face::cfg().set<bool>("lockout_enable", true);
+    Face::cfg().set<bool>("lockout_timed_enable", true);
     ASSERT_FALSE(mLockoutTracker.checkIfLockout(mCallback.get()));
     for (int i = 0; i < LOCKOUT_TIMED_THRESHOLD; i++)
         mLockoutTracker.addFailedAttempt(mCallback.get());
@@ -182,8 +185,8 @@
 }
 
 TEST_F(FakeLockoutTrackerTest, addFailedAttemptLockoutTimedTwice) {
-    FaceHalProperties::lockout_enable(true);
-    FaceHalProperties::lockout_timed_enable(true);
+    Face::cfg().set<bool>("lockout_enable", true);
+    Face::cfg().set<bool>("lockout_timed_enable", true);
     ASSERT_FALSE(mLockoutTracker.checkIfLockout(mCallback.get()));
     ASSERT_EQ(0, mCallback->mLockoutTimed);
     for (int i = 0; i < LOCKOUT_TIMED_THRESHOLD; i++)
@@ -198,7 +201,7 @@
 }
 
 TEST_F(FakeLockoutTrackerTest, resetLockout) {
-    FaceHalProperties::lockout_enable(true);
+    Face::cfg().set<bool>("lockout_enable", true);
     ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kNone);
     for (int i = 0; i < LOCKOUT_PERMANENT_THRESHOLD; i++)
         mLockoutTracker.addFailedAttempt(mCallback.get());
diff --git a/biometrics/face/aidl/default/tests/VirtualHalTest.cpp b/biometrics/face/aidl/default/tests/VirtualHalTest.cpp
new file mode 100644
index 0000000..2f19805
--- /dev/null
+++ b/biometrics/face/aidl/default/tests/VirtualHalTest.cpp
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/binder_process.h>
+#include <face.sysprop.h>
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+
+#include "Face.h"
+#include "VirtualHal.h"
+
+using namespace ::android::face::virt;
+using namespace ::aidl::android::hardware::biometrics::face;
+
+namespace aidl::android::hardware::biometrics::face {
+
+class VirtualHalTest : public ::testing::Test {
+  public:
+    static const int32_t STATUS_FAILED_TO_SET_PARAMETER = 2;
+
+  protected:
+    void SetUp() override {
+        mHal = ndk::SharedRefBase::make<Face>();
+        mVhal = ndk::SharedRefBase::make<VirtualHal>(mHal);
+        ASSERT_TRUE(mVhal != nullptr);
+        mHal->resetConfigToDefault();
+    }
+
+    void TearDown() override { mHal->resetConfigToDefault(); }
+
+    std::shared_ptr<VirtualHal> mVhal;
+
+    ndk::ScopedAStatus validateNonNegativeInputOfInt32(const char* name,
+                                                       ndk::ScopedAStatus (VirtualHal::*f)(int32_t),
+                                                       const std::vector<int32_t>& in_good);
+
+  private:
+    std::shared_ptr<Face> mHal;
+};
+
+ndk::ScopedAStatus VirtualHalTest::validateNonNegativeInputOfInt32(
+        const char* name, ndk::ScopedAStatus (VirtualHal::*f)(int32_t),
+        const std::vector<int32_t>& in_params_good) {
+    ndk::ScopedAStatus status;
+    for (auto& param : in_params_good) {
+        status = (*mVhal.*f)(param);
+        if (!status.isOk()) return status;
+        if (Face::cfg().get<int32_t>(name) != param) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    VirtualHalTest::STATUS_FAILED_TO_SET_PARAMETER,
+                    "Error: fail to set non-negative parameter"));
+        }
+    }
+
+    int32_t old_param = Face::cfg().get<int32_t>(name);
+    status = (*mVhal.*f)(-1);
+    if (status.isOk()) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                VirtualHalTest::STATUS_FAILED_TO_SET_PARAMETER, "Error: should return NOK"));
+    }
+    if (status.getServiceSpecificError() != IVirtualHal::STATUS_INVALID_PARAMETER) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                VirtualHalTest::STATUS_FAILED_TO_SET_PARAMETER,
+                "Error: unexpected return error code"));
+    }
+    if (Face::cfg().get<int32_t>(name) != old_param) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                VirtualHalTest::STATUS_FAILED_TO_SET_PARAMETER,
+                "Error: unexpected parameter change on failed attempt"));
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+TEST_F(VirtualHalTest, init) {
+    mVhal->setLockout(false);
+    ASSERT_TRUE(Face::cfg().get<bool>("lockout") == false);
+    ASSERT_TRUE(Face::cfg().get<std::string>("type") == "rgb");
+    ASSERT_TRUE(Face::cfg().get<std::string>("strength") == "strong");
+    std::int64_t id = Face::cfg().get<std::int64_t>("authenticator_id");
+    ASSERT_TRUE(Face::cfg().get<std::int64_t>("authenticator_id") == 0);
+    ASSERT_TRUE(Face::cfg().getopt<OptIntVec>("enrollments") == OptIntVec());
+}
+
+TEST_F(VirtualHalTest, enrollment_hit_int32) {
+    mVhal->setEnrollmentHit(11);
+    ASSERT_TRUE(Face::cfg().get<int32_t>("enrollment_hit") == 11);
+}
+
+TEST_F(VirtualHalTest, next_enrollment) {
+    struct {
+        std::string nextEnrollmentStr;
+        face::NextEnrollment nextEnrollment;
+    } testData[] = {
+            {"1:20:true", {1, {{20}}, true}},
+            {"1:50,60,70:true", {1, {{50}, {60}, {70}}, true}},
+            {"2:50-[21],60,70-[4,1002,1]:false",
+             {2,
+              {{50, {{AcquiredInfo::START}}},
+               {60},
+               {70, {{AcquiredInfo::TOO_DARK}, {1002}, {AcquiredInfo::GOOD}}}},
+              false}},
+    };
+
+    for (auto& d : testData) {
+        mVhal->setNextEnrollment(d.nextEnrollment);
+        ASSERT_TRUE(Face::cfg().get<std::string>("next_enrollment") == d.nextEnrollmentStr);
+    }
+}
+
+TEST_F(VirtualHalTest, authenticator_id_int64) {
+    mVhal->setAuthenticatorId(12345678900);
+    ASSERT_TRUE(Face::cfg().get<int64_t>("authenticator_id") == 12345678900);
+}
+
+TEST_F(VirtualHalTest, opeationAuthenticateFails_bool) {
+    mVhal->setOperationAuthenticateFails(true);
+    ASSERT_TRUE(Face::cfg().get<bool>("operation_authenticate_fails"));
+}
+
+TEST_F(VirtualHalTest, operationAuthenticateAcquired_int32_vector) {
+    using Tag = AcquiredInfoAndVendorCode::Tag;
+    std::vector<AcquiredInfoAndVendorCode> ac{
+            {AcquiredInfo::START}, {AcquiredInfo::TOO_FAR}, {1023}};
+    mVhal->setOperationAuthenticateAcquired(ac);
+    OptIntVec ac_get = Face::cfg().getopt<OptIntVec>("operation_authenticate_acquired");
+    ASSERT_TRUE(ac_get.size() == ac.size());
+    for (int i = 0; i < ac.size(); i++) {
+        int acCode = (ac[i].getTag() == Tag::acquiredInfo) ? (int)ac[i].get<Tag::acquiredInfo>()
+                                                           : ac[i].get<Tag::vendorCode>();
+        ASSERT_TRUE(acCode == ac_get[i]);
+    }
+}
+
+TEST_F(VirtualHalTest, type) {
+    struct {
+        FaceSensorType type;
+        const char* typeStr;
+    } typeMap[] = {{FaceSensorType::RGB, "rgb"},
+                   {FaceSensorType::IR, "ir"},
+                   {FaceSensorType::UNKNOWN, "unknown"}};
+    for (auto const& x : typeMap) {
+        mVhal->setType(x.type);
+        ASSERT_TRUE(Face::cfg().get<std::string>("type") == x.typeStr);
+    }
+}
+
+TEST_F(VirtualHalTest, sensorStrength) {
+    struct {
+        common::SensorStrength strength;
+        const char* strengthStr;
+    } strengths[] = {{common::SensorStrength::CONVENIENCE, "CONVENIENCE"},
+                     {common::SensorStrength::WEAK, "WEAK"},
+                     {common::SensorStrength::STRONG, "STRONG"}};
+
+    for (auto const& x : strengths) {
+        mVhal->setSensorStrength(x.strength);
+        ASSERT_TRUE(Face::cfg().get<std::string>("strength") == x.strengthStr);
+    }
+}
+
+TEST_F(VirtualHalTest, setLatency) {
+    ndk::ScopedAStatus status;
+    std::vector<int32_t> in_lats[] = {{1}, {2, 3}, {5, 4}};
+    for (auto const& in_lat : in_lats) {
+        status = mVhal->setOperationAuthenticateLatency(in_lat);
+        ASSERT_TRUE(status.isOk());
+        OptIntVec out_lat = Face::cfg().getopt<OptIntVec>("operation_authenticate_latency");
+        ASSERT_TRUE(in_lat.size() == out_lat.size());
+        for (int i = 0; i < in_lat.size(); i++) {
+            ASSERT_TRUE(in_lat[i] == out_lat[i]);
+        }
+    }
+
+    std::vector<int32_t> bad_in_lats[] = {{}, {1, 2, 3}, {1, -3}};
+    for (auto const& in_lat : bad_in_lats) {
+        status = mVhal->setOperationAuthenticateLatency(in_lat);
+        ASSERT_TRUE(!status.isOk());
+        ASSERT_TRUE(status.getServiceSpecificError() == IVirtualHal::STATUS_INVALID_PARAMETER);
+    }
+}
+
+TEST_F(VirtualHalTest, setOperationAuthenticateDuration) {
+    ndk::ScopedAStatus status = validateNonNegativeInputOfInt32(
+            "operation_authenticate_duration", &IVirtualHal::setOperationAuthenticateDuration,
+            {0, 33});
+    ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(VirtualHalTest, setLockoutTimedDuration) {
+    ndk::ScopedAStatus status = validateNonNegativeInputOfInt32(
+            "lockout_timed_duration", &IVirtualHal::setLockoutTimedDuration, {0, 35});
+    ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(VirtualHalTest, setLockoutTimedThreshold) {
+    ndk::ScopedAStatus status = validateNonNegativeInputOfInt32(
+            "lockout_timed_threshold", &IVirtualHal::setLockoutTimedThreshold, {0, 36});
+    ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(VirtualHalTest, setLockoutPermanentThreshold) {
+    ndk::ScopedAStatus status = validateNonNegativeInputOfInt32(
+            "lockout_permanent_threshold", &IVirtualHal::setLockoutPermanentThreshold, {0, 37});
+    ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(VirtualHalTest, setOthers) {
+    // Verify that there is no CHECK() failures
+    mVhal->setEnrollments({7, 6, 5});
+    mVhal->setChallenge(111222333444555666);
+    mVhal->setOperationAuthenticateError(4);
+    mVhal->setOperationEnrollLatency({4, 5});
+    mVhal->setLockout(false);
+    mVhal->setLockoutEnable(false);
+}
+
+}  // namespace aidl::android::hardware::biometrics::face
+
+int main(int argc, char** argv) {
+    testing::InitGoogleTest(&argc, argv);
+    ABinderProcess_startThreadPool();
+    return RUN_ALL_TESTS();
+}
diff --git a/biometrics/fingerprint/aidl/Android.bp b/biometrics/fingerprint/aidl/Android.bp
index d0c1b8b..9f9e723 100644
--- a/biometrics/fingerprint/aidl/Android.bp
+++ b/biometrics/fingerprint/aidl/Android.bp
@@ -11,7 +11,7 @@
     name: "android.hardware.biometrics.fingerprint",
     vendor_available: true,
     srcs: [
-        "android/hardware/biometrics/fingerprint/**/*.aidl",
+        "android/hardware/biometrics/fingerprint/*.aidl",
     ],
     imports: [
         "android.hardware.biometrics.common-V4",
@@ -25,6 +25,12 @@
         cpp: {
             enabled: false,
         },
+        ndk: {
+            apex_available: [
+                "//apex_available:platform",
+                "//apex_available:anyapex",
+            ],
+        },
         rust: {
             enabled: true,
         },
@@ -60,5 +66,34 @@
         },
 
     ],
+    frozen: true,
+}
+
+aidl_interface {
+    name: "android.hardware.biometrics.fingerprint.virtualhal",
+    srcs: [
+        "android/hardware/biometrics/fingerprint/virtualhal/*.aidl",
+    ],
+    imports: [
+        "android.hardware.biometrics.common-V4",
+        "android.hardware.keymaster-V4",
+        "android.hardware.biometrics.fingerprint-V4",
+    ],
+    vendor_available: true,
+    unstable: true,
+    backend: {
+        java: {
+            platform_apis: true,
+        },
+        cpp: {
+            enabled: false,
+        },
+        ndk: {
+            apex_available: [
+                "com.android.hardware.biometrics.fingerprint.virtual",
+                "//apex_available:platform",
+            ],
+        },
+    },
     frozen: false,
 }
diff --git a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/AcquiredInfoAndVendorCode.aidl b/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/AcquiredInfoAndVendorCode.aidl
deleted file mode 100644
index c1dc51c..0000000
--- a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/AcquiredInfoAndVendorCode.aidl
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-///////////////////////////////////////////////////////////////////////////////
-// 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.biometrics.fingerprint;
-/* @hide */
-@VintfStability
-union AcquiredInfoAndVendorCode {
-  android.hardware.biometrics.fingerprint.AcquiredInfo acquiredInfo = android.hardware.biometrics.fingerprint.AcquiredInfo.UNKNOWN;
-  int vendorCode;
-}
diff --git a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/IVirtualHal.aidl b/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/IVirtualHal.aidl
deleted file mode 100644
index 33ae83c..0000000
--- a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/IVirtualHal.aidl
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-///////////////////////////////////////////////////////////////////////////////
-// 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.biometrics.fingerprint;
-/* @hide */
-@VintfStability
-interface IVirtualHal {
-  oneway void setEnrollments(in int[] id);
-  oneway void setEnrollmentHit(in int hit_id);
-  oneway void setNextEnrollment(in android.hardware.biometrics.fingerprint.NextEnrollment next_enrollment);
-  oneway void setAuthenticatorId(in long id);
-  oneway void setChallenge(in long challenge);
-  oneway void setOperationAuthenticateFails(in boolean fail);
-  oneway void setOperationAuthenticateLatency(in int[] latencyMs);
-  oneway void setOperationAuthenticateDuration(in int durationMs);
-  oneway void setOperationAuthenticateError(in int error);
-  oneway void setOperationAuthenticateAcquired(in android.hardware.biometrics.fingerprint.AcquiredInfoAndVendorCode[] acquired);
-  oneway void setOperationEnrollError(in int error);
-  oneway void setOperationEnrollLatency(in int[] latencyMs);
-  oneway void setOperationDetectInteractionLatency(in int[] latencyMs);
-  oneway void setOperationDetectInteractionError(in int error);
-  oneway void setOperationDetectInteractionDuration(in int durationMs);
-  oneway void setOperationDetectInteractionAcquired(in android.hardware.biometrics.fingerprint.AcquiredInfoAndVendorCode[] acquired);
-  oneway void setLockout(in boolean lockout);
-  oneway void setLockoutEnable(in boolean enable);
-  oneway void setLockoutTimedThreshold(in int threshold);
-  oneway void setLockoutTimedDuration(in int durationMs);
-  oneway void setLockoutPermanentThreshold(in int threshold);
-  oneway void resetConfigurations();
-  oneway void setType(in android.hardware.biometrics.fingerprint.FingerprintSensorType type);
-  oneway void setSensorId(in int id);
-  oneway void setSensorStrength(in android.hardware.biometrics.common.SensorStrength strength);
-  oneway void setMaxEnrollmentPerUser(in int max);
-  oneway void setSensorLocation(in android.hardware.biometrics.fingerprint.SensorLocation loc);
-  oneway void setNavigationGuesture(in boolean v);
-  oneway void setDetectInteraction(in boolean v);
-  oneway void setDisplayTouch(in boolean v);
-  oneway void setControlIllumination(in boolean v);
-  const int STATUS_INVALID_PARAMETER = 1;
-}
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/AcquiredInfoAndVendorCode.aidl b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/virtualhal/AcquiredInfoAndVendorCode.aidl
similarity index 93%
rename from biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/AcquiredInfoAndVendorCode.aidl
rename to biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/virtualhal/AcquiredInfoAndVendorCode.aidl
index c7be950..1fc7221 100644
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/AcquiredInfoAndVendorCode.aidl
+++ b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/virtualhal/AcquiredInfoAndVendorCode.aidl
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-package android.hardware.biometrics.fingerprint;
+package android.hardware.biometrics.fingerprint.virtualhal;
 
 import android.hardware.biometrics.fingerprint.AcquiredInfo;
 
 /**
  * @hide
  */
-@VintfStability
 union AcquiredInfoAndVendorCode {
     /**
      * Acquired info as specified in AcqauiredInfo.aidl
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/EnrollmentProgressStep.aidl b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/virtualhal/EnrollmentProgressStep.aidl
similarity index 87%
rename from biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/EnrollmentProgressStep.aidl
rename to biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/virtualhal/EnrollmentProgressStep.aidl
index bf038f6..b0b2926 100644
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/EnrollmentProgressStep.aidl
+++ b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/virtualhal/EnrollmentProgressStep.aidl
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-package android.hardware.biometrics.fingerprint;
+package android.hardware.biometrics.fingerprint.virtualhal;
 
-import android.hardware.biometrics.fingerprint.AcquiredInfoAndVendorCode;
+import android.hardware.biometrics.fingerprint.virtualhal.AcquiredInfoAndVendorCode;
 
 /**
  * @hide
  */
-@VintfStability
 parcelable EnrollmentProgressStep {
     /**
      * The duration of the enrollment step in milli-seconds
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/IVirtualHal.aidl b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/virtualhal/IVirtualHal.aidl
similarity index 96%
rename from biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/IVirtualHal.aidl
rename to biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/virtualhal/IVirtualHal.aidl
index cb9135e..5af84ed 100644
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/IVirtualHal.aidl
+++ b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/virtualhal/IVirtualHal.aidl
@@ -14,19 +14,19 @@
  * limitations under the License.
  */
 
-package android.hardware.biometrics.fingerprint;
+package android.hardware.biometrics.fingerprint.virtualhal;
 
 import android.hardware.biometrics.common.SensorStrength;
-import android.hardware.biometrics.fingerprint.AcquiredInfoAndVendorCode;
 import android.hardware.biometrics.fingerprint.FingerprintSensorType;
-import android.hardware.biometrics.fingerprint.NextEnrollment;
+import android.hardware.biometrics.fingerprint.IFingerprint;
 import android.hardware.biometrics.fingerprint.SensorLocation;
+import android.hardware.biometrics.fingerprint.virtualhal.AcquiredInfoAndVendorCode;
+import android.hardware.biometrics.fingerprint.virtualhal.NextEnrollment;
 
 /**
  * @hide
  */
-@VintfStability
-oneway interface IVirtualHal {
+interface IVirtualHal {
     /**
      * The operation failed due to invalid input parameters, the error messages should
      * gives more details
@@ -315,4 +315,5 @@
     void setDetectInteraction(in boolean v);
     void setDisplayTouch(in boolean v);
     void setControlIllumination(in boolean v);
+    IFingerprint getFingerprintHal();
 }
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/NextEnrollment.aidl b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/virtualhal/NextEnrollment.aidl
similarity index 85%
copy from biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/NextEnrollment.aidl
copy to biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/virtualhal/NextEnrollment.aidl
index 4b50850..2d704f1 100644
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/NextEnrollment.aidl
+++ b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/virtualhal/NextEnrollment.aidl
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-package android.hardware.biometrics.fingerprint;
+package android.hardware.biometrics.fingerprint.virtualhal;
+
+import android.hardware.biometrics.fingerprint.virtualhal.EnrollmentProgressStep;
 
 /**
  * @hide
  */
-@VintfStability
 parcelable NextEnrollment {
     /**
      *  Identifier of the next enrollment if successful
@@ -31,7 +32,7 @@
      *  and sequence of acquired info codes to be generated by HAL.
      *  See EnrollmentProgressStep.aidl for more details
      */
-    android.hardware.biometrics.fingerprint.EnrollmentProgressStep[] progressSteps;
+    EnrollmentProgressStep[] progressSteps;
 
     /**
      * Success or failure of the next enrollment
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/virtualhal/README.md b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/virtualhal/README.md
new file mode 100644
index 0000000..eaf2336
--- /dev/null
+++ b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/virtualhal/README.md
@@ -0,0 +1,2 @@
+The aidl files in this directory are used only by fingerprint virtual hal
+which is controlled/configured via IVirtualHal interface
diff --git a/biometrics/fingerprint/aidl/default/Android.bp b/biometrics/fingerprint/aidl/default/Android.bp
index 9b72c87..faaa9c6 100644
--- a/biometrics/fingerprint/aidl/default/Android.bp
+++ b/biometrics/fingerprint/aidl/default/Android.bp
@@ -8,10 +8,9 @@
     default_applicable_licenses: ["hardware_interfaces_license"],
 }
 
-cc_binary {
-    name: "android.hardware.biometrics.fingerprint-service.example",
-    vendor: true,
-    relative_install_path: "hw",
+cc_library_static {
+    name: "android.hardware.biometrics.fingerprint-service.lib",
+    vendor_available: true,
     local_include_dirs: ["include"],
     srcs: [
         "FakeLockoutTracker.cpp",
@@ -30,22 +29,80 @@
         "libbinder_ndk",
         "liblog",
     ],
-    static_libs: [
+    whole_static_libs: [
         "libandroid.hardware.biometrics.fingerprint.VirtualProps",
         "libbase",
-        "android.hardware.biometrics.fingerprint-V5-ndk",
+        "android.hardware.biometrics.fingerprint.virtualhal-ndk",
+        "android.hardware.biometrics.fingerprint-V4-ndk",
         "android.hardware.biometrics.common-V4-ndk",
         "android.hardware.biometrics.common.thread",
         "android.hardware.biometrics.common.util",
         "android.hardware.biometrics.common.config",
         "android.hardware.keymaster-V4-ndk",
     ],
+    product_variables: {
+        debuggable: {
+            cflags: ["-DFPS_DEBUGGABLE"],
+        },
+    },
+    apex_available: [
+        "com.android.hardware.biometrics.fingerprint.virtual",
+        "//apex_available:platform",
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.biometrics.fingerprint-service.example",
+    system_ext_specific: true,
+    relative_install_path: "hw",
+    local_include_dirs: ["include"],
+    srcs: [
+    ],
+    stl: "c++_static",
+    shared_libs: [
+        "libbinder_ndk",
+        "liblog",
+    ],
+    whole_static_libs: [
+        "android.hardware.biometrics.fingerprint-service.lib",
+    ],
     installable: false, // install APEX instead
     product_variables: {
         debuggable: {
             cflags: ["-DFPS_DEBUGGABLE"],
         },
     },
+    apex_available: [
+        "com.android.hardware.biometrics.fingerprint.virtual",
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.biometrics.fingerprint-service.default",
+    //system_ext_specific: true,
+    vendor: true,
+    relative_install_path: "hw",
+    init_rc: ["fingerprint-default.rc"],
+    vintf_fragments: ["fingerprint-default.xml"],
+    local_include_dirs: ["include"],
+    srcs: [
+    ],
+    stl: "c++_static",
+    shared_libs: [
+        "libbinder_ndk",
+        "liblog",
+    ],
+    whole_static_libs: [
+        "android.hardware.biometrics.fingerprint-service.lib",
+    ],
+    product_variables: {
+        debuggable: {
+            cflags: ["-DFPS_DEBUGGABLE"],
+        },
+    },
+    apex_available: [
+        "//apex_available:platform",
+    ],
 }
 
 cc_test {
@@ -63,14 +120,13 @@
     ],
     static_libs: [
         "libandroid.hardware.biometrics.fingerprint.VirtualProps",
-        "android.hardware.biometrics.fingerprint-V5-ndk",
+        "android.hardware.biometrics.fingerprint-V4-ndk",
         "android.hardware.biometrics.common-V4-ndk",
         "android.hardware.keymaster-V4-ndk",
         "android.hardware.biometrics.common.util",
         "android.hardware.biometrics.common.config",
         "android.hardware.biometrics.common.thread",
     ],
-    vendor: true,
     test_suites: ["general-tests"],
     require_root: true,
 }
@@ -91,14 +147,13 @@
     ],
     static_libs: [
         "libandroid.hardware.biometrics.fingerprint.VirtualProps",
-        "android.hardware.biometrics.fingerprint-V5-ndk",
+        "android.hardware.biometrics.fingerprint-V4-ndk",
         "android.hardware.biometrics.common-V4-ndk",
         "android.hardware.keymaster-V4-ndk",
         "android.hardware.biometrics.common.util",
         "android.hardware.biometrics.common.config",
         "android.hardware.biometrics.common.thread",
     ],
-    vendor: true,
     test_suites: ["general-tests"],
     require_root: true,
 }
@@ -117,14 +172,13 @@
     ],
     static_libs: [
         "libandroid.hardware.biometrics.fingerprint.VirtualProps",
-        "android.hardware.biometrics.fingerprint-V5-ndk",
+        "android.hardware.biometrics.fingerprint-V4-ndk",
         "android.hardware.biometrics.common-V4-ndk",
         "android.hardware.keymaster-V4-ndk",
         "android.hardware.biometrics.common.util",
         "android.hardware.biometrics.common.thread",
         "android.hardware.biometrics.common.config",
     ],
-    vendor: true,
     test_suites: ["general-tests"],
     require_root: true,
 }
@@ -145,14 +199,13 @@
     ],
     static_libs: [
         "libandroid.hardware.biometrics.fingerprint.VirtualProps",
-        "android.hardware.biometrics.fingerprint-V5-ndk",
+        "android.hardware.biometrics.fingerprint-V4-ndk",
         "android.hardware.biometrics.common-V4-ndk",
         "android.hardware.keymaster-V4-ndk",
         "android.hardware.biometrics.common.util",
         "android.hardware.biometrics.common.thread",
         "android.hardware.biometrics.common.config",
     ],
-    vendor: true,
     test_suites: ["general-tests"],
     require_root: true,
 }
@@ -178,7 +231,8 @@
     ],
     static_libs: [
         "libandroid.hardware.biometrics.fingerprint.VirtualProps",
-        "android.hardware.biometrics.fingerprint-V5-ndk",
+        "android.hardware.biometrics.fingerprint-V4-ndk",
+        "android.hardware.biometrics.fingerprint.virtualhal-ndk",
         "android.hardware.biometrics.common-V4-ndk",
         "android.hardware.keymaster-V4-ndk",
         "android.hardware.biometrics.common.util",
@@ -190,7 +244,6 @@
             cflags: ["-DFPS_DEBUGGABLE"],
         },
     },
-    vendor: true,
     test_suites: ["general-tests"],
     require_root: true,
 }
@@ -198,39 +251,34 @@
 sysprop_library {
     name: "android.hardware.biometrics.fingerprint.VirtualProps",
     srcs: ["fingerprint.sysprop"],
-    property_owner: "Vendor",
-    vendor: true,
+    property_owner: "Platform",
+    vendor_available: true,
+    apex_available: [
+        "com.android.hardware.biometrics.fingerprint.virtual",
+        "//apex_available:platform",
+    ],
 }
 
 prebuilt_etc {
-    name: "fingerprint-example.rc",
-    src: "fingerprint-example.rc",
-    installable: false,
-}
-
-prebuilt_etc {
-    name: "fingerprint-example.xml",
-    src: "fingerprint-example.xml",
-    sub_dir: "vintf",
+    name: "fingerprint-virtual.rc",
+    src: "fingerprint-virtual.rc",
     installable: false,
 }
 
 apex {
     name: "com.android.hardware.biometrics.fingerprint.virtual",
     manifest: "apex_manifest.json",
-    file_contexts: "apex_file_contexts",
+    file_contexts: ":com.android.biometrics.virtual.fingerprint-file_contexts",
     key: "com.android.hardware.key",
     certificate: ":com.android.hardware.certificate",
     updatable: false,
-    vendor: true,
+    system_ext_specific: true,
 
     binaries: [
         "android.hardware.biometrics.fingerprint-service.example",
     ],
     prebuilts: [
         // init_rc
-        "fingerprint-example.rc",
-        // vintf_fragment
-        "fingerprint-example.xml",
+        "fingerprint-virtual.rc",
     ],
 }
diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
index 67eb837..7a43d7b 100644
--- a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
+++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
@@ -389,10 +389,10 @@
     if (isLockoutTimerStarted) isLockoutTimerAborted = true;
 }
 
-void FakeFingerprintEngine::clearLockout(ISessionCallback* cb) {
+void FakeFingerprintEngine::clearLockout(ISessionCallback* cb, bool dueToTimeout) {
     Fingerprint::cfg().set<bool>("lockout", false);
     cb->onLockoutCleared();
-    mLockoutTracker.reset();
+    mLockoutTracker.reset(dueToTimeout);
 }
 
 ndk::ScopedAStatus FakeFingerprintEngine::onPointerDownImpl(int32_t /*pointerId*/, int32_t /*x*/,
@@ -536,7 +536,7 @@
 void FakeFingerprintEngine::lockoutTimerExpired(ISessionCallback* cb) {
     BEGIN_OP(0);
     if (!isLockoutTimerAborted) {
-        clearLockout(cb);
+        clearLockout(cb, true);
     }
     isLockoutTimerStarted = false;
     isLockoutTimerAborted = false;
diff --git a/biometrics/fingerprint/aidl/default/FakeLockoutTracker.cpp b/biometrics/fingerprint/aidl/default/FakeLockoutTracker.cpp
index a056db5..7d46845 100644
--- a/biometrics/fingerprint/aidl/default/FakeLockoutTracker.cpp
+++ b/biometrics/fingerprint/aidl/default/FakeLockoutTracker.cpp
@@ -23,8 +23,11 @@
 
 namespace aidl::android::hardware::biometrics::fingerprint {
 
-void FakeLockoutTracker::reset() {
-    mFailedCount = 0;
+void FakeLockoutTracker::reset(bool dueToTimeout) {
+    if (!dueToTimeout) {
+        mFailedCount = 0;
+    }
+    mFailedCountTimed = 0;
     mLockoutTimedStart = 0;
     mCurrentMode = LockoutMode::kNone;
 }
@@ -33,6 +36,7 @@
     bool enabled = Fingerprint::cfg().get<bool>("lockout_enable");
     if (enabled) {
         mFailedCount++;
+        mFailedCountTimed++;
         int32_t lockoutTimedThreshold =
                 Fingerprint::cfg().get<std::int32_t>("lockout_timed_threshold");
         int32_t lockoutPermanetThreshold =
@@ -40,7 +44,7 @@
         if (mFailedCount >= lockoutPermanetThreshold) {
             mCurrentMode = LockoutMode::kPermanent;
             Fingerprint::cfg().set<bool>("lockout", true);
-        } else if (mFailedCount >= lockoutTimedThreshold) {
+        } else if (mFailedCountTimed >= lockoutTimedThreshold) {
             if (mCurrentMode == LockoutMode::kNone) {
                 mCurrentMode = LockoutMode::kTimed;
                 mLockoutTimedStart = Util::getSystemNanoTime();
diff --git a/biometrics/fingerprint/aidl/default/VirtualHal.cpp b/biometrics/fingerprint/aidl/default/VirtualHal.cpp
index e107d2f..d161765 100644
--- a/biometrics/fingerprint/aidl/default/VirtualHal.cpp
+++ b/biometrics/fingerprint/aidl/default/VirtualHal.cpp
@@ -26,7 +26,7 @@
 #define LOG_TAG "FingerprintVirtualHalAidl"
 
 namespace aidl::android::hardware::biometrics::fingerprint {
-
+using AcquiredInfoAndVendorCode = virtualhal::AcquiredInfoAndVendorCode;
 using Tag = AcquiredInfoAndVendorCode::Tag;
 
 ::ndk::ScopedAStatus VirtualHal::setEnrollments(const std::vector<int32_t>& enrollments) {
@@ -41,8 +41,7 @@
     return ndk::ScopedAStatus::ok();
 }
 
-::ndk::ScopedAStatus VirtualHal::setNextEnrollment(
-        const ::aidl::android::hardware::biometrics::fingerprint::NextEnrollment& next_enrollment) {
+::ndk::ScopedAStatus VirtualHal::setNextEnrollment(const NextEnrollment& next_enrollment) {
     Fingerprint::cfg().sourcedFromAidl();
     std::ostringstream os;
     os << next_enrollment.id << ":";
@@ -333,4 +332,10 @@
     return ndk::ScopedAStatus::ok();
 }
 
+::ndk::ScopedAStatus VirtualHal::getFingerprintHal(
+        std::shared_ptr<::aidl::android::hardware::biometrics::fingerprint::IFingerprint>* pFp) {
+    LOG(INFO) << " calling getFingerprintHal in VirtualHal.cpp";
+    *pFp = mFp;
+    return ndk::ScopedAStatus::ok();
+}
 }  // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt b/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt
index e69de29..8c02a68 100644
--- a/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt
+++ b/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt
@@ -0,0 +1,178 @@
+props {
+  owner: Vendor
+  module: "android.fingerprint.virt.FingerprintHalProperties"
+  prop {
+    api_name: "authenticator_id"
+    type: Long
+    access: ReadWrite
+    prop_name: "persist.vendor.fingerprint.virtual.authenticator_id"
+  }
+  prop {
+    api_name: "challenge"
+    type: Long
+    access: ReadWrite
+    prop_name: "vendor.fingerprint.virtual.challenge"
+  }
+  prop {
+    api_name: "control_illumination"
+    access: ReadWrite
+    prop_name: "persist.vendor.fingerprint.virtual.udfps.control_illumination"
+  }
+  prop {
+    api_name: "detect_interaction"
+    access: ReadWrite
+    prop_name: "persist.vendor.fingerprint.virtual.detect_interaction"
+  }
+  prop {
+    api_name: "display_touch"
+    access: ReadWrite
+    prop_name: "persist.vendor.fingerprint.virtual.udfps.display_touch"
+  }
+  prop {
+    api_name: "enrollment_hit"
+    type: Integer
+    access: ReadWrite
+    prop_name: "vendor.fingerprint.virtual.enrollment_hit"
+  }
+  prop {
+    api_name: "enrollments"
+    type: IntegerList
+    access: ReadWrite
+    prop_name: "persist.vendor.fingerprint.virtual.enrollments"
+  }
+  prop {
+    api_name: "lockout"
+    access: ReadWrite
+    prop_name: "persist.vendor.fingerprint.virtual.lockout"
+  }
+  prop {
+    api_name: "lockout_enable"
+    access: ReadWrite
+    prop_name: "persist.vendor.fingerprint.virtual.lockout_enable"
+  }
+  prop {
+    api_name: "lockout_permanent_threshold"
+    type: Integer
+    access: ReadWrite
+    prop_name: "persist.vendor.fingerprint.virtual.lockout_permanent_threshold"
+  }
+  prop {
+    api_name: "lockout_timed_duration"
+    type: Integer
+    access: ReadWrite
+    prop_name: "persist.vendor.fingerprint.virtual.lockout_timed_duration"
+  }
+  prop {
+    api_name: "lockout_timed_threshold"
+    type: Integer
+    access: ReadWrite
+    prop_name: "persist.vendor.fingerprint.virtual.lockout_timed_threshold"
+  }
+  prop {
+    api_name: "max_enrollments"
+    type: Integer
+    access: ReadWrite
+    prop_name: "persist.vendor.fingerprint.virtual.max_enrollments"
+  }
+  prop {
+    api_name: "navigation_guesture"
+    access: ReadWrite
+    prop_name: "persist.vendor.fingerprint.virtual.navigation_guesture"
+  }
+  prop {
+    api_name: "next_enrollment"
+    type: String
+    access: ReadWrite
+    prop_name: "vendor.fingerprint.virtual.next_enrollment"
+  }
+  prop {
+    api_name: "operation_authenticate_acquired"
+    type: String
+    access: ReadWrite
+    prop_name: "vendor.fingerprint.virtual.operation_authenticate_acquired"
+  }
+  prop {
+    api_name: "operation_authenticate_duration"
+    type: Integer
+    access: ReadWrite
+    prop_name: "vendor.fingerprint.virtual.operation_authenticate_duration"
+  }
+  prop {
+    api_name: "operation_authenticate_error"
+    type: Integer
+    access: ReadWrite
+    prop_name: "vendor.fingerprint.virtual.operation_authenticate_error"
+  }
+  prop {
+    api_name: "operation_authenticate_fails"
+    access: ReadWrite
+    prop_name: "vendor.fingerprint.virtual.operation_authenticate_fails"
+  }
+  prop {
+    api_name: "operation_authenticate_latency"
+    type: IntegerList
+    access: ReadWrite
+    prop_name: "vendor.fingerprint.virtual.operation_authenticate_latency"
+  }
+  prop {
+    api_name: "operation_detect_interaction_acquired"
+    type: String
+    access: ReadWrite
+    prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_acquired"
+  }
+  prop {
+    api_name: "operation_detect_interaction_duration"
+    type: Integer
+    access: ReadWrite
+    prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_duration"
+  }
+  prop {
+    api_name: "operation_detect_interaction_error"
+    type: Integer
+    access: ReadWrite
+    prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_error"
+  }
+  prop {
+    api_name: "operation_detect_interaction_latency"
+    type: IntegerList
+    access: ReadWrite
+    prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_latency"
+  }
+  prop {
+    api_name: "operation_enroll_error"
+    type: Integer
+    access: ReadWrite
+    prop_name: "vendor.fingerprint.virtual.operation_enroll_error"
+  }
+  prop {
+    api_name: "operation_enroll_latency"
+    type: IntegerList
+    access: ReadWrite
+    prop_name: "vendor.fingerprint.virtual.operation_enroll_latency"
+  }
+  prop {
+    api_name: "sensor_id"
+    type: Integer
+    access: ReadWrite
+    prop_name: "persist.vendor.fingerprint.virtual.sensor_id"
+  }
+  prop {
+    api_name: "sensor_location"
+    type: String
+    access: ReadWrite
+    prop_name: "persist.vendor.fingerprint.virtual.sensor_location"
+  }
+  prop {
+    api_name: "sensor_strength"
+    type: Integer
+    access: ReadWrite
+    prop_name: "persist.vendor.fingerprint.virtual.sensor_strength"
+  }
+  prop {
+    api_name: "type"
+    type: String
+    access: ReadWrite
+    prop_name: "persist.vendor.fingerprint.virtual.type"
+    enum_values: "default|rear|udfps|side"
+  }
+}
diff --git a/biometrics/fingerprint/aidl/default/fingerprint-default.rc b/biometrics/fingerprint/aidl/default/fingerprint-default.rc
new file mode 100644
index 0000000..7e46bc1
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/fingerprint-default.rc
@@ -0,0 +1,7 @@
+service vendor.fingerprint-default /vendor/bin/hw/android.hardware.biometrics.fingerprint-service.default default
+    class hal
+    user nobody
+    group nobody
+    interface aidl android.hardware.biometrics.fingerprint.IFingerprint/default
+    oneshot
+    disabled
diff --git a/biometrics/fingerprint/aidl/default/fingerprint-default.xml b/biometrics/fingerprint/aidl/default/fingerprint-default.xml
new file mode 100644
index 0000000..d140459
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/fingerprint-default.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.biometrics.fingerprint</name>
+        <version>4</version>
+        <interface>
+          <name>IFingerprint</name>
+          <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/biometrics/fingerprint/aidl/default/fingerprint-example.rc b/biometrics/fingerprint/aidl/default/fingerprint-example.rc
deleted file mode 100644
index da4ea45..0000000
--- a/biometrics/fingerprint/aidl/default/fingerprint-example.rc
+++ /dev/null
@@ -1,7 +0,0 @@
-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
-    interface aidl android.hardware.biometrics.fingerprint.IFingerprint/virtual
-    oneshot
-    disabled
diff --git a/biometrics/fingerprint/aidl/default/fingerprint-example.xml b/biometrics/fingerprint/aidl/default/fingerprint-example.xml
deleted file mode 100644
index ee529e9..0000000
--- a/biometrics/fingerprint/aidl/default/fingerprint-example.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<manifest version="1.0" type="device">
-    <hal format="aidl">
-        <name>android.hardware.biometrics.fingerprint</name>
-        <version>5</version>
-        <fqname>IFingerprint/virtual</fqname>
-    </hal>
-</manifest>
diff --git a/biometrics/fingerprint/aidl/default/fingerprint-virtual.rc b/biometrics/fingerprint/aidl/default/fingerprint-virtual.rc
new file mode 100644
index 0000000..5d1506c
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/fingerprint-virtual.rc
@@ -0,0 +1,7 @@
+service fingerprint-virtual /apex/com.android.hardware.biometrics.fingerprint.virtual/bin/hw/android.hardware.biometrics.fingerprint-service.example virtual
+    class hal
+    user nobody
+    group nobody
+    interface aidl android.hardware.biometrics.fingerprint.virtualhal.IVirtualHal/virtual
+    oneshot
+    disabled
diff --git a/biometrics/fingerprint/aidl/default/fingerprint.sysprop b/biometrics/fingerprint/aidl/default/fingerprint.sysprop
index 6a6c297..eb33432 100644
--- a/biometrics/fingerprint/aidl/default/fingerprint.sysprop
+++ b/biometrics/fingerprint/aidl/default/fingerprint.sysprop
@@ -7,7 +7,7 @@
 prop {
     prop_name: "persist.vendor.fingerprint.virtual.type"
     type: String
-    scope: Internal
+    scope: Public
     access: ReadWrite
     enum_values: "default|rear|udfps|side"
     api_name: "type"
@@ -17,7 +17,7 @@
 prop {
     prop_name: "persist.vendor.fingerprint.virtual.enrollments"
     type: IntegerList
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "enrollments"
 }
@@ -27,7 +27,7 @@
 prop {
     prop_name: "vendor.fingerprint.virtual.enrollment_hit"
     type: Integer
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "enrollment_hit"
 }
@@ -42,7 +42,7 @@
 prop {
     prop_name: "vendor.fingerprint.virtual.next_enrollment"
     type: String
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "next_enrollment"
 }
@@ -51,7 +51,7 @@
 prop {
     prop_name: "persist.vendor.fingerprint.virtual.authenticator_id"
     type: Long
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "authenticator_id"
 }
@@ -60,7 +60,7 @@
 prop {
     prop_name: "vendor.fingerprint.virtual.challenge"
     type: Long
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "challenge"
 }
@@ -69,7 +69,7 @@
 prop {
     prop_name: "vendor.fingerprint.virtual.operation_authenticate_fails"
     type: Boolean
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "operation_authenticate_fails"
 }
@@ -82,7 +82,7 @@
 prop {
     prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_error"
     type: Integer
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "operation_detect_interaction_error"
 }
@@ -91,7 +91,7 @@
 prop {
     prop_name: "vendor.fingerprint.virtual.operation_enroll_error"
     type: Integer
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "operation_enroll_error"
 }
@@ -104,7 +104,7 @@
 prop {
     prop_name: "vendor.fingerprint.virtual.operation_authenticate_latency"
     type: IntegerList
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "operation_authenticate_latency"
 }
@@ -114,7 +114,7 @@
 prop {
     prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_latency"
     type: IntegerList
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "operation_detect_interaction_latency"
 }
@@ -124,7 +124,7 @@
 prop {
     prop_name: "vendor.fingerprint.virtual.operation_enroll_latency"
     type: IntegerList
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "operation_enroll_latency"
 }
@@ -134,7 +134,7 @@
 prop {
     prop_name: "vendor.fingerprint.virtual.operation_authenticate_duration"
     type: Integer
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "operation_authenticate_duration"
 }
@@ -143,7 +143,7 @@
 prop {
     prop_name: "vendor.fingerprint.virtual.operation_authenticate_error"
     type: Integer
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "operation_authenticate_error"
 }
@@ -153,7 +153,7 @@
 prop {
     prop_name: "persist.vendor.fingerprint.virtual.sensor_location"
     type: String
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "sensor_location"
 }
@@ -162,7 +162,7 @@
 prop {
     prop_name: "vendor.fingerprint.virtual.operation_authenticate_acquired"
     type: String
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "operation_authenticate_acquired"
 }
@@ -172,7 +172,7 @@
 prop {
     prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_duration"
     type: Integer
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "operation_detect_interaction_duration"
 }
@@ -184,7 +184,7 @@
 prop {
     prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_acquired"
     type: String
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "operation_detect_interaction_acquired"
 }
@@ -193,7 +193,7 @@
 prop {
     prop_name: "persist.vendor.fingerprint.virtual.sensor_id"
     type: Integer
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "sensor_id"
 }
@@ -203,7 +203,7 @@
 prop {
     prop_name: "persist.vendor.fingerprint.virtual.sensor_strength"
     type: Integer
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "sensor_strength"
 }
@@ -213,7 +213,7 @@
 prop {
     prop_name: "persist.vendor.fingerprint.virtual.max_enrollments"
     type: Integer
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "max_enrollments"
 }
@@ -222,7 +222,7 @@
 prop {
     prop_name: "persist.vendor.fingerprint.virtual.navigation_guesture"
     type: Boolean
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "navigation_guesture"
 }
@@ -231,7 +231,7 @@
 prop {
     prop_name: "persist.vendor.fingerprint.virtual.detect_interaction"
     type: Boolean
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "detect_interaction"
 }
@@ -240,7 +240,7 @@
 prop {
     prop_name: "persist.vendor.fingerprint.virtual.udfps.display_touch"
     type: Boolean
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "display_touch"
 }
@@ -249,7 +249,7 @@
 prop {
     prop_name: "persist.vendor.fingerprint.virtual.udfps.control_illumination"
     type: Boolean
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "control_illumination"
 }
@@ -258,7 +258,7 @@
 prop {
     prop_name: "persist.vendor.fingerprint.virtual.lockout"
     type: Boolean
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "lockout"
 }
@@ -267,7 +267,7 @@
 prop {
     prop_name: "persist.vendor.fingerprint.virtual.lockout_enable"
     type: Boolean
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "lockout_enable"
 }
@@ -276,7 +276,7 @@
 prop {
     prop_name: "persist.vendor.fingerprint.virtual.lockout_timed_threshold"
     type: Integer
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "lockout_timed_threshold"
 }
@@ -285,7 +285,7 @@
 prop {
     prop_name: "persist.vendor.fingerprint.virtual.lockout_timed_duration"
     type: Integer
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "lockout_timed_duration"
 }
@@ -294,7 +294,7 @@
 prop {
     prop_name: "persist.vendor.fingerprint.virtual.lockout_permanent_threshold"
     type: Integer
-    scope: Internal
+    scope: Public
     access: ReadWrite
     api_name: "lockout_permanent_threshold"
 }
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
index 0d53575..362d0df 100644
--- a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
@@ -110,7 +110,7 @@
     std::pair<Error, int32_t> convertError(int32_t code);
     int32_t getRandomInRange(int32_t bound1, int32_t bound2);
     bool checkSensorLockout(ISessionCallback*);
-    void clearLockout(ISessionCallback* cb);
+    void clearLockout(ISessionCallback* cb, bool dueToTimeout = false);
     void waitForFingerDown(ISessionCallback* cb, const std::future<void>& cancel);
 
     FakeLockoutTracker mLockoutTracker;
diff --git a/biometrics/fingerprint/aidl/default/include/FakeLockoutTracker.h b/biometrics/fingerprint/aidl/default/include/FakeLockoutTracker.h
index a1b6128..a7f2f8e 100644
--- a/biometrics/fingerprint/aidl/default/include/FakeLockoutTracker.h
+++ b/biometrics/fingerprint/aidl/default/include/FakeLockoutTracker.h
@@ -24,12 +24,12 @@
 
 class FakeLockoutTracker {
   public:
-    FakeLockoutTracker() : mFailedCount(0) {}
+    FakeLockoutTracker() : mFailedCount(0), mFailedCountTimed(0) {}
     ~FakeLockoutTracker() {}
 
     enum class LockoutMode : int8_t { kNone = 0, kTimed, kPermanent };
 
-    void reset();
+    void reset(bool dueToTimeout = false);
     LockoutMode getMode();
     void addFailedAttempt();
     int64_t getLockoutTimeLeft();
@@ -44,6 +44,7 @@
 
   private:
     int32_t mFailedCount;
+    int32_t mFailedCountTimed;
     int64_t mLockoutTimedStart;
     LockoutMode mCurrentMode;
 };
diff --git a/biometrics/fingerprint/aidl/default/include/VirtualHal.h b/biometrics/fingerprint/aidl/default/include/VirtualHal.h
index e5f62fc..5488383 100644
--- a/biometrics/fingerprint/aidl/default/include/VirtualHal.h
+++ b/biometrics/fingerprint/aidl/default/include/VirtualHal.h
@@ -16,21 +16,21 @@
 
 #pragma once
 
-#include <aidl/android/hardware/biometrics/fingerprint/BnVirtualHal.h>
+#include <aidl/android/hardware/biometrics/fingerprint/virtualhal/BnVirtualHal.h>
 
 #include "Fingerprint.h"
 
 namespace aidl::android::hardware::biometrics::fingerprint {
 
+using namespace virtualhal;
+
 class VirtualHal : public BnVirtualHal {
   public:
-    VirtualHal(Fingerprint* fp) : mFp(fp) {}
+    VirtualHal(std::shared_ptr<Fingerprint> fp) : mFp(fp) {}
 
     ::ndk::ScopedAStatus setEnrollments(const std::vector<int32_t>& in_id) override;
     ::ndk::ScopedAStatus setEnrollmentHit(int32_t in_hit_id) override;
-    ::ndk::ScopedAStatus setNextEnrollment(
-            const ::aidl::android::hardware::biometrics::fingerprint::NextEnrollment&
-                    in_next_enrollment) override;
+    ::ndk::ScopedAStatus setNextEnrollment(const NextEnrollment& in_next_enrollment) override;
     ::ndk::ScopedAStatus setAuthenticatorId(int64_t in_id) override;
     ::ndk::ScopedAStatus setChallenge(int64_t in_challenge) override;
     ::ndk::ScopedAStatus setOperationAuthenticateFails(bool in_fail) override;
@@ -67,12 +67,15 @@
     ::ndk::ScopedAStatus setDetectInteraction(bool in_v) override;
     ::ndk::ScopedAStatus setDisplayTouch(bool in_v) override;
     ::ndk::ScopedAStatus setControlIllumination(bool in_v) override;
+    ::ndk::ScopedAStatus getFingerprintHal(
+            std::shared_ptr<::aidl::android::hardware::biometrics::fingerprint::IFingerprint>*
+                    _aidl_return);
 
   private:
     OptIntVec intVec2OptIntVec(const std::vector<int32_t>& intVec);
     OptIntVec acquiredInfoVec2OptIntVec(const std::vector<AcquiredInfoAndVendorCode>& intVec);
     ::ndk::ScopedAStatus sanityCheckLatency(const std::vector<int32_t>& in_latency);
-    Fingerprint* mFp;
+    std::shared_ptr<Fingerprint> mFp;
 };
 
 }  // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/main.cpp b/biometrics/fingerprint/aidl/default/main.cpp
index ba0c8ec..8ca44d6 100644
--- a/biometrics/fingerprint/aidl/default/main.cpp
+++ b/biometrics/fingerprint/aidl/default/main.cpp
@@ -24,21 +24,38 @@
 using aidl::android::hardware::biometrics::fingerprint::Fingerprint;
 using aidl::android::hardware::biometrics::fingerprint::VirtualHal;
 
-int main() {
-    LOG(INFO) << "Fingerprint HAL started";
+int main(int argc, char** argv) {
+    if (argc < 2) {
+        LOG(ERROR) << "Missing argument -> exiting";
+        return EXIT_FAILURE;
+    }
+
+    LOG(INFO) << "Fingerprint HAL started: " << argv[1];
     ABinderProcess_setThreadPoolMaxThreadCount(0);
     std::shared_ptr<Fingerprint> hal = ndk::SharedRefBase::make<Fingerprint>();
-    auto binder = hal->asBinder();
-
-    std::shared_ptr<VirtualHal> hal_ext = ndk::SharedRefBase::make<VirtualHal>(hal.get());
-    auto binder_ext = hal_ext->asBinder();
+    std::shared_ptr<VirtualHal> hal_vhal = ndk::SharedRefBase::make<VirtualHal>(hal);
 
     if (hal->connected()) {
-        CHECK(STATUS_OK == AIBinder_setExtension(binder.get(), binder_ext.get()));
-        const std::string instance = std::string(Fingerprint::descriptor) + "/virtual";
-        binder_status_t status =
-                AServiceManager_registerLazyService(binder.get(), instance.c_str());
-        CHECK_EQ(status, STATUS_OK);
+        if (strcmp(argv[1], "default") == 0) {
+            const std::string instance = std::string(Fingerprint::descriptor) + "/default";
+            auto binder = hal->asBinder();
+            auto binder_ext = hal_vhal->asBinder();
+            CHECK(STATUS_OK == AIBinder_setExtension(binder.get(), binder_ext.get()));
+            binder_status_t status =
+                    AServiceManager_registerLazyService(binder.get(), instance.c_str());
+            CHECK_EQ(status, STATUS_OK);
+            LOG(INFO) << "started IFingerprint/default";
+        } else if (strcmp(argv[1], "virtual") == 0) {
+            const std::string instance = std::string(VirtualHal::descriptor) + "/virtual";
+            auto binder = hal_vhal->asBinder();
+            binder_status_t status =
+                    AServiceManager_registerLazyService(binder.get(), instance.c_str());
+            CHECK_EQ(status, STATUS_OK);
+            LOG(INFO) << "started IVirtualHal/virtual";
+        } else {
+            LOG(ERROR) << "Unexpected argument: " << argv[1];
+            return EXIT_FAILURE;
+        }
         AServiceManager_forceLazyServicesPersist(true);
     } else {
         LOG(ERROR) << "Fingerprint HAL is not connected";
diff --git a/biometrics/fingerprint/aidl/default/tests/FakeLockoutTrackerTest.cpp b/biometrics/fingerprint/aidl/default/tests/FakeLockoutTrackerTest.cpp
index 3c12b6d..4c1277b 100644
--- a/biometrics/fingerprint/aidl/default/tests/FakeLockoutTrackerTest.cpp
+++ b/biometrics/fingerprint/aidl/default/tests/FakeLockoutTrackerTest.cpp
@@ -75,7 +75,7 @@
         prevTimeLeft = currTimeLeft;
     }
     ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kNone);
-    mLockoutTracker.reset();
+    mLockoutTracker.reset(true);
 }
 
 TEST_F(FakeLockoutTrackerTest, addFailedAttemptPermanent) {
diff --git a/biometrics/fingerprint/aidl/default/tests/VirtualHalTest.cpp b/biometrics/fingerprint/aidl/default/tests/VirtualHalTest.cpp
index 3fe0b2a..8ffc96b 100644
--- a/biometrics/fingerprint/aidl/default/tests/VirtualHalTest.cpp
+++ b/biometrics/fingerprint/aidl/default/tests/VirtualHalTest.cpp
@@ -35,7 +35,7 @@
   protected:
     void SetUp() override {
         mHal = ndk::SharedRefBase::make<Fingerprint>();
-        mVhal = ndk::SharedRefBase::make<VirtualHal>(mHal.get());
+        mVhal = ndk::SharedRefBase::make<VirtualHal>(mHal);
         ASSERT_TRUE(mVhal != nullptr);
         mHal->resetConfigToDefault();
     }
diff --git a/biometrics/fingerprint/aidl/vts/Android.bp b/biometrics/fingerprint/aidl/vts/Android.bp
index fc32fe6..628f03f 100644
--- a/biometrics/fingerprint/aidl/vts/Android.bp
+++ b/biometrics/fingerprint/aidl/vts/Android.bp
@@ -16,8 +16,8 @@
     ],
     srcs: ["VtsHalBiometricsFingerprintTargetTest.cpp"],
     static_libs: [
-        "android.hardware.biometrics.common-V3-ndk",
-        "android.hardware.biometrics.fingerprint-V3-ndk",
+        "android.hardware.biometrics.common-V4-ndk",
+        "android.hardware.biometrics.fingerprint-V4-ndk",
         "android.hardware.keymaster-V4-ndk",
     ],
     shared_libs: [
diff --git a/camera/common/aidl/Android.bp b/camera/common/aidl/Android.bp
index 8f7d19d..b59c92e 100644
--- a/camera/common/aidl/Android.bp
+++ b/camera/common/aidl/Android.bp
@@ -10,6 +10,7 @@
 
 aidl_interface {
     name: "android.hardware.camera.common",
+    host_supported: true,
     vendor_available: true,
     srcs: ["android/hardware/camera/common/*.aidl"],
     frozen: true,
diff --git a/camera/device/aidl/Android.bp b/camera/device/aidl/Android.bp
index 31c2dbe..48ae34e 100644
--- a/camera/device/aidl/Android.bp
+++ b/camera/device/aidl/Android.bp
@@ -10,6 +10,7 @@
 
 aidl_interface {
     name: "android.hardware.camera.device",
+    host_supported: true,
     vendor_available: true,
     srcs: ["android/hardware/camera/device/*.aidl"],
     frozen: true,
@@ -31,6 +32,9 @@
             sdk_version: "module_current",
             enabled: false,
         },
+        rust: {
+            enabled: true,
+        },
     },
     versions_with_info: [
         {
diff --git a/camera/device/aidl/android/hardware/camera/device/ICameraDeviceSession.aidl b/camera/device/aidl/android/hardware/camera/device/ICameraDeviceSession.aidl
index 62a19cf..63ae320 100644
--- a/camera/device/aidl/android/hardware/camera/device/ICameraDeviceSession.aidl
+++ b/camera/device/aidl/android/hardware/camera/device/ICameraDeviceSession.aidl
@@ -575,6 +575,11 @@
      * This can be called at any point after 'processCaptureRequest' in response
      * to camera clients disabling an active repeating request.
      *
+     * Note: The frame number parameter is the latest possible frame number at which the
+     * ongoing repeating request will end. It is possible that the repeating request may end
+     * before the specified frame number due to reasons such as the camera client abandoning
+     * buffers, which is timing dependent.
+     *
      * Performance requirements:
      * The call must not be blocked for extensive periods and should be extremely lightweight. There
      * must be no frame rate degradation or frame jitter introduced.
diff --git a/camera/metadata/aidl/Android.bp b/camera/metadata/aidl/Android.bp
index ae8ba14..a9c1a1a 100644
--- a/camera/metadata/aidl/Android.bp
+++ b/camera/metadata/aidl/Android.bp
@@ -10,6 +10,7 @@
 
 aidl_interface {
     name: "android.hardware.camera.metadata",
+    host_supported: true,
     vendor_available: true,
     srcs: ["android/hardware/camera/metadata/*.aidl"],
     frozen: true,
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index 82666ae..49c8410 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -52,6 +52,7 @@
 #include <android/hardware/camera/provider/2.7/ICameraProvider.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <binder/MemoryHeapBase.h>
+#include <com_android_graphics_libgui_flags.h>
 #include <cutils/properties.h>
 #include <fmq/MessageQueue.h>
 #include <grallocusage/GrallocUsageConversion.h>
@@ -8714,16 +8715,25 @@
     ASSERT_NE(nullptr, bufferItemConsumer);
     ASSERT_NE(nullptr, bufferHandler);
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+    *bufferItemConsumer = new BufferItemConsumer(
+            GraphicBuffer::USAGE_HW_TEXTURE);  // Use GLConsumer default usage flags
+#else
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
     *bufferItemConsumer = new BufferItemConsumer(consumer,
             GraphicBuffer::USAGE_HW_TEXTURE); //Use GLConsumer default usage flags
+#endif  // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
     ASSERT_NE(nullptr, (*bufferItemConsumer).get());
     *bufferHandler = new BufferItemHander(*bufferItemConsumer);
     ASSERT_NE(nullptr, (*bufferHandler).get());
     (*bufferItemConsumer)->setFrameAvailableListener(*bufferHandler);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+    sp<Surface> surface = (*bufferItemConsumer)->getSurface();
+#else
     sp<Surface> surface = new Surface(producer);
+#endif  // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
     sp<PreviewWindowCb> previewCb = new PreviewWindowCb(surface);
 
     auto rc = device->setPreviewWindow(previewCb);
diff --git a/camera/provider/aidl/Android.bp b/camera/provider/aidl/Android.bp
index 38a8936..faad35a 100644
--- a/camera/provider/aidl/Android.bp
+++ b/camera/provider/aidl/Android.bp
@@ -10,6 +10,7 @@
 
 aidl_interface {
     name: "android.hardware.camera.provider",
+    host_supported: true,
     vendor_available: true,
     srcs: [
         "android/hardware/camera/provider/*.aidl",
@@ -27,6 +28,9 @@
         cpp: {
             enabled: false,
         },
+        rust: {
+            enabled: true,
+        },
     },
     versions_with_info: [
         {
diff --git a/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp b/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
index ad8d4c8..9fa4df2 100644
--- a/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
+++ b/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
@@ -165,26 +165,21 @@
 
 // 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);
-            ret = mSession->close();
-            mSession = nullptr;
-            ASSERT_TRUE(ret.isOk());
-        }
-    } else {
-        ALOGI("validateManualFlashStrengthControlKeys: Test skipped.\n");
-        GTEST_SKIP();
+    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);
+        ret = mSession->close();
+        mSession = nullptr;
+        ASSERT_TRUE(ret.isOk());
     }
 }
 
@@ -288,77 +283,70 @@
 }
 
 TEST_P(CameraAidlTest, getSessionCharacteristics) {
-    if (flags::feature_combination_query()) {
-        std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider);
+    std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider);
 
-        for (const auto& name : cameraDeviceNames) {
-            std::shared_ptr<ICameraDevice> device;
-            ALOGI("getSessionCharacteristics: Testing camera device %s", name.c_str());
-            ndk::ScopedAStatus ret = mProvider->getCameraDeviceInterface(name, &device);
-            ALOGI("getCameraDeviceInterface returns: %d:%d", ret.getExceptionCode(),
-                  ret.getServiceSpecificError());
-            ASSERT_TRUE(ret.isOk());
-            ASSERT_NE(device, nullptr);
+    for (const auto& name : cameraDeviceNames) {
+        std::shared_ptr<ICameraDevice> device;
+        ALOGI("getSessionCharacteristics: Testing camera device %s", name.c_str());
+        ndk::ScopedAStatus ret = mProvider->getCameraDeviceInterface(name, &device);
+        ALOGI("getCameraDeviceInterface returns: %d:%d", ret.getExceptionCode(),
+              ret.getServiceSpecificError());
+        ASSERT_TRUE(ret.isOk());
+        ASSERT_NE(device, nullptr);
 
-            int32_t interfaceVersion = -1;
-            ret = device->getInterfaceVersion(&interfaceVersion);
-            ASSERT_TRUE(ret.isOk());
-            bool supportSessionCharacteristics =
-                    (interfaceVersion >= CAMERA_DEVICE_API_MINOR_VERSION_3);
-            if (!supportSessionCharacteristics) {
-                continue;
-            }
-
-            CameraMetadata meta;
-            openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/,
-                                   &device /*out*/);
-
-            std::vector<AvailableStream> outputStreams;
-            camera_metadata_t* staticMeta =
-                    reinterpret_cast<camera_metadata_t*>(meta.metadata.data());
-            outputStreams.clear();
-            ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta, outputStreams));
-            ASSERT_NE(0u, outputStreams.size());
-
-            AvailableStream sampleStream = outputStreams[0];
-
-            int32_t streamId = 0;
-            Stream stream = {streamId,
-                             StreamType::OUTPUT,
-                             sampleStream.width,
-                             sampleStream.height,
-                             static_cast<PixelFormat>(sampleStream.format),
-                             static_cast<aidl::android::hardware::graphics::common::BufferUsage>(
-                                     GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER),
-                             Dataspace::UNKNOWN,
-                             StreamRotation::ROTATION_0,
-                             std::string(),
-                             /*bufferSize*/ 0,
-                             /*groupId*/ -1,
-                             {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT},
-                             RequestAvailableDynamicRangeProfilesMap::
-                                     ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD};
-
-            std::vector<Stream> streams = {stream};
-            StreamConfiguration config;
-            createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config);
-
-            CameraMetadata camera_chars;
-            ret = device->getCameraCharacteristics(&camera_chars);
-            ASSERT_TRUE(ret.isOk());
-
-            CameraMetadata session_chars;
-            ret = device->getSessionCharacteristics(config, &session_chars);
-            ASSERT_TRUE(ret.isOk());
-            verifySessionCharacteristics(session_chars, camera_chars);
-
-            ret = mSession->close();
-            mSession = nullptr;
-            ASSERT_TRUE(ret.isOk());
+        int32_t interfaceVersion = -1;
+        ret = device->getInterfaceVersion(&interfaceVersion);
+        ASSERT_TRUE(ret.isOk());
+        bool supportSessionCharacteristics =
+                (interfaceVersion >= CAMERA_DEVICE_API_MINOR_VERSION_3);
+        if (!supportSessionCharacteristics) {
+            continue;
         }
-    } else {
-        ALOGI("getSessionCharacteristics: Test skipped.\n");
-        GTEST_SKIP();
+
+        CameraMetadata meta;
+        openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, &device /*out*/);
+
+        std::vector<AvailableStream> outputStreams;
+        camera_metadata_t* staticMeta = reinterpret_cast<camera_metadata_t*>(meta.metadata.data());
+        outputStreams.clear();
+        ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta, outputStreams));
+        ASSERT_NE(0u, outputStreams.size());
+
+        AvailableStream sampleStream = outputStreams[0];
+
+        int32_t streamId = 0;
+        Stream stream = {streamId,
+                         StreamType::OUTPUT,
+                         sampleStream.width,
+                         sampleStream.height,
+                         static_cast<PixelFormat>(sampleStream.format),
+                         static_cast<aidl::android::hardware::graphics::common::BufferUsage>(
+                                 GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER),
+                         Dataspace::UNKNOWN,
+                         StreamRotation::ROTATION_0,
+                         std::string(),
+                         /*bufferSize*/ 0,
+                         /*groupId*/ -1,
+                         {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT},
+                         RequestAvailableDynamicRangeProfilesMap::
+                                 ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD};
+
+        std::vector<Stream> streams = {stream};
+        StreamConfiguration config;
+        createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config);
+
+        CameraMetadata camera_chars;
+        ret = device->getCameraCharacteristics(&camera_chars);
+        ASSERT_TRUE(ret.isOk());
+
+        CameraMetadata session_chars;
+        ret = device->getSessionCharacteristics(config, &session_chars);
+        ASSERT_TRUE(ret.isOk());
+        verifySessionCharacteristics(session_chars, camera_chars);
+
+        ret = mSession->close();
+        mSession = nullptr;
+        ASSERT_TRUE(ret.isOk());
     }
 }
 
@@ -615,19 +603,17 @@
                 ASSERT_EQ(0u, rawMetadata.metadata.size());
             }
 
-            if (flags::feature_combination_query()) {
-                if (supportFeatureCombinationQuery) {
-                    CameraMetadata rawMetadata2;
-                    ndk::ScopedAStatus ret2 =
-                            device->constructDefaultRequestSettings(reqTemplate, &rawMetadata2);
+            if (supportFeatureCombinationQuery) {
+                CameraMetadata rawMetadata2;
+                ndk::ScopedAStatus ret2 =
+                        device->constructDefaultRequestSettings(reqTemplate, &rawMetadata2);
 
-                    ASSERT_EQ(ret.isOk(), ret2.isOk());
-                    ASSERT_EQ(ret.getStatus(), ret2.getStatus());
+                ASSERT_EQ(ret.isOk(), ret2.isOk());
+                ASSERT_EQ(ret.getStatus(), ret2.getStatus());
 
-                    ASSERT_EQ(rawMetadata.metadata.size(), rawMetadata2.metadata.size());
-                    if (ret2.isOk()) {
-                        validateDefaultRequestMetadata(reqTemplate, rawMetadata2);
-                    }
+                ASSERT_EQ(rawMetadata.metadata.size(), rawMetadata2.metadata.size());
+                if (ret2.isOk()) {
+                    validateDefaultRequestMetadata(reqTemplate, rawMetadata2);
                 }
             }
         }
diff --git a/camera/provider/aidl/vts/camera_aidl_test.cpp b/camera/provider/aidl/vts/camera_aidl_test.cpp
index f905011..6ecd451 100644
--- a/camera/provider/aidl/vts/camera_aidl_test.cpp
+++ b/camera/provider/aidl/vts/camera_aidl_test.cpp
@@ -1920,28 +1920,22 @@
         ASSERT_TRUE(ret.isOk());
         ASSERT_EQ(expectedStatus, streamCombinationSupported);
 
-        if (flags::feature_combination_query()) {
-            int32_t interfaceVersion;
-            ret = device->getInterfaceVersion(&interfaceVersion);
+        int32_t interfaceVersion;
+        ret = device->getInterfaceVersion(&interfaceVersion);
+        ASSERT_TRUE(ret.isOk());
+        bool supportFeatureCombinationQuery =
+                (interfaceVersion >= CAMERA_DEVICE_API_MINOR_VERSION_3);
+        if (supportFeatureCombinationQuery) {
+            ret = device->isStreamCombinationWithSettingsSupported(config,
+                                                                   &streamCombinationSupported);
             ASSERT_TRUE(ret.isOk());
-            bool supportFeatureCombinationQuery =
-                    (interfaceVersion >= CAMERA_DEVICE_API_MINOR_VERSION_3);
-            if (supportFeatureCombinationQuery) {
-                ret = device->isStreamCombinationWithSettingsSupported(config,
-                                                                       &streamCombinationSupported);
-                ASSERT_TRUE(ret.isOk());
-                ASSERT_EQ(expectedStatus, streamCombinationSupported);
-            }
+            ASSERT_EQ(expectedStatus, streamCombinationSupported);
         }
     }
 }
 
 void CameraAidlTest::verifySessionCharacteristics(const CameraMetadata& session_chars,
                                                   const CameraMetadata& camera_chars) {
-    if (!flags::feature_combination_query()) {
-        return;
-    }
-
     const camera_metadata_t* session_metadata =
             reinterpret_cast<const camera_metadata_t*>(session_chars.metadata.data());
 
diff --git a/compatibility_matrices/compatibility_matrix.202504.xml b/compatibility_matrices/compatibility_matrix.202504.xml
index 4b762ca..d04d3ad 100644
--- a/compatibility_matrices/compatibility_matrix.202504.xml
+++ b/compatibility_matrices/compatibility_matrix.202504.xml
@@ -249,7 +249,7 @@
     </hal>
     <hal format="aidl">
         <name>android.hardware.graphics.composer3</name>
-        <version>3</version>
+        <version>4</version>
         <interface>
             <name>IComposer</name>
             <instance>default</instance>
@@ -638,7 +638,7 @@
     </hal>
     <hal format="aidl">
         <name>android.hardware.vibrator</name>
-        <version>1-2</version>
+        <version>1-3</version>
         <interface>
             <name>IVibrator</name>
             <instance>default</instance>
@@ -646,7 +646,7 @@
     </hal>
     <hal format="aidl">
         <name>android.hardware.vibrator</name>
-        <version>1-2</version>
+        <version>1-3</version>
         <interface>
             <name>IVibratorManager</name>
             <instance>default</instance>
@@ -678,7 +678,7 @@
     </hal>
     <hal format="aidl">
         <name>android.hardware.wifi.hostapd</name>
-        <version>1-2</version>
+        <version>2-3</version>
         <interface>
             <name>IHostapd</name>
             <instance>default</instance>
@@ -686,7 +686,7 @@
     </hal>
     <hal format="aidl">
         <name>android.hardware.wifi.supplicant</name>
-        <version>2-3</version>
+        <version>3-4</version>
         <interface>
             <name>ISupplicant</name>
             <instance>default</instance>
diff --git a/compatibility_matrices/exclude/fcm_exclude.cpp b/compatibility_matrices/exclude/fcm_exclude.cpp
index 61fb2b3..eec5a75 100644
--- a/compatibility_matrices/exclude/fcm_exclude.cpp
+++ b/compatibility_matrices/exclude/fcm_exclude.cpp
@@ -149,6 +149,7 @@
             "android.hardware.radio@",
             "android.hardware.uwb.fira_android@",
             "android.hardware.wifi.common@",
+            "android.hardware.biometrics.fingerprint.virtualhal@",
 
             // Test packages are exempted.
             "android.hardware.tests.",
diff --git a/drm/aidl/Android.bp b/drm/aidl/Android.bp
index c42e723..827621c 100644
--- a/drm/aidl/Android.bp
+++ b/drm/aidl/Android.bp
@@ -15,6 +15,7 @@
     frozen: true,
     imports: [
         "android.hardware.common-V2",
+        "android.hardware.drm.common-V1",
     ],
     backend: {
         cpp: {
@@ -31,7 +32,10 @@
     versions_with_info: [
         {
             version: "1",
-            imports: ["android.hardware.common-V2"],
+            imports: [
+                "android.hardware.common-V2",
+                "android.hardware.drm.common-V1",
+            ],
         },
     ],
 
diff --git a/drm/aidl/aidl_api/android.hardware.drm/1/.hash b/drm/aidl/aidl_api/android.hardware.drm/1/.hash
index 886e28c..9a735e9 100644
--- a/drm/aidl/aidl_api/android.hardware.drm/1/.hash
+++ b/drm/aidl/aidl_api/android.hardware.drm/1/.hash
@@ -1 +1,2 @@
 7b4b0a0f36a7a6bb22d2016375e4a9d4a033592f
+3a0197fb44863256da9034c26e721b1eee12d1be
diff --git a/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/EventType.aidl b/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/EventType.aidl
index 80ebb28..f09eadd 100644
--- a/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/EventType.aidl
+++ b/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/EventType.aidl
@@ -34,9 +34,9 @@
 package android.hardware.drm;
 @Backing(type="int") @VintfStability
 enum EventType {
-  PROVISION_REQUIRED = 0,
-  KEY_NEEDED = 1,
-  KEY_EXPIRED = 2,
-  VENDOR_DEFINED = 3,
-  SESSION_RECLAIMED = 4,
+  PROVISION_REQUIRED,
+  KEY_NEEDED,
+  KEY_EXPIRED,
+  VENDOR_DEFINED,
+  SESSION_RECLAIMED,
 }
diff --git a/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/HdcpLevel.aidl b/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/HdcpLevel.aidl
deleted file mode 100644
index 5704fb0..0000000
--- a/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/HdcpLevel.aidl
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-///////////////////////////////////////////////////////////////////////////////
-// 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.drm;
-@Backing(type="int") @VintfStability
-enum HdcpLevel {
-  HDCP_UNKNOWN = 0,
-  HDCP_NONE = 1,
-  HDCP_V1 = 2,
-  HDCP_V2 = 3,
-  HDCP_V2_1 = 4,
-  HDCP_V2_2 = 5,
-  HDCP_NO_OUTPUT = 6,
-  HDCP_V2_3 = 7,
-}
diff --git a/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/KeyRequestType.aidl b/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/KeyRequestType.aidl
index 34b9615..556ee38 100644
--- a/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/KeyRequestType.aidl
+++ b/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/KeyRequestType.aidl
@@ -34,10 +34,10 @@
 package android.hardware.drm;
 @Backing(type="int") @VintfStability
 enum KeyRequestType {
-  INITIAL = 0,
-  RENEWAL = 1,
-  RELEASE = 2,
-  UNKNOWN = 3,
-  NONE = 4,
-  UPDATE = 5,
+  INITIAL,
+  RENEWAL,
+  RELEASE,
+  UNKNOWN,
+  NONE,
+  UPDATE,
 }
diff --git a/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/KeyStatusType.aidl b/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/KeyStatusType.aidl
index 261516f..5a46552 100644
--- a/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/KeyStatusType.aidl
+++ b/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/KeyStatusType.aidl
@@ -34,10 +34,10 @@
 package android.hardware.drm;
 @Backing(type="int") @VintfStability
 enum KeyStatusType {
-  USABLE = 0,
-  EXPIRED = 1,
-  OUTPUT_NOT_ALLOWED = 2,
-  STATUS_PENDING = 3,
-  INTERNAL_ERROR = 4,
-  USABLE_IN_FUTURE = 5,
+  USABLE,
+  EXPIRED,
+  OUTPUT_NOT_ALLOWED,
+  STATUS_PENDING,
+  INTERNAL_ERROR,
+  USABLE_IN_FUTURE,
 }
diff --git a/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/KeyType.aidl b/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/KeyType.aidl
index 7a9d633..e677c86 100644
--- a/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/KeyType.aidl
+++ b/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/KeyType.aidl
@@ -34,7 +34,7 @@
 package android.hardware.drm;
 @Backing(type="int") @VintfStability
 enum KeyType {
-  OFFLINE = 0,
-  STREAMING = 1,
-  RELEASE = 2,
+  OFFLINE,
+  STREAMING,
+  RELEASE,
 }
diff --git a/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/LogPriority.aidl b/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/LogPriority.aidl
index 83362c3..b77ddf6 100644
--- a/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/LogPriority.aidl
+++ b/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/LogPriority.aidl
@@ -34,12 +34,12 @@
 package android.hardware.drm;
 @Backing(type="int") @VintfStability
 enum LogPriority {
-  UNKNOWN = 0,
-  DEFAULT = 1,
-  VERBOSE = 2,
-  DEBUG = 3,
-  INFO = 4,
-  WARN = 5,
-  ERROR = 6,
-  FATAL = 7,
+  UNKNOWN,
+  DEFAULT,
+  VERBOSE,
+  DEBUG,
+  INFO,
+  WARN,
+  ERROR,
+  FATAL,
 }
diff --git a/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/OfflineLicenseState.aidl b/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/OfflineLicenseState.aidl
index 629564d..be0e822 100644
--- a/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/OfflineLicenseState.aidl
+++ b/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/OfflineLicenseState.aidl
@@ -34,7 +34,7 @@
 package android.hardware.drm;
 @Backing(type="int") @VintfStability
 enum OfflineLicenseState {
-  UNKNOWN = 0,
-  USABLE = 1,
-  INACTIVE = 2,
+  UNKNOWN,
+  USABLE,
+  INACTIVE,
 }
diff --git a/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/SecurityLevel.aidl b/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/SecurityLevel.aidl
index 65b2b9d..87b3641 100644
--- a/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/SecurityLevel.aidl
+++ b/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/SecurityLevel.aidl
@@ -34,11 +34,11 @@
 package android.hardware.drm;
 @Backing(type="int") @VintfStability
 enum SecurityLevel {
-  UNKNOWN = 0,
-  SW_SECURE_CRYPTO = 1,
-  SW_SECURE_DECODE = 2,
-  HW_SECURE_CRYPTO = 3,
-  HW_SECURE_DECODE = 4,
-  HW_SECURE_ALL = 5,
-  DEFAULT = 6,
+  UNKNOWN,
+  SW_SECURE_CRYPTO,
+  SW_SECURE_DECODE,
+  HW_SECURE_CRYPTO,
+  HW_SECURE_DECODE,
+  HW_SECURE_ALL,
+  DEFAULT,
 }
diff --git a/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/Status.aidl b/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/Status.aidl
index c640689..a3ba6c3 100644
--- a/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/Status.aidl
+++ b/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/Status.aidl
@@ -34,44 +34,44 @@
 package android.hardware.drm;
 @Backing(type="int") @VintfStability
 enum Status {
-  OK = 0,
-  ERROR_DRM_NO_LICENSE = 1,
-  ERROR_DRM_LICENSE_EXPIRED = 2,
-  ERROR_DRM_SESSION_NOT_OPENED = 3,
-  ERROR_DRM_CANNOT_HANDLE = 4,
-  ERROR_DRM_INVALID_STATE = 5,
-  BAD_VALUE = 6,
-  ERROR_DRM_NOT_PROVISIONED = 7,
-  ERROR_DRM_RESOURCE_BUSY = 8,
-  ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION = 9,
-  ERROR_DRM_DEVICE_REVOKED = 10,
-  ERROR_DRM_DECRYPT = 11,
-  ERROR_DRM_UNKNOWN = 12,
-  ERROR_DRM_INSUFFICIENT_SECURITY = 13,
-  ERROR_DRM_FRAME_TOO_LARGE = 14,
-  ERROR_DRM_SESSION_LOST_STATE = 15,
-  ERROR_DRM_RESOURCE_CONTENTION = 16,
-  CANNOT_DECRYPT_ZERO_SUBSAMPLES = 17,
-  CRYPTO_LIBRARY_ERROR = 18,
-  GENERAL_OEM_ERROR = 19,
-  GENERAL_PLUGIN_ERROR = 20,
-  INIT_DATA_INVALID = 21,
-  KEY_NOT_LOADED = 22,
-  LICENSE_PARSE_ERROR = 23,
-  LICENSE_POLICY_ERROR = 24,
-  LICENSE_RELEASE_ERROR = 25,
-  LICENSE_REQUEST_REJECTED = 26,
-  LICENSE_RESTORE_ERROR = 27,
-  LICENSE_STATE_ERROR = 28,
-  MALFORMED_CERTIFICATE = 29,
-  MEDIA_FRAMEWORK_ERROR = 30,
-  MISSING_CERTIFICATE = 31,
-  PROVISIONING_CERTIFICATE_ERROR = 32,
-  PROVISIONING_CONFIGURATION_ERROR = 33,
-  PROVISIONING_PARSE_ERROR = 34,
-  PROVISIONING_REQUEST_REJECTED = 35,
-  RETRYABLE_PROVISIONING_ERROR = 36,
-  SECURE_STOP_RELEASE_ERROR = 37,
-  STORAGE_READ_FAILURE = 38,
-  STORAGE_WRITE_FAILURE = 39,
+  OK,
+  ERROR_DRM_NO_LICENSE,
+  ERROR_DRM_LICENSE_EXPIRED,
+  ERROR_DRM_SESSION_NOT_OPENED,
+  ERROR_DRM_CANNOT_HANDLE,
+  ERROR_DRM_INVALID_STATE,
+  BAD_VALUE,
+  ERROR_DRM_NOT_PROVISIONED,
+  ERROR_DRM_RESOURCE_BUSY,
+  ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION,
+  ERROR_DRM_DEVICE_REVOKED,
+  ERROR_DRM_DECRYPT,
+  ERROR_DRM_UNKNOWN,
+  ERROR_DRM_INSUFFICIENT_SECURITY,
+  ERROR_DRM_FRAME_TOO_LARGE,
+  ERROR_DRM_SESSION_LOST_STATE,
+  ERROR_DRM_RESOURCE_CONTENTION,
+  CANNOT_DECRYPT_ZERO_SUBSAMPLES,
+  CRYPTO_LIBRARY_ERROR,
+  GENERAL_OEM_ERROR,
+  GENERAL_PLUGIN_ERROR,
+  INIT_DATA_INVALID,
+  KEY_NOT_LOADED,
+  LICENSE_PARSE_ERROR,
+  LICENSE_POLICY_ERROR,
+  LICENSE_RELEASE_ERROR,
+  LICENSE_REQUEST_REJECTED,
+  LICENSE_RESTORE_ERROR,
+  LICENSE_STATE_ERROR,
+  MALFORMED_CERTIFICATE,
+  MEDIA_FRAMEWORK_ERROR,
+  MISSING_CERTIFICATE,
+  PROVISIONING_CERTIFICATE_ERROR,
+  PROVISIONING_CONFIGURATION_ERROR,
+  PROVISIONING_PARSE_ERROR,
+  PROVISIONING_REQUEST_REJECTED,
+  RETRYABLE_PROVISIONING_ERROR,
+  SECURE_STOP_RELEASE_ERROR,
+  STORAGE_READ_FAILURE,
+  STORAGE_WRITE_FAILURE,
 }
diff --git a/drm/aidl/vts/Android.bp b/drm/aidl/vts/Android.bp
index e813bd1..1fe0972 100644
--- a/drm/aidl/vts/Android.bp
+++ b/drm/aidl/vts/Android.bp
@@ -48,6 +48,7 @@
     ],
     static_libs: [
         "android.hardware.drm@1.0-helper",
+        "android.hardware.drm.common-V1-ndk",
         "android.hardware.drm-V1-ndk",
         "android.hardware.common-V2-ndk",
         "libaidlcommonsupport",
@@ -60,13 +61,19 @@
             data: [":libvtswidevine-arm-prebuilts"],
         },
         arm64: {
-            data: [":libvtswidevine-arm64-prebuilts", ":libvtswidevine-arm-prebuilts"],
+            data: [
+                ":libvtswidevine-arm64-prebuilts",
+                ":libvtswidevine-arm-prebuilts",
+            ],
         },
         x86: {
             data: [":libvtswidevine-x86-prebuilts"],
         },
         x86_64: {
-            data: [":libvtswidevine-x86_64-prebuilts", ":libvtswidevine-x86-prebuilts"],
+            data: [
+                ":libvtswidevine-x86_64-prebuilts",
+                ":libvtswidevine-x86-prebuilts",
+            ],
         },
     },
     test_suites: [
diff --git a/drm/common/aidl/Android.bp b/drm/common/aidl/Android.bp
new file mode 100644
index 0000000..c5cb441
--- /dev/null
+++ b/drm/common/aidl/Android.bp
@@ -0,0 +1,39 @@
+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.drm.common",
+    host_supported: true,
+    vendor_available: true,
+    srcs: ["android/hardware/drm/*.aidl"],
+    stability: "vintf",
+    backend: {
+        cpp: {
+            enabled: false,
+        },
+        java: {
+            sdk_version: "module_current",
+        },
+        ndk: {
+            min_sdk_version: "34",
+        },
+        rust: {
+            enabled: true,
+        },
+    },
+    double_loadable: true,
+    versions_with_info: [
+        {
+            version: "1",
+            imports: [],
+        },
+    ],
+    frozen: true,
+
+}
diff --git a/drm/common/aidl/aidl_api/android.hardware.drm.common/1/.hash b/drm/common/aidl/aidl_api/android.hardware.drm.common/1/.hash
new file mode 100644
index 0000000..66690e1
--- /dev/null
+++ b/drm/common/aidl/aidl_api/android.hardware.drm.common/1/.hash
@@ -0,0 +1 @@
+1b5e9159609b3aa05e2c7158f3a1488fda2250d1
diff --git a/drm/aidl/aidl_api/android.hardware.drm/1/android/hardware/drm/HdcpLevel.aidl b/drm/common/aidl/aidl_api/android.hardware.drm.common/1/android/hardware/drm/HdcpLevel.aidl
similarity index 92%
rename from drm/aidl/aidl_api/android.hardware.drm/1/android/hardware/drm/HdcpLevel.aidl
rename to drm/common/aidl/aidl_api/android.hardware.drm.common/1/android/hardware/drm/HdcpLevel.aidl
index 5704fb0..118bef6 100644
--- a/drm/aidl/aidl_api/android.hardware.drm/1/android/hardware/drm/HdcpLevel.aidl
+++ b/drm/common/aidl/aidl_api/android.hardware.drm.common/1/android/hardware/drm/HdcpLevel.aidl
@@ -34,12 +34,12 @@
 package android.hardware.drm;
 @Backing(type="int") @VintfStability
 enum HdcpLevel {
-  HDCP_UNKNOWN = 0,
-  HDCP_NONE = 1,
-  HDCP_V1 = 2,
-  HDCP_V2 = 3,
-  HDCP_V2_1 = 4,
-  HDCP_V2_2 = 5,
-  HDCP_NO_OUTPUT = 6,
-  HDCP_V2_3 = 7,
+  HDCP_UNKNOWN,
+  HDCP_NONE,
+  HDCP_V1,
+  HDCP_V2,
+  HDCP_V2_1,
+  HDCP_V2_2,
+  HDCP_NO_OUTPUT,
+  HDCP_V2_3,
 }
diff --git a/drm/aidl/aidl_api/android.hardware.drm/1/android/hardware/drm/HdcpLevels.aidl b/drm/common/aidl/aidl_api/android.hardware.drm.common/1/android/hardware/drm/HdcpLevels.aidl
similarity index 100%
rename from drm/aidl/aidl_api/android.hardware.drm/1/android/hardware/drm/HdcpLevels.aidl
rename to drm/common/aidl/aidl_api/android.hardware.drm.common/1/android/hardware/drm/HdcpLevels.aidl
diff --git a/drm/aidl/aidl_api/android.hardware.drm/1/android/hardware/drm/HdcpLevel.aidl b/drm/common/aidl/aidl_api/android.hardware.drm.common/current/android/hardware/drm/HdcpLevel.aidl
similarity index 92%
copy from drm/aidl/aidl_api/android.hardware.drm/1/android/hardware/drm/HdcpLevel.aidl
copy to drm/common/aidl/aidl_api/android.hardware.drm.common/current/android/hardware/drm/HdcpLevel.aidl
index 5704fb0..118bef6 100644
--- a/drm/aidl/aidl_api/android.hardware.drm/1/android/hardware/drm/HdcpLevel.aidl
+++ b/drm/common/aidl/aidl_api/android.hardware.drm.common/current/android/hardware/drm/HdcpLevel.aidl
@@ -34,12 +34,12 @@
 package android.hardware.drm;
 @Backing(type="int") @VintfStability
 enum HdcpLevel {
-  HDCP_UNKNOWN = 0,
-  HDCP_NONE = 1,
-  HDCP_V1 = 2,
-  HDCP_V2 = 3,
-  HDCP_V2_1 = 4,
-  HDCP_V2_2 = 5,
-  HDCP_NO_OUTPUT = 6,
-  HDCP_V2_3 = 7,
+  HDCP_UNKNOWN,
+  HDCP_NONE,
+  HDCP_V1,
+  HDCP_V2,
+  HDCP_V2_1,
+  HDCP_V2_2,
+  HDCP_NO_OUTPUT,
+  HDCP_V2_3,
 }
diff --git a/drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/HdcpLevels.aidl b/drm/common/aidl/aidl_api/android.hardware.drm.common/current/android/hardware/drm/HdcpLevels.aidl
similarity index 100%
rename from drm/aidl/aidl_api/android.hardware.drm/current/android/hardware/drm/HdcpLevels.aidl
rename to drm/common/aidl/aidl_api/android.hardware.drm.common/current/android/hardware/drm/HdcpLevels.aidl
diff --git a/drm/aidl/android/hardware/drm/HdcpLevel.aidl b/drm/common/aidl/android/hardware/drm/HdcpLevel.aidl
similarity index 100%
rename from drm/aidl/android/hardware/drm/HdcpLevel.aidl
rename to drm/common/aidl/android/hardware/drm/HdcpLevel.aidl
diff --git a/drm/aidl/android/hardware/drm/HdcpLevels.aidl b/drm/common/aidl/android/hardware/drm/HdcpLevels.aidl
similarity index 100%
rename from drm/aidl/android/hardware/drm/HdcpLevels.aidl
rename to drm/common/aidl/android/hardware/drm/HdcpLevels.aidl
diff --git a/gnss/aidl/android/hardware/gnss/IGnss.aidl b/gnss/aidl/android/hardware/gnss/IGnss.aidl
index 8a22d6e..aaafe7f 100644
--- a/gnss/aidl/android/hardware/gnss/IGnss.aidl
+++ b/gnss/aidl/android/hardware/gnss/IGnss.aidl
@@ -217,10 +217,6 @@
      * Starts a location output stream using the IGnssCallback gnssLocationCb(), following the
      * settings from the most recent call to setPositionMode().
      *
-     * When a location output stream is in progress, calling setPositionMode() does not change the
-     * settings of the current location output stream. stop() and start() must be called to make the
-     * new settings effective.
-     *
      * This output must operate independently of any GNSS location batching operations,
      * see the IGnssBatching for details.
      */
@@ -310,10 +306,6 @@
     /**
      * Sets the GnssPositionMode parameter, its associated recurrence value, the time between fixes,
      * requested fix accuracy, time to first fix.
-     *
-     * If a location output stream is in progress, calling this method does not affect the settings
-     * of current location output stream. stop() and start() must be called to make the new settings
-     * effective.
      */
     void setPositionMode(in PositionModeOptions options);
 
diff --git a/graphics/Android.bp b/graphics/Android.bp
index ac5c17f..0ad3852 100644
--- a/graphics/Android.bp
+++ b/graphics/Android.bp
@@ -91,13 +91,15 @@
 cc_defaults {
     name: "android.hardware.graphics.composer3-ndk_static",
     static_libs: [
-        "android.hardware.graphics.composer3-V3-ndk",
+        "android.hardware.drm.common-V1-ndk",
+        "android.hardware.graphics.composer3-V4-ndk",
     ],
 }
 
 cc_defaults {
     name: "android.hardware.graphics.composer3-ndk_shared",
     shared_libs: [
-        "android.hardware.graphics.composer3-V3-ndk",
+        "android.hardware.drm.common-V1-ndk",
+        "android.hardware.graphics.composer3-V4-ndk",
     ],
 }
diff --git a/graphics/composer/2.2/utils/vts/Android.bp b/graphics/composer/2.2/utils/vts/Android.bp
index 7157862..3b0a597 100644
--- a/graphics/composer/2.2/utils/vts/Android.bp
+++ b/graphics/composer/2.2/utils/vts/Android.bp
@@ -39,6 +39,7 @@
     shared_libs: [
         "libui",
         "server_configurable_flags",
+        "libtracing_perfetto",
     ],
     static_libs: [
         "android.hardware.graphics.composer@2.1-vts",
diff --git a/graphics/composer/2.2/vts/functional/Android.bp b/graphics/composer/2.2/vts/functional/Android.bp
index bda4198..431b1b6 100644
--- a/graphics/composer/2.2/vts/functional/Android.bp
+++ b/graphics/composer/2.2/vts/functional/Android.bp
@@ -55,6 +55,7 @@
         "libui",
         "android.hardware.common-V2-ndk",
         "server_configurable_flags",
+        "libtracing_perfetto",
     ],
     static_libs: [
         "android.hardware.graphics.common@1.1",
diff --git a/graphics/composer/aidl/Android.bp b/graphics/composer/aidl/Android.bp
index ace940a..bba41da 100644
--- a/graphics/composer/aidl/Android.bp
+++ b/graphics/composer/aidl/Android.bp
@@ -29,7 +29,7 @@
     host_supported: true,
     vendor_available: true,
     double_loadable: true,
-    frozen: true,
+    frozen: false,
     srcs: [
         "android/hardware/graphics/composer3/*.aidl",
     ],
@@ -39,6 +39,7 @@
     ],
     imports: [
         "android.hardware.common-V2",
+        "android.hardware.drm.common-V1",
     ],
     backend: {
         cpp: {
diff --git a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/CommandResultPayload.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/CommandResultPayload.aidl
index 6892f06..0fff523 100644
--- a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/CommandResultPayload.aidl
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/CommandResultPayload.aidl
@@ -41,4 +41,5 @@
   android.hardware.graphics.composer3.ReleaseFences releaseFences;
   android.hardware.graphics.composer3.PresentOrValidate presentOrValidateResult;
   android.hardware.graphics.composer3.ClientTargetPropertyWithBrightness clientTargetProperty;
+  android.hardware.graphics.composer3.DisplayLuts displayLuts;
 }
diff --git a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/NextEnrollment.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayLuts.aidl
similarity index 84%
rename from biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/NextEnrollment.aidl
rename to graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayLuts.aidl
index 75ed070..327e84c 100644
--- a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/NextEnrollment.aidl
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayLuts.aidl
@@ -5,7 +5,7 @@
  * 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
+ *     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,
@@ -31,11 +31,13 @@
 // 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.biometrics.fingerprint;
-/* @hide */
+package android.hardware.graphics.composer3;
 @VintfStability
-parcelable NextEnrollment {
-  int id;
-  android.hardware.biometrics.fingerprint.EnrollmentProgressStep[] progressSteps;
-  boolean result = true;
+parcelable DisplayLuts {
+  long display;
+  android.hardware.graphics.composer3.DisplayLuts.LayerLut[] layerLuts;
+  parcelable LayerLut {
+    long layer;
+    android.hardware.graphics.composer3.Lut lut;
+  }
 }
diff --git a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerCallback.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerCallback.aidl
index e64bd52..cd27360 100644
--- a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerCallback.aidl
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerCallback.aidl
@@ -45,4 +45,5 @@
   oneway void onVsyncIdle(long display);
   oneway void onRefreshRateChangedDebug(in android.hardware.graphics.composer3.RefreshRateChangedDebugData data);
   void onHotplugEvent(long display, android.hardware.graphics.common.DisplayHotplugEvent event);
+  oneway void onHdcpLevelsChanged(long display, in android.hardware.drm.HdcpLevels levels);
 }
diff --git a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/LayerCommand.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/LayerCommand.aidl
index 87c8c18..8b2b13c 100644
--- a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/LayerCommand.aidl
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/LayerCommand.aidl
@@ -57,4 +57,5 @@
   @nullable int[] bufferSlotsToClear;
   android.hardware.graphics.composer3.LayerLifecycleBatchCommandType layerLifecycleBatchCommandType;
   int newBufferSlotCount;
+  @nullable android.hardware.graphics.composer3.Lut[] luts;
 }
diff --git a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/EnrollmentProgressStep.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/Lut.aidl
similarity index 85%
copy from biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/EnrollmentProgressStep.aidl
copy to graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/Lut.aidl
index 173ac17..5fae35b 100644
--- a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/EnrollmentProgressStep.aidl
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/Lut.aidl
@@ -5,7 +5,7 @@
  * 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
+ *     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,
@@ -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.biometrics.fingerprint;
-/* @hide */
+package android.hardware.graphics.composer3;
 @VintfStability
-parcelable EnrollmentProgressStep {
-  int durationMs;
-  android.hardware.biometrics.fingerprint.AcquiredInfoAndVendorCode[] acquiredInfoAndVendorCodes;
+parcelable Lut {
+  @nullable ParcelFileDescriptor pfd;
+  android.hardware.graphics.composer3.LutProperties lutProperties;
 }
diff --git a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/NextEnrollment.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/LutProperties.aidl
similarity index 79%
copy from biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/NextEnrollment.aidl
copy to graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/LutProperties.aidl
index 75ed070..5edceb5 100644
--- a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/NextEnrollment.aidl
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/LutProperties.aidl
@@ -5,7 +5,7 @@
  * 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
+ *     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,
@@ -31,11 +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.biometrics.fingerprint;
-/* @hide */
+package android.hardware.graphics.composer3;
 @VintfStability
-parcelable NextEnrollment {
-  int id;
-  android.hardware.biometrics.fingerprint.EnrollmentProgressStep[] progressSteps;
-  boolean result = true;
+parcelable LutProperties {
+  android.hardware.graphics.composer3.LutProperties.Dimension dimension;
+  long size;
+  android.hardware.graphics.composer3.LutProperties.SamplingKey[] samplingKeys;
+  @VintfStability
+  enum Dimension {
+    ONE_D = 1,
+    THREE_D = 3,
+  }
+  @VintfStability
+  enum SamplingKey {
+    RGB,
+    MAX_RGB,
+  }
 }
diff --git a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/OverlayProperties.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/OverlayProperties.aidl
index 7d31ea3..dae78fd 100644
--- a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/OverlayProperties.aidl
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/OverlayProperties.aidl
@@ -36,6 +36,7 @@
 parcelable OverlayProperties {
   android.hardware.graphics.composer3.OverlayProperties.SupportedBufferCombinations[] combinations;
   boolean supportMixedColorSpaces;
+  @nullable android.hardware.graphics.composer3.LutProperties[] lutProperties;
   parcelable SupportedBufferCombinations {
     android.hardware.graphics.common.PixelFormat[] pixelFormats;
     android.hardware.graphics.common.Dataspace[] standards;
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/CommandResultPayload.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/CommandResultPayload.aidl
index 99c91aa..94fc3d0 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/CommandResultPayload.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/CommandResultPayload.aidl
@@ -19,6 +19,7 @@
 import android.hardware.graphics.composer3.ChangedCompositionTypes;
 import android.hardware.graphics.composer3.ClientTargetPropertyWithBrightness;
 import android.hardware.graphics.composer3.CommandError;
+import android.hardware.graphics.composer3.DisplayLuts;
 import android.hardware.graphics.composer3.DisplayRequest;
 import android.hardware.graphics.composer3.PresentFence;
 import android.hardware.graphics.composer3.PresentOrValidate;
@@ -96,4 +97,13 @@
      * the SDR buffers when an HDR layer is simultaneously device-composited.
      */
     ClientTargetPropertyWithBrightness clientTargetProperty;
+
+    /**
+     * Sets the Lut(s) for the layers.
+     *
+     * HWC should only request Lut(s) if SurfaceFlinger does not send the Lut(s) to the HWC.
+     * The main use-case is like HDR10+ or Dolby Vision where there is no Lut to send from
+     * SurfaceFlinger.
+     */
+    DisplayLuts displayLuts;
 }
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayLuts.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayLuts.aidl
new file mode 100644
index 0000000..ac0a606
--- /dev/null
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayLuts.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.graphics.composer3;
+
+import android.hardware.graphics.composer3.Lut;
+
+/**
+ * LUT (Look-Up Table) Interface for Color Transformation.
+ *
+ * This interface allows the HWC (Hardware Composer) to define and communicate Luts
+ * to SurfaceFlinger.
+ */
+@VintfStability
+parcelable DisplayLuts {
+    /**
+     * The display which the layerLuts list is for.
+     */
+    long display;
+
+    parcelable LayerLut {
+        /**
+         * The layer that the HWC is requesting a LUT to be applied during GPU composition.
+         */
+        long layer;
+        /**
+         * A Lut specified by the HWC for given HDR layers that don't have Luts provided.
+         */
+        Lut lut;
+    }
+
+    LayerLut[] layerLuts;
+}
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerCallback.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerCallback.aidl
index 96eccd7..a1d61fd 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerCallback.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerCallback.aidl
@@ -16,6 +16,7 @@
 
 package android.hardware.graphics.composer3;
 
+import android.hardware.drm.HdcpLevels;
 import android.hardware.graphics.common.DisplayHotplugEvent;
 import android.hardware.graphics.composer3.RefreshRateChangedDebugData;
 import android.hardware.graphics.composer3.VsyncPeriodChangeTimeline;
@@ -139,4 +140,12 @@
      * @param event is the type of event that occurred.
      */
     void onHotplugEvent(long display, DisplayHotplugEvent event);
+
+    /**
+     * Notify the client the HDCP levels of the display changed.
+     *
+     * @param display is the display whose HDCP levels have changed.
+     * @param levels is the new HDCP levels.
+     */
+    oneway void onHdcpLevelsChanged(long display, in HdcpLevels levels);
 }
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/LayerCommand.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/LayerCommand.aidl
index e961c48..bf4f504 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/LayerCommand.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/LayerCommand.aidl
@@ -24,6 +24,7 @@
 import android.hardware.graphics.composer3.Color;
 import android.hardware.graphics.composer3.LayerBrightness;
 import android.hardware.graphics.composer3.LayerLifecycleBatchCommandType;
+import android.hardware.graphics.composer3.Lut;
 import android.hardware.graphics.composer3.ParcelableBlendMode;
 import android.hardware.graphics.composer3.ParcelableComposition;
 import android.hardware.graphics.composer3.ParcelableDataspace;
@@ -279,4 +280,9 @@
      * Specifies the number of buffer slot to be reserved.
      */
     int newBufferSlotCount;
+
+    /**
+     * Sets the lut(s) for the layer.
+     */
+    @nullable Lut[] luts;
 }
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/Lut.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/Lut.aidl
new file mode 100644
index 0000000..abfeb14
--- /dev/null
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/Lut.aidl
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.graphics.composer3;
+
+import android.hardware.graphics.composer3.LutProperties;
+
+/**
+ * LUT (Look-Up Table) Interface for Color Transformation.
+ *
+ * This interface allows the HWC (Hardware Composer) to define and communicate LUTs
+ * with SurfaceFlinger.
+ */
+
+@VintfStability
+parcelable Lut {
+    /**
+     * A handle to a memory region.
+     * If the file descriptor is not set, this means that the HWC doesn't specify a Lut.
+     *
+     * When specifying a Lut, the HWC is required to follow the instructions as below:
+     * 1. use `memfd_create` to create a shared memory segment
+     *    with the size specified in lutProperties.
+     * 2. use `mmap` to map the shared memory segment into its own virtual address space.
+     *    PROT_READ/PROT_WRITE recommended for prot argument.
+     *
+     * For data precision, 32-bit float is used to specify a Lut by both the HWC and
+     * the platform.
+     *
+     * For unflattening/flattening 3D Lut(s), the algorithm below should be observed
+     * by both the HWC and the platform.
+     * Assuming that we have a 3D array `ORIGINAL[WIDTH, HEIGHT, DEPTH]`, we would turn it into
+     * `FLAT[WIDTH * HEIGHT * DEPTH]` by
+     *
+     * `FLAT[z + DEPTH * (y + HEIGHT * x)] = ORIGINAL[x, y, z]`
+     */
+    @nullable ParcelFileDescriptor pfd;
+
+    /**
+     * The properties of the Lut.
+     */
+    LutProperties lutProperties;
+}
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/LutProperties.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/LutProperties.aidl
new file mode 100644
index 0000000..47ec390
--- /dev/null
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/LutProperties.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.graphics.composer3;
+
+/**
+ * The properties of the LUT (Look-Up Table).
+ */
+@VintfStability
+parcelable LutProperties {
+    /**
+     * The dimension of the Lut.
+     * Either 1d or 3d.
+     */
+    @VintfStability enum Dimension { ONE_D = 1, THREE_D = 3 }
+    Dimension dimension;
+
+    /**
+     * The size of the Lut.
+     * This refers to the length of a 1D Lut, or the grid size of a 3D one.
+     */
+    long size;
+
+    /**
+     * SamplingKey is about how a Lut can be sampled.
+     * A Lut can be sampled in more than one way,
+     * but only one sampling method is used at one time.
+     *
+     * The implementations should use a sampling strategy
+     * at least as good as linear sampling.
+     */
+    // TODO(b/358422255): add sampling ways
+    @VintfStability enum SamplingKey { RGB, MAX_RGB }
+    SamplingKey[] samplingKeys;
+}
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/OverlayProperties.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/OverlayProperties.aidl
index c25eea4..b97cdcc 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/OverlayProperties.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/OverlayProperties.aidl
@@ -16,6 +16,8 @@
 
 package android.hardware.graphics.composer3;
 
+import android.hardware.graphics.composer3.LutProperties;
+
 @VintfStability
 parcelable OverlayProperties {
     parcelable SupportedBufferCombinations {
@@ -42,4 +44,8 @@
     // True if the DPU is able to color manage at least two overlays
     // with different input colorspaces, false otherwise.
     boolean supportMixedColorSpaces;
+
+    // Array of lut properties in order that the HWC supports.
+    // The list accepts 1D lut(s) and 3D lut(s).
+    @nullable LutProperties[] lutProperties;
 }
diff --git a/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientReader.h b/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientReader.h
index 76ba24b..331d717 100644
--- a/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientReader.h
+++ b/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientReader.h
@@ -27,9 +27,8 @@
 #include <string.h>
 
 #include <aidl/android/hardware/graphics/composer3/ClientTargetProperty.h>
-#include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/CommandResultPayload.h>
-
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
 
 #include <log/log.h>
 #include <sync/sync.h>
@@ -84,6 +83,10 @@
                     parseSetClientTargetProperty(std::move(
                             result.get<CommandResultPayload::Tag::clientTargetProperty>()));
                     break;
+                case CommandResultPayload::Tag::displayLuts:
+                    parseSetDisplayLuts(
+                            std::move(result.get<CommandResultPayload::Tag::displayLuts>()));
+                    break;
             }
         }
     }
@@ -182,6 +185,20 @@
         return std::move(data.clientTargetProperty);
     }
 
+    // Get the lut(s) requested by hardware composer.
+    std::vector<DisplayLuts::LayerLut> takeDisplayLuts(int64_t display) {
+        LOG_ALWAYS_FATAL_IF(mDisplay && display != *mDisplay);
+        auto found = mReturnData.find(display);
+
+        // If not found, return the empty vector
+        if (found == mReturnData.end()) {
+            return {};
+        }
+
+        ReturnData& data = found->second;
+        return std::move(data.layerLuts);
+    }
+
   private:
     void resetData() {
         mErrors.clear();
@@ -227,6 +244,18 @@
         data.clientTargetProperty = std::move(clientTargetProperty);
     }
 
+    void parseSetDisplayLuts(DisplayLuts&& displayLuts) {
+        LOG_ALWAYS_FATAL_IF(mDisplay && displayLuts.display != *mDisplay);
+        auto& data = mReturnData[displayLuts.display];
+        for (auto& layerLut : displayLuts.layerLuts) {
+            if (layerLut.lut.pfd.get() >= 0) {
+                data.layerLuts.push_back(
+                        {layerLut.layer, Lut{ndk::ScopedFileDescriptor(layerLut.lut.pfd.release()),
+                                             layerLut.lut.lutProperties}});
+            }
+        }
+    }
+
     struct ReturnData {
         DisplayRequest displayRequests;
         std::vector<ChangedCompositionLayer> changedLayers;
@@ -238,6 +267,7 @@
                 .clientTargetProperty = {common::PixelFormat::RGBA_8888, Dataspace::UNKNOWN},
                 .brightness = 1.f,
         };
+        std::vector<DisplayLuts::LayerLut> layerLuts;
     };
 
     std::vector<CommandError> mErrors;
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 a1ccbfe..02fb3aa 100644
--- a/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientWriter.h
+++ b/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientWriter.h
@@ -30,6 +30,7 @@
 #include <aidl/android/hardware/graphics/composer3/DisplayBrightness.h>
 #include <aidl/android/hardware/graphics/composer3/LayerBrightness.h>
 #include <aidl/android/hardware/graphics/composer3/LayerLifecycleBatchCommandType.h>
+#include <aidl/android/hardware/graphics/composer3/Lut.h>
 #include <aidl/android/hardware/graphics/composer3/PerFrameMetadata.h>
 #include <aidl/android/hardware/graphics/composer3/PerFrameMetadataBlob.h>
 
@@ -245,6 +246,15 @@
         getLayerCommand(display, layer).blockingRegion.emplace(blocking.begin(), blocking.end());
     }
 
+    void setLayerLuts(int64_t display, int64_t layer, std::vector<Lut>& luts) {
+        std::vector<std::optional<Lut>> currentLuts;
+        for (auto& lut : luts) {
+            currentLuts.push_back(std::make_optional<Lut>(
+                    {ndk::ScopedFileDescriptor(lut.pfd.release()), lut.lutProperties}));
+        }
+        getLayerCommand(display, layer).luts.emplace(std::move(currentLuts));
+    }
+
     std::vector<DisplayCommand> takePendingCommands() {
         flushLayerCommand();
         flushDisplayCommand();
diff --git a/graphics/composer/aidl/vts/Android.bp b/graphics/composer/aidl/vts/Android.bp
index 3464fe9..894ca52 100644
--- a/graphics/composer/aidl/vts/Android.bp
+++ b/graphics/composer/aidl/vts/Android.bp
@@ -57,6 +57,7 @@
         "libprocessgroup",
         "libvndksupport",
         "server_configurable_flags",
+        "libtracing_perfetto",
     ],
     header_libs: [
         "android.hardware.graphics.composer3-command-buffer",
@@ -65,6 +66,7 @@
         "android.hardware.graphics.common@1.2",
         "android.hardware.common-V2-ndk",
         "android.hardware.common.fmq-V1-ndk",
+        "android.hardware.drm.common-V1-ndk",
         "libaidlcommonsupport",
         "libarect",
         "libbase",
diff --git a/graphics/composer/aidl/vts/GraphicsComposerCallback.cpp b/graphics/composer/aidl/vts/GraphicsComposerCallback.cpp
index 544f692..1f7972c 100644
--- a/graphics/composer/aidl/vts/GraphicsComposerCallback.cpp
+++ b/graphics/composer/aidl/vts/GraphicsComposerCallback.cpp
@@ -208,4 +208,15 @@
     }
 }
 
+::ndk::ScopedAStatus GraphicsComposerCallback::onHdcpLevelsChanged(
+        int64_t in_display, const ::aidl::android::hardware::drm::HdcpLevels&) {
+    std::scoped_lock lock(mMutex);
+
+    const auto it = std::find(mDisplays.begin(), mDisplays.end(), in_display);
+    if (it != mDisplays.end()) {
+        mHdcpLevelChangedCount++;
+    }
+    return ::ndk::ScopedAStatus::ok();
+}
+
 }  // namespace aidl::android::hardware::graphics::composer3::vts
diff --git a/graphics/composer/aidl/vts/GraphicsComposerCallback.h b/graphics/composer/aidl/vts/GraphicsComposerCallback.h
index 7a8d4a3..97f8e2b 100644
--- a/graphics/composer/aidl/vts/GraphicsComposerCallback.h
+++ b/graphics/composer/aidl/vts/GraphicsComposerCallback.h
@@ -65,6 +65,8 @@
             const RefreshRateChangedDebugData&) override;
     virtual ::ndk::ScopedAStatus onHotplugEvent(int64_t in_display,
                                                 common::DisplayHotplugEvent) override;
+    virtual ::ndk::ScopedAStatus onHdcpLevelsChanged(
+            int64_t in_display, const ::aidl::android::hardware::drm::HdcpLevels&) override;
 
     mutable std::mutex mMutex;
     // the set of all currently connected displays
@@ -88,6 +90,7 @@
     int32_t mInvalidVsyncPeriodChangeCount GUARDED_BY(mMutex) = 0;
     int32_t mInvalidSeamlessPossibleCount GUARDED_BY(mMutex) = 0;
     int32_t mInvalidRefreshRateDebugEnabledCallbackCount GUARDED_BY(mMutex) = 0;
+    int32_t mHdcpLevelChangedCount GUARDED_BY(mMutex) = 0;
 };
 
 }  // namespace aidl::android::hardware::graphics::composer3::vts
diff --git a/graphics/composer/aidl/vts/ReadbackVts.cpp b/graphics/composer/aidl/vts/ReadbackVts.cpp
index 283b8ce..9d5928d 100644
--- a/graphics/composer/aidl/vts/ReadbackVts.cpp
+++ b/graphics/composer/aidl/vts/ReadbackVts.cpp
@@ -16,6 +16,7 @@
 
 #include "ReadbackVts.h"
 #include <aidl/android/hardware/graphics/common/BufferUsage.h>
+#include <cmath>
 #include "RenderEngineVts.h"
 #include "renderengine/ExternalTexture.h"
 #include "renderengine/impl/ExternalTexture.h"
@@ -106,37 +107,72 @@
     return layerSettings;
 }
 
-int32_t ReadbackHelper::GetBytesPerPixel(common::PixelFormat pixelFormat) {
+int32_t ReadbackHelper::GetBitsPerChannel(common::PixelFormat pixelFormat) {
     switch (pixelFormat) {
+        case common::PixelFormat::RGBA_1010102:
+            return 10;
         case common::PixelFormat::RGBA_8888:
-            return 4;
         case common::PixelFormat::RGB_888:
-            return 3;
+            return 8;
         default:
             return -1;
     }
 }
 
-void ReadbackHelper::fillBuffer(uint32_t width, uint32_t height, uint32_t stride, void* bufferData,
+int32_t ReadbackHelper::GetAlphaBits(common::PixelFormat pixelFormat) {
+    switch (pixelFormat) {
+        case common::PixelFormat::RGBA_8888:
+            return 8;
+        case common::PixelFormat::RGBA_1010102:
+            return 2;
+        case common::PixelFormat::RGB_888:
+            return 0;
+        default:
+            return -1;
+    }
+}
+
+void ReadbackHelper::fillBuffer(uint32_t width, uint32_t height, uint32_t stride,
+                                int32_t bytesPerPixel, void* bufferData,
                                 common::PixelFormat pixelFormat,
                                 std::vector<Color> desiredPixelColors) {
     ASSERT_TRUE(pixelFormat == common::PixelFormat::RGB_888 ||
-                pixelFormat == common::PixelFormat::RGBA_8888);
-    int32_t bytesPerPixel = GetBytesPerPixel(pixelFormat);
+                pixelFormat == common::PixelFormat::RGBA_8888 ||
+                pixelFormat == common::PixelFormat::RGBA_1010102);
+    int32_t bitsPerChannel = GetBitsPerChannel(pixelFormat);
+    int32_t alphaBits = GetAlphaBits(pixelFormat);
+    ASSERT_NE(-1, alphaBits);
+    ASSERT_NE(-1, bitsPerChannel);
     ASSERT_NE(-1, bytesPerPixel);
-    for (int row = 0; row < height; row++) {
-        for (int col = 0; col < width; col++) {
-            auto pixel = row * static_cast<int32_t>(width) + col;
+
+    uint32_t maxValue = (1 << bitsPerChannel) - 1;
+    uint32_t maxAlphaValue = (1 << alphaBits) - 1;
+    for (uint32_t row = 0; row < height; row++) {
+        for (uint32_t col = 0; col < width; col++) {
+            auto pixel = row * width + col;
             Color srcColor = desiredPixelColors[static_cast<size_t>(pixel)];
 
-            int offset = (row * static_cast<int32_t>(stride) + col) * bytesPerPixel;
-            uint8_t* pixelColor = (uint8_t*)bufferData + offset;
-            pixelColor[0] = static_cast<uint8_t>(std::round(255.0f * srcColor.r));
-            pixelColor[1] = static_cast<uint8_t>(std::round(255.0f * srcColor.g));
-            pixelColor[2] = static_cast<uint8_t>(std::round(255.0f * srcColor.b));
+            uint32_t offset = (row * stride + col) * static_cast<uint32_t>(bytesPerPixel);
 
-            if (bytesPerPixel == 4) {
-                pixelColor[3] = static_cast<uint8_t>(std::round(255.0f * srcColor.a));
+            uint32_t* pixelStart = (uint32_t*)((uint8_t*)bufferData + offset);
+
+            uint32_t red = static_cast<uint32_t>(std::round(maxValue * srcColor.r));
+            uint32_t green = static_cast<uint32_t>(std::round(maxValue * srcColor.g));
+            uint32_t blue = static_cast<uint32_t>(std::round(maxValue * srcColor.b));
+
+            // Boo we're not word aligned so special case this.
+            if (pixelFormat == common::PixelFormat::RGB_888) {
+                uint8_t* pixelColor = (uint8_t*)pixelStart;
+                pixelColor[0] = static_cast<uint8_t>(red);
+                pixelColor[1] = static_cast<uint8_t>(green);
+                pixelColor[2] = static_cast<uint8_t>(blue);
+            } else {
+                uint32_t alpha = static_cast<uint32_t>(std::round(maxAlphaValue * srcColor.a));
+                uint32_t color = (alpha << (32 - alphaBits)) |
+                                 (blue << (32 - alphaBits - bitsPerChannel)) |
+                                 (green << (32 - alphaBits - bitsPerChannel * 2)) |
+                                 (red << (32 - alphaBits - bitsPerChannel * 3));
+                *pixelStart = color;
             }
         }
     }
@@ -165,7 +201,8 @@
 bool ReadbackHelper::readbackSupported(const common::PixelFormat& pixelFormat,
                                        const common::Dataspace& dataspace) {
     if (pixelFormat != common::PixelFormat::RGB_888 &&
-        pixelFormat != common::PixelFormat::RGBA_8888) {
+        pixelFormat != common::PixelFormat::RGBA_8888 &&
+        pixelFormat != common::PixelFormat::RGBA_1010102) {
         return false;
     }
     if (std::find(dataspaces.begin(), dataspaces.end(), dataspace) == dataspaces.end()) {
@@ -175,36 +212,110 @@
 }
 
 void ReadbackHelper::compareColorBuffers(const std::vector<Color>& expectedColors, void* bufferData,
-                                         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++) {
-            auto pixel = row * static_cast<int32_t>(width) + col;
-            int offset = (row * static_cast<int32_t>(stride) + col) * bytesPerPixel;
-            uint8_t* pixelColor = (uint8_t*)bufferData + offset;
+                                         const uint32_t stride, int32_t bytesPerPixel,
+                                         const uint32_t width, const uint32_t height,
+                                         common::PixelFormat pixelFormat) {
+    int32_t bitsPerChannel = GetBitsPerChannel(pixelFormat);
+    int32_t alphaBits = GetAlphaBits(pixelFormat);
+    ASSERT_GT(bytesPerPixel, 0);
+    ASSERT_NE(-1, alphaBits);
+    ASSERT_NE(-1, bitsPerChannel);
+    uint32_t maxValue = (1 << bitsPerChannel) - 1;
+    uint32_t maxAlphaValue = (1 << alphaBits) - 1;
+    for (uint32_t row = 0; row < height; row++) {
+        for (uint32_t col = 0; col < width; col++) {
+            auto pixel = row * width + col;
             const Color expectedColor = expectedColors[static_cast<size_t>(pixel)];
-            ASSERT_EQ(std::round(255.0f * expectedColor.r), pixelColor[0]);
-            ASSERT_EQ(std::round(255.0f * expectedColor.g), pixelColor[1]);
-            ASSERT_EQ(std::round(255.0f * expectedColor.b), pixelColor[2]);
+
+            uint32_t offset = (row * stride + col) * static_cast<uint32_t>(bytesPerPixel);
+            uint32_t* pixelStart = (uint32_t*)((uint8_t*)bufferData + offset);
+
+            uint32_t expectedRed = static_cast<uint32_t>(std::round(maxValue * expectedColor.r));
+            uint32_t expectedGreen = static_cast<uint32_t>(std::round(maxValue * expectedColor.g));
+            uint32_t expectedBlue = static_cast<uint32_t>(std::round(maxValue * expectedColor.b));
+
+            // Boo we're not word aligned so special case this.
+            if (pixelFormat == common::PixelFormat::RGB_888) {
+                uint8_t* pixelColor = (uint8_t*)pixelStart;
+                ASSERT_EQ(pixelColor[0], static_cast<uint8_t>(expectedRed))
+                        << "Red channel mismatch at (" << row << ", " << col << ")";
+                ASSERT_EQ(pixelColor[1], static_cast<uint8_t>(expectedGreen))
+                        << "Green channel mismatch at (" << row << ", " << col << ")";
+                ASSERT_EQ(pixelColor[2], static_cast<uint8_t>(expectedBlue))
+                        << "Blue channel mismatch at (" << row << ", " << col << ")";
+            } else {
+                uint32_t expectedAlpha =
+                        static_cast<uint32_t>(std::round(maxAlphaValue * expectedColor.a));
+
+                uint32_t actualRed =
+                        (*pixelStart >> (32 - alphaBits - bitsPerChannel * 3)) & maxValue;
+                uint32_t actualGreen =
+                        (*pixelStart >> (32 - alphaBits - bitsPerChannel * 2)) & maxValue;
+                uint32_t actualBlue = (*pixelStart >> (32 - alphaBits - bitsPerChannel)) & maxValue;
+                uint32_t actualAlpha = (*pixelStart >> (32 - alphaBits)) & maxAlphaValue;
+
+                ASSERT_EQ(expectedRed, actualRed)
+                        << "Red channel mismatch at (" << row << ", " << col << ")";
+                ASSERT_EQ(expectedGreen, actualGreen)
+                        << "Green channel mismatch at (" << row << ", " << col << ")";
+                ASSERT_EQ(expectedBlue, actualBlue)
+                        << "Blue channel mismatch at (" << row << ", " << col << ")";
+            }
         }
     }
 }
 
 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]);
+                                         const uint32_t stride, int32_t bytesPerPixel,
+                                         const uint32_t width, const uint32_t height,
+                                         common::PixelFormat pixelFormat) {
+    int32_t bitsPerChannel = GetBitsPerChannel(pixelFormat);
+    int32_t alphaBits = GetAlphaBits(pixelFormat);
+    ASSERT_GT(bytesPerPixel, 0);
+    ASSERT_NE(-1, alphaBits);
+    ASSERT_NE(-1, bitsPerChannel);
+    uint32_t maxValue = (1 << bitsPerChannel) - 1;
+    uint32_t maxAlphaValue = (1 << alphaBits) - 1;
+    for (uint32_t row = 0; row < height; row++) {
+        for (uint32_t col = 0; col < width; col++) {
+            uint32_t offset = (row * stride + col) * static_cast<uint32_t>(bytesPerPixel);
+            uint32_t* expectedStart = (uint32_t*)((uint8_t*)expectedBuffer + offset);
+            uint32_t* actualStart = (uint32_t*)((uint8_t*)actualBuffer + offset);
+
+            // Boo we're not word aligned so special case this.
+            if (pixelFormat == common::PixelFormat::RGB_888) {
+                uint8_t* expectedPixel = (uint8_t*)expectedStart;
+                uint8_t* actualPixel = (uint8_t*)actualStart;
+                ASSERT_EQ(actualPixel[0], expectedPixel[0])
+                        << "Red channel mismatch at (" << row << ", " << col << ")";
+                ASSERT_EQ(actualPixel[1], expectedPixel[1])
+                        << "Green channel mismatch at (" << row << ", " << col << ")";
+                ASSERT_EQ(actualPixel[2], expectedPixel[2])
+                        << "Blue channel mismatch at (" << row << ", " << col << ")";
+            } else {
+                uint32_t expectedRed =
+                        (*expectedStart >> (32 - alphaBits - bitsPerChannel * 3)) & maxValue;
+                uint32_t expectedGreen =
+                        (*expectedStart >> (32 - alphaBits - bitsPerChannel * 2)) & maxValue;
+                uint32_t expectedBlue =
+                        (*expectedStart >> (32 - alphaBits - bitsPerChannel)) & maxValue;
+                uint32_t expectedAlpha = (*expectedStart >> (32 - alphaBits)) & maxAlphaValue;
+
+                uint32_t actualRed =
+                        (*actualStart >> (32 - alphaBits - bitsPerChannel * 3)) & maxValue;
+                uint32_t actualGreen =
+                        (*actualStart >> (32 - alphaBits - bitsPerChannel * 2)) & maxValue;
+                uint32_t actualBlue =
+                        (*actualStart >> (32 - alphaBits - bitsPerChannel)) & maxValue;
+                uint32_t actualAlpha = (*actualStart >> (32 - alphaBits)) & maxAlphaValue;
+
+                ASSERT_EQ(expectedRed, actualRed)
+                        << "Red channel mismatch at (" << row << ", " << col << ")";
+                ASSERT_EQ(expectedGreen, actualGreen)
+                        << "Green channel mismatch at (" << row << ", " << col << ")";
+                ASSERT_EQ(expectedBlue, actualBlue)
+                        << "Blue channel mismatch at (" << row << ", " << col << ")";
+            }
         }
     }
 }
@@ -258,12 +369,13 @@
     auto status = mGraphicBuffer->lockAsync(mUsage, mAccessRegion, &bufData, dup(bufferFence.get()),
                                             &bytesPerPixel, &bytesPerStride);
     EXPECT_EQ(::android::OK, status);
-    ASSERT_TRUE(mPixelFormat == PixelFormat::RGB_888 || mPixelFormat == PixelFormat::RGBA_8888);
+    ASSERT_TRUE(mPixelFormat == PixelFormat::RGB_888 || mPixelFormat == PixelFormat::RGBA_8888 ||
+                mPixelFormat == PixelFormat::RGBA_1010102);
     const uint32_t stride = (bytesPerPixel > 0 && bytesPerStride > 0)
                                     ? static_cast<uint32_t>(bytesPerStride / bytesPerPixel)
                                     : mGraphicBuffer->getStride();
-    ReadbackHelper::compareColorBuffers(expectedColors, bufData, stride, mWidth, mHeight,
-                                        mPixelFormat);
+    ReadbackHelper::compareColorBuffers(expectedColors, bufData, stride, bytesPerPixel, mWidth,
+                                        mHeight, mPixelFormat);
     status = mGraphicBuffer->unlock();
     EXPECT_EQ(::android::OK, status);
 }
@@ -353,8 +465,8 @@
                                     ? static_cast<uint32_t>(bytesPerStride / bytesPerPixel)
                                     : mGraphicBuffer->getStride();
     EXPECT_EQ(::android::OK, status);
-    ASSERT_NO_FATAL_FAILURE(ReadbackHelper::fillBuffer(mWidth, mHeight, stride, bufData,
-                                                       mPixelFormat, expectedColors));
+    ASSERT_NO_FATAL_FAILURE(ReadbackHelper::fillBuffer(mWidth, mHeight, stride, bytesPerPixel,
+                                                       bufData, mPixelFormat, expectedColors));
 
     const auto unlockStatus = mGraphicBuffer->unlockAsync(&mFillFence);
     ASSERT_EQ(::android::OK, unlockStatus);
diff --git a/graphics/composer/aidl/vts/ReadbackVts.h b/graphics/composer/aidl/vts/ReadbackVts.h
index 8ac0f4b..e3b2384 100644
--- a/graphics/composer/aidl/vts/ReadbackVts.h
+++ b/graphics/composer/aidl/vts/ReadbackVts.h
@@ -172,10 +172,12 @@
 
     static Dataspace getDataspaceForColorMode(ColorMode mode);
 
-    static int32_t GetBytesPerPixel(PixelFormat pixelFormat);
+    static int32_t GetBitsPerChannel(PixelFormat pixelFormat);
+    static int32_t GetAlphaBits(PixelFormat pixelFormat);
 
-    static void fillBuffer(uint32_t width, uint32_t height, uint32_t stride, void* bufferData,
-                           PixelFormat pixelFormat, std::vector<Color> desiredPixelColors);
+    static void fillBuffer(uint32_t width, uint32_t height, uint32_t stride, int32_t bytesPerPixel,
+                           void* bufferData, PixelFormat pixelFormat,
+                           std::vector<Color> desiredPixelColors);
 
     static void clearColors(std::vector<Color>& expectedColors, int32_t width, int32_t height,
                             int32_t displayWidth);
@@ -189,11 +191,12 @@
     static const std::vector<Dataspace> dataspaces;
 
     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 stride, int32_t bytesPerPixel,
                                     const uint32_t width, const uint32_t height,
                                     PixelFormat pixelFormat);
+    static void compareColorBuffers(void* expectedBuffer, void* actualBuffer, const uint32_t stride,
+                                    int32_t bytesPerPixel, const uint32_t width,
+                                    const uint32_t height, PixelFormat pixelFormat);
 };
 
 class ReadbackBuffer {
diff --git a/graphics/composer/aidl/vts/RenderEngineVts.cpp b/graphics/composer/aidl/vts/RenderEngineVts.cpp
index bc5eb6f..8f8b5fd 100644
--- a/graphics/composer/aidl/vts/RenderEngineVts.cpp
+++ b/graphics/composer/aidl/vts/RenderEngineVts.cpp
@@ -81,7 +81,7 @@
     const uint32_t stride = (bytesPerPixel > 0 && bytesPerStride > 0)
                                     ? static_cast<uint32_t>(bytesPerStride / bytesPerPixel)
                                     : mGraphicBuffer->getStride();
-    ReadbackHelper::compareColorBuffers(expectedColors, bufferData, stride,
+    ReadbackHelper::compareColorBuffers(expectedColors, bufferData, stride, bytesPerPixel,
                                         mGraphicBuffer->getWidth(), mGraphicBuffer->getHeight(),
                                         mFormat);
     ASSERT_EQ(::android::OK, mGraphicBuffer->unlock());
@@ -108,7 +108,7 @@
 
     ASSERT_EQ(renderedStride, bufferStride);
 
-    ReadbackHelper::compareColorBuffers(renderedBufferData, bufferData, bufferStride,
+    ReadbackHelper::compareColorBuffers(renderedBufferData, bufferData, bufferStride, bytesPerPixel,
                                         mGraphicBuffer->getWidth(), mGraphicBuffer->getHeight(),
                                         mFormat);
     ASSERT_EQ(::android::OK, buffer->unlock());
diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
index 3d9253f..9db8794 100644
--- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
+++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
@@ -496,11 +496,14 @@
             const auto& buffer = graphicBuffer->handle;
             void* clientBufData;
             const auto stride = static_cast<uint32_t>(graphicBuffer->stride);
-            graphicBuffer->lock(clientUsage, layer->getAccessRegion(), &clientBufData);
+            int bytesPerPixel = -1;
+            int bytesPerStride = -1;
+            graphicBuffer->lock(clientUsage, layer->getAccessRegion(), &clientBufData,
+                                &bytesPerPixel, &bytesPerStride);
 
-            ASSERT_NO_FATAL_FAILURE(
-                    ReadbackHelper::fillBuffer(layer->getWidth(), layer->getHeight(), stride,
-                                               clientBufData, clientFormat, expectedColors));
+            ASSERT_NO_FATAL_FAILURE(ReadbackHelper::fillBuffer(
+                    layer->getWidth(), layer->getHeight(), stride, bytesPerPixel, clientBufData,
+                    clientFormat, expectedColors));
             int32_t clientFence;
             const auto unlockStatus = graphicBuffer->unlockAsync(&clientFence);
             ASSERT_EQ(::android::OK, unlockStatus);
@@ -677,15 +680,18 @@
         const auto& buffer = graphicBuffer->handle;
 
         void* clientBufData;
+        int bytesPerPixel = -1;
+        int bytesPerStride = -1;
         graphicBuffer->lock(clientUsage, {0, 0, getDisplayWidth(), getDisplayHeight()},
-                            &clientBufData);
+                            &clientBufData, &bytesPerPixel, &bytesPerStride);
 
         std::vector<Color> clientColors(
                 static_cast<size_t>(getDisplayWidth() * getDisplayHeight()));
         ReadbackHelper::fillColorsArea(clientColors, getDisplayWidth(), clientFrame, RED);
         ASSERT_NO_FATAL_FAILURE(ReadbackHelper::fillBuffer(
                 static_cast<uint32_t>(getDisplayWidth()), static_cast<uint32_t>(getDisplayHeight()),
-                graphicBuffer->getStride(), clientBufData, clientFormat, clientColors));
+                graphicBuffer->getStride(), bytesPerPixel, clientBufData, clientFormat,
+                clientColors));
         int32_t clientFence;
         const auto unlockStatus = graphicBuffer->unlockAsync(&clientFence);
         ASSERT_EQ(::android::OK, unlockStatus);
diff --git a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
index bae362f..f398c53 100644
--- a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
+++ b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
@@ -719,6 +719,38 @@
 }
 
 /**
+ * Test IMapper::lock and IMapper::unlock with no CPU usage requested.
+ */
+TEST_P(GraphicsMapperHidlTest, LockUnlockNoCPUUsage) {
+    const auto& info = mDummyDescriptorInfo;
+
+    const native_handle_t* bufferHandle;
+    uint32_t stride;
+    ASSERT_NO_FATAL_FAILURE(
+            bufferHandle = mGralloc->allocate(info, true, Tolerance::kToleranceStrict, &stride));
+
+    // lock buffer with 0 usage
+    const IMapper::Rect region{0, 0, static_cast<int32_t>(info.width),
+                               static_cast<int32_t>(info.height)};
+
+    hidl_handle acquireFenceHandle;
+
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+    mGralloc->getMapper()->lock(buffer, 0, region, acquireFenceHandle,
+                                [&](const auto& tmpError, const auto& /*tmpData*/) {
+                                    EXPECT_EQ(Error::BAD_VALUE, tmpError)
+                                            << "Locking with 0 access succeeded";
+                                });
+
+    mGralloc->getMapper()->unlock(buffer, [&](const auto& tmpError, const auto&) {
+        EXPECT_EQ(Error::BAD_BUFFER, tmpError)
+                << "Unlocking not locked buffer succeeded";
+    });
+
+    mGralloc->freeBuffer(bufferHandle);
+}
+
+/**
  *  Test multiple operations associated with different color formats
  */
 TEST_P(GraphicsMapperHidlTest, Lock_YCRCB_420_SP) {
diff --git a/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp b/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp
index 1e0c427..bdbe4d0 100644
--- a/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp
+++ b/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp
@@ -750,6 +750,42 @@
 }
 
 /**
+ * Test IMapper::lock and IMapper::unlock with no CPU usage requested.
+ */
+TEST_P(GraphicsMapperStableCTests, LockUnlockNoCPUUsage) {
+    constexpr auto usage = BufferUsage::CPU_READ_NEVER | BufferUsage::CPU_WRITE_NEVER;
+    auto buffer = allocate({
+            .name = {"VTS_TEMP"},
+            .width = 64,
+            .height = 64,
+            .layerCount = 1,
+            .format = PixelFormat::RGBA_8888,
+            .usage = usage,
+            .reservedSize = 0,
+    });
+    ASSERT_NE(nullptr, buffer.get());
+
+    // lock buffer for writing
+    const auto& info = buffer->info();
+    const ARect region{0, 0, info.width, info.height};
+    auto handle = buffer->import();
+    uint8_t* data = nullptr;
+
+    EXPECT_EQ(AIMAPPER_ERROR_BAD_VALUE,
+              mapper()->v5.lock(*handle, static_cast<int64_t>(info.usage),
+                                region, -1,(void**)&data))
+              << "Locking with 0 access succeeded";
+
+    int releaseFence = -1;
+    EXPECT_EQ(AIMAPPER_ERROR_BAD_BUFFER,
+              mapper()->v5.unlock(*handle, &releaseFence))
+              << "Unlocking not locked buffer succeeded";
+    if (releaseFence != -1) {
+        close(releaseFence);
+    }
+}
+
+/**
  *  Test multiple operations associated with different color formats
  */
 TEST_P(GraphicsMapperStableCTests, Lock_YCRCB_420_SP) {
diff --git a/keymaster/aidl/Android.bp b/keymaster/aidl/Android.bp
index 56997a9..c101f56 100644
--- a/keymaster/aidl/Android.bp
+++ b/keymaster/aidl/Android.bp
@@ -18,6 +18,13 @@
         java: {
             platform_apis: true,
         },
+        ndk: {
+            apex_available: [
+                "com.android.hardware.biometrics.face.virtual",
+                "com.android.hardware.biometrics.fingerprint.virtual",
+                "//apex_available:platform",
+            ],
+        },
         rust: {
             enabled: true,
         },
diff --git a/radio/aidl/vts/radio_sim_test.cpp b/radio/aidl/vts/radio_sim_test.cpp
index ec1facc..2823977 100644
--- a/radio/aidl/vts/radio_sim_test.cpp
+++ b/radio/aidl/vts/radio_sim_test.cpp
@@ -533,7 +533,7 @@
         EXPECT_EQ(RadioError::NONE, radioRsp_sim->rspInfo.error);
 
         if (aidl_version <= 2) {
-            EXPECT_EQ(1, radioRsp_sim->carrierRestrictionsResp.allowedCarriers.size());
+            ASSERT_EQ(1, radioRsp_sim->carrierRestrictionsResp.allowedCarriers.size());
             EXPECT_EQ(0, radioRsp_sim->carrierRestrictionsResp.excludedCarriers.size());
 
             ASSERT_TRUE(std::string("123") ==
@@ -545,7 +545,7 @@
             ASSERT_TRUE(radioRsp_sim->carrierRestrictionsResp.allowedCarriersPrioritized);
             EXPECT_EQ(SimLockMultiSimPolicy::NO_MULTISIM_POLICY, radioRsp_sim->multiSimPolicyResp);
         } else {
-            EXPECT_EQ(1, radioRsp_sim->carrierRestrictionsResp.allowedCarrierInfoList.size());
+            ASSERT_EQ(1, radioRsp_sim->carrierRestrictionsResp.allowedCarrierInfoList.size());
             EXPECT_EQ(0, radioRsp_sim->carrierRestrictionsResp.excludedCarrierInfoList.size());
             ASSERT_EQ(std::string("123"),
                       radioRsp_sim->carrierRestrictionsResp.allowedCarrierInfoList[0].mcc);
diff --git a/sensors/aidl/android/hardware/sensors/SensorType.aidl b/sensors/aidl/android/hardware/sensors/SensorType.aidl
index 9098894..4904c3f 100644
--- a/sensors/aidl/android/hardware/sensors/SensorType.aidl
+++ b/sensors/aidl/android/hardware/sensors/SensorType.aidl
@@ -718,8 +718,8 @@
     HEADING = 42,
 
     /**
-     * Base for device manufacturers private sensor types.
-     * These sensor types can't be exposed in the SDK.
+     * Base of the range reserved for device manufacturers' private sensor
+     * types. These sensor types aren't documented in the SDK.
      */
     DEVICE_PRIVATE_BASE = 0x10000,
 }
diff --git a/sensors/aidl/default/include/sensors-impl/Sensors.h b/sensors/aidl/default/include/sensors-impl/Sensors.h
index 2adbc9d..c90db69 100644
--- a/sensors/aidl/default/include/sensors-impl/Sensors.h
+++ b/sensors/aidl/default/include/sensors-impl/Sensors.h
@@ -97,9 +97,13 @@
             return;
         }
         if (mEventQueue->write(&events.front(), events.size())) {
+            if (mEventQueueFlag == nullptr) {
+                // Don't take the wake lock if we can't wake the receiver to avoid holding it
+                // indefinitely.
+                return;
+            }
             mEventQueueFlag->wake(
                     static_cast<uint32_t>(BnSensors::EVENT_QUEUE_FLAG_BITS_READ_AND_PROCESS));
-
             if (wakeup) {
                 // Keep track of the number of outstanding WAKE_UP events in order to properly hold
                 // a wake lock until the framework has secured a wake lock
diff --git a/tests/extension/vibrator/aidl/Android.bp b/tests/extension/vibrator/aidl/Android.bp
index 0306dca..3ef8749 100644
--- a/tests/extension/vibrator/aidl/Android.bp
+++ b/tests/extension/vibrator/aidl/Android.bp
@@ -37,6 +37,15 @@
         java: {
             enabled: false,
         },
+        ndk: {
+            enabled: true,
+        },
+        cpp: {
+            enabled: false,
+        },
+        rust: {
+            enabled: false,
+        },
     },
     frozen: true,
     versions_with_info: [
diff --git a/tests/extension/vibrator/aidl/client/Android.bp b/tests/extension/vibrator/aidl/client/Android.bp
index 284ac74..00510b7 100644
--- a/tests/extension/vibrator/aidl/client/Android.bp
+++ b/tests/extension/vibrator/aidl/client/Android.bp
@@ -16,16 +16,10 @@
     srcs: [
         // system code has the option to use the unstable C++ libbinder API
         // or the NDK one. For maximum code portability, using the ndk client
-        // makes the most sense, but both are provided here as an example.
-        "test-cpp-client.cpp",
+        // makes the most sense.
         "test-ndk-client.cpp",
     ],
     shared_libs: [
-        "libbinder",
-        "libutils",
-        "android.hardware.vibrator-V2-cpp",
-        "android.hardware.tests.extension.vibrator-V1-cpp",
-
         "libbinder_ndk",
         "android.hardware.vibrator-V2-ndk",
         "android.hardware.tests.extension.vibrator-V1-ndk",
diff --git a/tests/extension/vibrator/aidl/client/test-cpp-client.cpp b/tests/extension/vibrator/aidl/client/test-cpp-client.cpp
deleted file mode 100644
index 015a345..0000000
--- a/tests/extension/vibrator/aidl/client/test-cpp-client.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android/hardware/tests/extension/vibrator/ICustomVibrator.h>
-#include <android/hardware/vibrator/IVibrator.h>
-#include <binder/IInterface.h>
-#include <binder/IServiceManager.h>
-#include <gtest/gtest.h>
-
-using android::checked_interface_cast;
-using android::IBinder;
-using android::IInterface;
-using android::OK;
-using android::sp;
-using android::waitForVintfService;
-using android::hardware::tests::extension::vibrator::Directionality;
-using android::hardware::tests::extension::vibrator::ICustomVibrator;
-using android::hardware::vibrator::IVibrator;
-
-TEST(Cpp, CallRootMethod) {
-    sp<IVibrator> vib = waitForVintfService<IVibrator>();
-    ASSERT_NE(nullptr, vib.get());
-    ASSERT_TRUE(vib->off().isOk());
-}
-
-TEST(Cpp, CallExtMethod) {
-    // normally you would want to cache this
-    sp<IVibrator> vib = waitForVintfService<IVibrator>();
-    ASSERT_NE(nullptr, vib.get());
-
-    // getting the extension
-    sp<IBinder> ext;
-    ASSERT_EQ(OK, IInterface::asBinder(vib)->getExtension(&ext));
-    sp<ICustomVibrator> cvib = checked_interface_cast<ICustomVibrator>(ext);
-    ASSERT_NE(nullptr, cvib.get());
-
-    // calling extension method
-    ASSERT_TRUE(cvib->setDirectionality(Directionality::TRANSVERSE).isOk());
-}
diff --git a/tests/extension/vibrator/aidl/client/test-ndk-client.cpp b/tests/extension/vibrator/aidl/client/test-ndk-client.cpp
index c846495..4dd86e8 100644
--- a/tests/extension/vibrator/aidl/client/test-ndk-client.cpp
+++ b/tests/extension/vibrator/aidl/client/test-ndk-client.cpp
@@ -28,7 +28,7 @@
 static const std::string kInstance = std::string() + IVibrator::descriptor + "/default";
 
 TEST(Ndk, CallRootMethod) {
-    SpAIBinder vibBinder = SpAIBinder(AServiceManager_getService(kInstance.c_str()));
+    SpAIBinder vibBinder = SpAIBinder(AServiceManager_waitForService(kInstance.c_str()));
     ASSERT_NE(nullptr, vibBinder.get());
     std::shared_ptr<IVibrator> vib = IVibrator::fromBinder(vibBinder);
     ASSERT_NE(nullptr, vib.get());
@@ -38,7 +38,7 @@
 TEST(Ndk, CallExtMethod) {
     // normally you would want to cache this
     //
-    SpAIBinder vibBinder = SpAIBinder(AServiceManager_getService(kInstance.c_str()));
+    SpAIBinder vibBinder = SpAIBinder(AServiceManager_waitForService(kInstance.c_str()));
     ASSERT_NE(nullptr, vibBinder.get());
     std::shared_ptr<IVibrator> vib = IVibrator::fromBinder(vibBinder);
     ASSERT_NE(nullptr, vib.get());
diff --git a/tests/extension/vibrator/aidl/default/Android.bp b/tests/extension/vibrator/aidl/default/Android.bp
index 5e156af..479b4fd 100644
--- a/tests/extension/vibrator/aidl/default/Android.bp
+++ b/tests/extension/vibrator/aidl/default/Android.bp
@@ -10,11 +10,6 @@
 cc_binary {
     name: "android.hardware.tests.extension.vibrator-service.example",
     relative_install_path: "hw",
-    // normally you implement a service directly, but we are using an implementation
-    // from a library to attach our extension to.
-    static_libs: [
-        "libvibratorexampleimpl",
-    ],
 
     // need to add this in the manifest and to init as well to use, see
     // android.hardware.vibrator-service.example. This binary is being tested
@@ -23,6 +18,7 @@
     vendor: true,
     srcs: [
         "service.cpp",
+        "Vibrator.cpp",
         "CustomVibrator.cpp",
     ],
     shared_libs: [
diff --git a/tests/extension/vibrator/aidl/default/Vibrator.cpp b/tests/extension/vibrator/aidl/default/Vibrator.cpp
new file mode 100644
index 0000000..5084096
--- /dev/null
+++ b/tests/extension/vibrator/aidl/default/Vibrator.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Vibrator.h"
+
+#include <android-base/logging.h>
+
+namespace aidl::android::hardware::vibrator {
+
+ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
+    // basic example with only amplitude control capability
+    *_aidl_return = IVibrator::CAP_AMPLITUDE_CONTROL;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::off() {
+    LOG(INFO) << "Vibrator off";
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs, const std::shared_ptr<IVibratorCallback>&) {
+    LOG(INFO) << "Vibrator on for timeoutMs: " << timeoutMs;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
+    LOG(INFO) << "Vibrator set amplitude: " << amplitude;
+    if (amplitude <= 0.0f || amplitude > 1.0f) {
+        return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::perform(Effect, EffectStrength,
+                                     const std::shared_ptr<IVibratorCallback>&, int32_t*) {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+}
+
+ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_return) {
+    *_aidl_return = {};
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::setExternalControl(bool) {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+}
+
+ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t*) {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+}
+
+ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t*) {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+}
+
+ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive>*) {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+}
+
+ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive, int32_t*) {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+}
+
+ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>&,
+                                     const std::shared_ptr<IVibratorCallback>&) {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+}
+
+ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect>*) {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+}
+
+ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t, Effect, EffectStrength) {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+}
+
+ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t) {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+}
+
+ndk::ScopedAStatus Vibrator::getResonantFrequency(float*) {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+}
+
+ndk::ScopedAStatus Vibrator::getQFactor(float*) {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+}
+
+ndk::ScopedAStatus Vibrator::getFrequencyResolution(float*) {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+}
+
+ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float*) {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+}
+
+ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float>*) {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+}
+
+ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t*) {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+}
+
+ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t*) {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+}
+
+ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking>*) {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+}
+
+ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle>&,
+                                         const std::shared_ptr<IVibratorCallback>&) {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+}
+
+}  // namespace aidl::android::hardware::vibrator
diff --git a/tests/extension/vibrator/aidl/default/Vibrator.h b/tests/extension/vibrator/aidl/default/Vibrator.h
new file mode 100644
index 0000000..80916ae
--- /dev/null
+++ b/tests/extension/vibrator/aidl/default/Vibrator.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/vibrator/BnVibrator.h>
+
+namespace aidl::android::hardware::vibrator {
+
+class Vibrator : public BnVibrator {
+    ndk::ScopedAStatus getCapabilities(int32_t* _aidl_return) override;
+    ndk::ScopedAStatus off() override;
+    ndk::ScopedAStatus on(int32_t timeoutMs,
+                          const std::shared_ptr<IVibratorCallback>& callback) override;
+    ndk::ScopedAStatus perform(Effect effect, EffectStrength strength,
+                               const std::shared_ptr<IVibratorCallback>& callback,
+                               int32_t* _aidl_return) override;
+    ndk::ScopedAStatus getSupportedEffects(std::vector<Effect>* _aidl_return) override;
+    ndk::ScopedAStatus setAmplitude(float amplitude) override;
+    ndk::ScopedAStatus setExternalControl(bool enabled) override;
+    ndk::ScopedAStatus getCompositionDelayMax(int32_t* maxDelayMs);
+    ndk::ScopedAStatus getCompositionSizeMax(int32_t* maxSize);
+    ndk::ScopedAStatus getSupportedPrimitives(std::vector<CompositePrimitive>* supported) override;
+    ndk::ScopedAStatus getPrimitiveDuration(CompositePrimitive primitive,
+                                            int32_t* durationMs) override;
+    ndk::ScopedAStatus compose(const std::vector<CompositeEffect>& composite,
+                               const std::shared_ptr<IVibratorCallback>& callback) override;
+    ndk::ScopedAStatus getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return) override;
+    ndk::ScopedAStatus alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) override;
+    ndk::ScopedAStatus alwaysOnDisable(int32_t id) override;
+    ndk::ScopedAStatus getResonantFrequency(float* resonantFreqHz) override;
+    ndk::ScopedAStatus getQFactor(float* qFactor) override;
+    ndk::ScopedAStatus getFrequencyResolution(float* freqResolutionHz) override;
+    ndk::ScopedAStatus getFrequencyMinimum(float* freqMinimumHz) override;
+    ndk::ScopedAStatus getBandwidthAmplitudeMap(std::vector<float>* _aidl_return) override;
+    ndk::ScopedAStatus getPwlePrimitiveDurationMax(int32_t* durationMs) override;
+    ndk::ScopedAStatus getPwleCompositionSizeMax(int32_t* maxSize) override;
+    ndk::ScopedAStatus getSupportedBraking(std::vector<Braking>* supported) override;
+    ndk::ScopedAStatus composePwle(const std::vector<PrimitivePwle>& composite,
+                                   const std::shared_ptr<IVibratorCallback>& callback) override;
+};
+
+}  // namespace aidl::android::hardware::vibrator
diff --git a/tests/extension/vibrator/aidl/default/service.cpp b/tests/extension/vibrator/aidl/default/service.cpp
index 16290df..5917d0f 100644
--- a/tests/extension/vibrator/aidl/default/service.cpp
+++ b/tests/extension/vibrator/aidl/default/service.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#include <vibrator-impl/Vibrator.h>
 #include "CustomVibrator.h"
+#include "Vibrator.h"
 
 #include <android-base/logging.h>
 #include <android/binder_manager.h>
diff --git a/tv/tuner/aidl/Android.bp b/tv/tuner/aidl/Android.bp
index 6cbf362..efcc327 100644
--- a/tv/tuner/aidl/Android.bp
+++ b/tv/tuner/aidl/Android.bp
@@ -41,6 +41,5 @@
         },
 
     ],
-    frozen: true,
 
 }
diff --git a/tv/tuner/aidl/default/Android.bp b/tv/tuner/aidl/default/Android.bp
index 4f0d04b..a76a653 100644
--- a/tv/tuner/aidl/default/Android.bp
+++ b/tv/tuner/aidl/default/Android.bp
@@ -29,7 +29,7 @@
     ],
     shared_libs: [
         "android.hardware.common.fmq-V1-ndk",
-        "android.hardware.tv.tuner-V2-ndk",
+        "android.hardware.tv.tuner-V3-ndk",
         "libbase",
         "libbinder_ndk",
         "libcutils",
diff --git a/tv/tuner/aidl/vts/functional/Android.bp b/tv/tuner/aidl/vts/functional/Android.bp
index ae4f598..4c961ad 100644
--- a/tv/tuner/aidl/vts/functional/Android.bp
+++ b/tv/tuner/aidl/vts/functional/Android.bp
@@ -56,7 +56,7 @@
         "android.hardware.cas-V1-ndk",
         "android.hardware.common-V2-ndk",
         "android.hardware.common.fmq-V1-ndk",
-        "android.hardware.tv.tuner-V2-ndk",
+        "android.hardware.tv.tuner-V3-ndk",
         "libaidlcommonsupport",
         "libfmq",
         "libcutils",
diff --git a/vibrator/aidl/Android.bp b/vibrator/aidl/Android.bp
index d3b72ee..b2d98f5 100644
--- a/vibrator/aidl/Android.bp
+++ b/vibrator/aidl/Android.bp
@@ -15,15 +15,33 @@
     srcs: [
         "android/hardware/vibrator/*.aidl",
     ],
+    headers: [
+        "PersistableBundle_aidl",
+    ],
     stability: "vintf",
     backend: {
         java: {
             sdk_version: "system_current",
         },
+        ndk: {
+            enabled: true,
+        },
+        cpp: {
+            enabled: false,
+        },
+        rust: {
+            enabled: false,
+        },
     },
-    versions: [
-        "1",
-        "2",
+    versions_with_info: [
+        {
+            version: "1",
+            imports: [],
+        },
+        {
+            version: "2",
+            imports: [],
+        },
     ],
-    frozen: true,
+    frozen: false,
 }
diff --git a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibrator.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibrator.aidl
index b7afb66..0dcc657 100644
--- a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibrator.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibrator.aidl
@@ -58,15 +58,23 @@
   int getPwleCompositionSizeMax();
   android.hardware.vibrator.Braking[] getSupportedBraking();
   void composePwle(in android.hardware.vibrator.PrimitivePwle[] composite, in android.hardware.vibrator.IVibratorCallback callback);
-  const int CAP_ON_CALLBACK = 1;
-  const int CAP_PERFORM_CALLBACK = 2;
-  const int CAP_AMPLITUDE_CONTROL = 4;
-  const int CAP_EXTERNAL_CONTROL = 8;
-  const int CAP_EXTERNAL_AMPLITUDE_CONTROL = 16;
-  const int CAP_COMPOSE_EFFECTS = 32;
-  const int CAP_ALWAYS_ON_CONTROL = 64;
-  const int CAP_GET_RESONANT_FREQUENCY = 128;
-  const int CAP_GET_Q_FACTOR = 256;
-  const int CAP_FREQUENCY_CONTROL = 512;
-  const int CAP_COMPOSE_PWLE_EFFECTS = 1024;
+  void performVendorEffect(in android.hardware.vibrator.VendorEffect vendorEffect, in android.hardware.vibrator.IVibratorCallback callback);
+  List<android.hardware.vibrator.PwleV2OutputMapEntry> getPwleV2FrequencyToOutputAccelerationMap();
+  int getPwleV2PrimitiveDurationMaxMillis();
+  int getPwleV2CompositionSizeMax();
+  int getPwleV2PrimitiveDurationMinMillis();
+  void composePwleV2(in android.hardware.vibrator.PwleV2Primitive[] composite, in android.hardware.vibrator.IVibratorCallback callback);
+  const int CAP_ON_CALLBACK = (1 << 0) /* 1 */;
+  const int CAP_PERFORM_CALLBACK = (1 << 1) /* 2 */;
+  const int CAP_AMPLITUDE_CONTROL = (1 << 2) /* 4 */;
+  const int CAP_EXTERNAL_CONTROL = (1 << 3) /* 8 */;
+  const int CAP_EXTERNAL_AMPLITUDE_CONTROL = (1 << 4) /* 16 */;
+  const int CAP_COMPOSE_EFFECTS = (1 << 5) /* 32 */;
+  const int CAP_ALWAYS_ON_CONTROL = (1 << 6) /* 64 */;
+  const int CAP_GET_RESONANT_FREQUENCY = (1 << 7) /* 128 */;
+  const int CAP_GET_Q_FACTOR = (1 << 8) /* 256 */;
+  const int CAP_FREQUENCY_CONTROL = (1 << 9) /* 512 */;
+  const int CAP_COMPOSE_PWLE_EFFECTS = (1 << 10) /* 1024 */;
+  const int CAP_PERFORM_VENDOR_EFFECTS = (1 << 11) /* 2048 */;
+  const int CAP_COMPOSE_PWLE_EFFECTS_V2 = (1 << 12) /* 4096 */;
 }
diff --git a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorManager.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorManager.aidl
index 290c68d..ef5794c 100644
--- a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorManager.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorManager.aidl
@@ -40,12 +40,12 @@
   void prepareSynced(in int[] vibratorIds);
   void triggerSynced(in android.hardware.vibrator.IVibratorCallback callback);
   void cancelSynced();
-  const int CAP_SYNC = 1;
-  const int CAP_PREPARE_ON = 2;
-  const int CAP_PREPARE_PERFORM = 4;
-  const int CAP_PREPARE_COMPOSE = 8;
-  const int CAP_MIXED_TRIGGER_ON = 16;
-  const int CAP_MIXED_TRIGGER_PERFORM = 32;
-  const int CAP_MIXED_TRIGGER_COMPOSE = 64;
-  const int CAP_TRIGGER_CALLBACK = 128;
+  const int CAP_SYNC = (1 << 0) /* 1 */;
+  const int CAP_PREPARE_ON = (1 << 1) /* 2 */;
+  const int CAP_PREPARE_PERFORM = (1 << 2) /* 4 */;
+  const int CAP_PREPARE_COMPOSE = (1 << 3) /* 8 */;
+  const int CAP_MIXED_TRIGGER_ON = (1 << 4) /* 16 */;
+  const int CAP_MIXED_TRIGGER_PERFORM = (1 << 5) /* 32 */;
+  const int CAP_MIXED_TRIGGER_COMPOSE = (1 << 6) /* 64 */;
+  const int CAP_TRIGGER_CALLBACK = (1 << 7) /* 128 */;
 }
diff --git a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/EnrollmentProgressStep.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/PwleV2OutputMapEntry.aidl
similarity index 88%
rename from biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/EnrollmentProgressStep.aidl
rename to vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/PwleV2OutputMapEntry.aidl
index 173ac17..a5eda52 100644
--- a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/EnrollmentProgressStep.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/PwleV2OutputMapEntry.aidl
@@ -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.biometrics.fingerprint;
-/* @hide */
+package android.hardware.vibrator;
 @VintfStability
-parcelable EnrollmentProgressStep {
-  int durationMs;
-  android.hardware.biometrics.fingerprint.AcquiredInfoAndVendorCode[] acquiredInfoAndVendorCodes;
+parcelable PwleV2OutputMapEntry {
+  float frequencyHz;
+  float maxOutputAccelerationGs;
 }
diff --git a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/EnrollmentProgressStep.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/PwleV2Primitive.aidl
similarity index 88%
copy from biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/EnrollmentProgressStep.aidl
copy to vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/PwleV2Primitive.aidl
index 173ac17..c4f3ea9 100644
--- a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/EnrollmentProgressStep.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/PwleV2Primitive.aidl
@@ -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.biometrics.fingerprint;
-/* @hide */
+package android.hardware.vibrator;
 @VintfStability
-parcelable EnrollmentProgressStep {
-  int durationMs;
-  android.hardware.biometrics.fingerprint.AcquiredInfoAndVendorCode[] acquiredInfoAndVendorCodes;
+parcelable PwleV2Primitive {
+  float amplitude;
+  float frequencyHz;
+  int timeMillis;
 }
diff --git a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/EnrollmentProgressStep.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/VendorEffect.aidl
similarity index 86%
copy from biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/EnrollmentProgressStep.aidl
copy to vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/VendorEffect.aidl
index 173ac17..62a7380 100644
--- a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/EnrollmentProgressStep.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/VendorEffect.aidl
@@ -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.biometrics.fingerprint;
-/* @hide */
+package android.hardware.vibrator;
 @VintfStability
-parcelable EnrollmentProgressStep {
-  int durationMs;
-  android.hardware.biometrics.fingerprint.AcquiredInfoAndVendorCode[] acquiredInfoAndVendorCodes;
+parcelable VendorEffect {
+  android.os.PersistableBundle vendorData;
+  android.hardware.vibrator.EffectStrength strength = android.hardware.vibrator.EffectStrength.MEDIUM;
+  float scale;
+  float vendorScale;
 }
diff --git a/vibrator/aidl/android/hardware/vibrator/Braking.aidl b/vibrator/aidl/android/hardware/vibrator/Braking.aidl
index 2bc51db..f934ff2 100644
--- a/vibrator/aidl/android/hardware/vibrator/Braking.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/Braking.aidl
@@ -23,12 +23,12 @@
      * No braking mechanism used.
      * This is the default if the hardware does not support any braking mechanism.
      */
-    NONE,
+    NONE = 0,
     /**
      * Closed-loop active braking.
      *
      * This effect should produce a sharp, crisp end to the waveform
      * Support is optional.
      */
-    CLAB,
+    CLAB = 1,
 }
diff --git a/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl b/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl
index 5314898..5f8ee8d 100644
--- a/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl
@@ -24,13 +24,13 @@
      *
      * Support is required.
      */
-    NOOP,
+    NOOP = 0,
     /**
      * This effect should produce a sharp, crisp click sensation.
      *
      * Support is required.
      */
-    CLICK,
+    CLICK = 1,
     /**
      * A haptic effect that simulates downwards movement with gravity. Often
      * followed by extra energy of hitting and reverberation to augment
@@ -38,43 +38,43 @@
      *
      * Support is optional.
      */
-    THUD,
+    THUD = 2,
     /**
      * A haptic effect that simulates spinning momentum.
      *
      * Support is optional.
      */
-    SPIN,
+    SPIN = 3,
     /**
      * A haptic effect that simulates quick upward movement against gravity.
      *
      * Support is required.
      */
-    QUICK_RISE,
+    QUICK_RISE = 4,
     /**
      * A haptic effect that simulates slow upward movement against gravity.
      *
      * Support is required.
      */
-    SLOW_RISE,
+    SLOW_RISE = 5,
     /**
      * A haptic effect that simulates quick downwards movement with gravity.
      *
      * Support is required.
      */
-    QUICK_FALL,
+    QUICK_FALL = 6,
     /**
      * This very short effect should produce a light crisp sensation intended
      * to be used repetitively for dynamic feedback.
      *
      * Support is required.
      */
-    LIGHT_TICK,
+    LIGHT_TICK = 7,
     /**
      * This very short low frequency effect should produce a light crisp sensation intended
      * to be used repetitively for dynamic feedback.
      *
      * Support is required.
      */
-    LOW_TICK,
+    LOW_TICK = 8,
 }
diff --git a/vibrator/aidl/android/hardware/vibrator/Effect.aidl b/vibrator/aidl/android/hardware/vibrator/Effect.aidl
index c60bfe9..f5cf9e3 100644
--- a/vibrator/aidl/android/hardware/vibrator/Effect.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/Effect.aidl
@@ -24,57 +24,57 @@
      *
      * This effect should produce a sharp, crisp click sensation.
      */
-    CLICK,
+    CLICK = 0,
     /**
      * A double click effect.
      *
      * This effect should produce two sequential sharp, crisp click sensations with a minimal
      * amount of time between them.
      */
-    DOUBLE_CLICK,
+    DOUBLE_CLICK = 1,
     /**
      * A tick effect.
      *
      * This effect should produce a soft, short sensation, like the tick of a clock.
      */
-    TICK,
+    TICK = 2,
     /**
      * A thud effect.
      *
      * This effect should solid feeling bump, like the depression of a heavy mechanical button.
      */
-    THUD,
+    THUD = 3,
     /**
      * A pop effect.
      *
      * A short, quick burst effect.
      */
-    POP,
+    POP = 4,
     /**
      * A heavy click effect.
      *
      * This should produce a sharp striking sensation, like a click but stronger.
      */
-    HEAVY_CLICK,
+    HEAVY_CLICK = 5,
     /**
      * Ringtone patterns. They may correspond with the device's ringtone audio, or may just be a
      * pattern that can be played as a ringtone with any audio, depending on the device.
      */
-    RINGTONE_1,
-    RINGTONE_2,
-    RINGTONE_3,
-    RINGTONE_4,
-    RINGTONE_5,
-    RINGTONE_6,
-    RINGTONE_7,
-    RINGTONE_8,
-    RINGTONE_9,
-    RINGTONE_10,
-    RINGTONE_11,
-    RINGTONE_12,
-    RINGTONE_13,
-    RINGTONE_14,
-    RINGTONE_15,
+    RINGTONE_1 = 6,
+    RINGTONE_2 = 7,
+    RINGTONE_3 = 8,
+    RINGTONE_4 = 9,
+    RINGTONE_5 = 10,
+    RINGTONE_6 = 11,
+    RINGTONE_7 = 12,
+    RINGTONE_8 = 13,
+    RINGTONE_9 = 14,
+    RINGTONE_10 = 15,
+    RINGTONE_11 = 16,
+    RINGTONE_12 = 17,
+    RINGTONE_13 = 18,
+    RINGTONE_14 = 19,
+    RINGTONE_15 = 20,
     /**
      * A soft tick effect meant to be played as a texture.
      *
@@ -82,5 +82,5 @@
      * are expected to be played multiple times in quick succession, replicating a specific
      * texture to the user as a form of haptic feedback.
      */
-    TEXTURE_TICK,
+    TEXTURE_TICK = 21,
 }
diff --git a/vibrator/aidl/android/hardware/vibrator/EffectStrength.aidl b/vibrator/aidl/android/hardware/vibrator/EffectStrength.aidl
index 66f70e5..c6a78d4 100644
--- a/vibrator/aidl/android/hardware/vibrator/EffectStrength.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/EffectStrength.aidl
@@ -19,7 +19,7 @@
 @VintfStability
 @Backing(type="byte")
 enum EffectStrength {
-    LIGHT,
-    MEDIUM,
-    STRONG,
+    LIGHT = 0,
+    MEDIUM = 1,
+    STRONG = 2,
 }
diff --git a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
index b4e7e44..11f36ba 100644
--- a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
@@ -16,13 +16,16 @@
 
 package android.hardware.vibrator;
 
-import android.hardware.vibrator.IVibratorCallback;
 import android.hardware.vibrator.Braking;
-import android.hardware.vibrator.Effect;
-import android.hardware.vibrator.EffectStrength;
 import android.hardware.vibrator.CompositeEffect;
 import android.hardware.vibrator.CompositePrimitive;
+import android.hardware.vibrator.Effect;
+import android.hardware.vibrator.EffectStrength;
+import android.hardware.vibrator.IVibratorCallback;
 import android.hardware.vibrator.PrimitivePwle;
+import android.hardware.vibrator.PwleV2OutputMapEntry;
+import android.hardware.vibrator.PwleV2Primitive;
+import android.hardware.vibrator.VendorEffect;
 
 @VintfStability
 interface IVibrator {
@@ -70,6 +73,14 @@
      * Whether composePwle is supported.
      */
     const int CAP_COMPOSE_PWLE_EFFECTS = 1 << 10;
+    /**
+     * Whether perform w/ vendor effect is supported.
+     */
+    const int CAP_PERFORM_VENDOR_EFFECTS = 1 << 11;
+    /**
+     * Whether composePwleV2 for PwlePrimitives is supported.
+     */
+    const int CAP_COMPOSE_PWLE_EFFECTS_V2 = 1 << 12;
 
     /**
      * Determine capabilities of the vibrator HAL (CAP_* mask)
@@ -359,4 +370,103 @@
      * @param composite Array of PWLEs.
      */
     void composePwle(in PrimitivePwle[] composite, in IVibratorCallback callback);
+
+    /**
+     * Fire off a vendor-defined haptic event.
+     *
+     * This may not be supported and this support is reflected in
+     * getCapabilities (CAP_PERFORM_VENDOR_EFFECTS).
+     *
+     * The duration of the effect is unknown and can be undefined for looping effects.
+     * IVibratorCallback.onComplete() support is required for this API.
+     *
+     * Doing this operation while the vibrator is already on is undefined behavior. Clients should
+     * explicitly call off.
+     *
+     * @param effect The vendor data representing the effect to be performed.
+     * @param callback A callback used to inform Frameworks of state change.
+     * @throws :
+     *         - EX_UNSUPPORTED_OPERATION if unsupported, as reflected by getCapabilities.
+     *         - EX_ILLEGAL_ARGUMENT for bad framework parameters, e.g. scale or effect strength.
+     *         - EX_SERVICE_SPECIFIC for bad vendor data, vibration is not triggered.
+     */
+    void performVendorEffect(in VendorEffect vendorEffect, in IVibratorCallback callback);
+
+    /**
+     * Retrieves a mapping of vibration frequency (Hz) to the maximum achievable output
+     * acceleration (Gs) the device can reach at that frequency.
+     *
+     * The map, represented as a list of `PwleV2OutputMapEntry` (frequency, output acceleration)
+     * pairs, defines the device's frequency response. The platform uses the minimum and maximum
+     * frequency values to determine the supported input range for `IVibrator.composePwleV2`.
+     * Output acceleration values are used to identify a frequency range suitable to safely play
+     * perceivable vibrations with a simple API. The map is also exposed for developers using an
+     * advanced API.
+     *
+     * The platform does not impose specific requirements on map resolution which can vary
+     * depending on the shape of device output curve. The values will be linearly interpolated
+     * during lookups. The platform will provide a simple API, defined by the first frequency range
+     * where output acceleration consistently exceeds a minimum threshold of 10 db SL.
+     *
+     *
+     * This may not be supported and this support is reflected in getCapabilities
+     * (CAP_COMPOSE_PWLE_EFFECTS_V2). If this is supported, it's expected to be non-empty and
+     * describe a valid non-empty frequency range where the simple API can be defined
+     * (i.e. a range where the output acceleration is always above 10 db SL).
+     *
+     * @return A list of map entries representing the frequency to max acceleration
+     *         mapping.
+     * @throws EX_UNSUPPORTED_OPERATION if unsupported, as reflected by getCapabilities.
+     */
+    List<PwleV2OutputMapEntry> getPwleV2FrequencyToOutputAccelerationMap();
+
+    /**
+     * Retrieve the maximum duration allowed for any primitive PWLE in units of
+     * milliseconds.
+     *
+     * This may not be supported and this support is reflected in
+     * getCapabilities (CAP_COMPOSE_PWLE_EFFECTS_V2).
+     *
+     * @return The maximum duration allowed for a single PrimitivePwle. Non-zero value if supported.
+     * @throws EX_UNSUPPORTED_OPERATION if unsupported, as reflected by getCapabilities.
+     */
+    int getPwleV2PrimitiveDurationMaxMillis();
+
+    /**
+     * Retrieve the maximum number of PWLE primitives input supported by IVibrator.composePwleV2.
+     *
+     * This may not be supported and this support is reflected in
+     * getCapabilities (CAP_COMPOSE_PWLE_EFFECTS_V2). Devices supporting PWLE effects must
+     * support effects with at least 16 PwleV2Primitive.
+     *
+     * @return The maximum count allowed. Non-zero value if supported.
+     * @throws EX_UNSUPPORTED_OPERATION if unsupported, as reflected by getCapabilities.
+     */
+    int getPwleV2CompositionSizeMax();
+
+    /**
+     * Retrieves the minimum duration (in milliseconds) of any segment within a
+     * PWLE effect. Devices supporting PWLE effects must support a minimum ramp
+     * time of 20 milliseconds.
+     *
+     * This may not be supported and this support is reflected in
+     * getCapabilities (CAP_COMPOSE_PWLE_EFFECTS_V2).
+     *
+     * @return The minimum duration allowed for a single PrimitivePwle. Non-zero value if supported.
+     * @throws EX_UNSUPPORTED_OPERATION if unsupported, as reflected by getCapabilities.
+     */
+    int getPwleV2PrimitiveDurationMinMillis();
+
+    /**
+     * Play composed sequence of chirps with optional callback upon completion.
+     *
+     * This may not be supported and this support is reflected in
+     * getCapabilities (CAP_COMPOSE_PWLE_EFFECTS_V2).
+     *
+     * Doing this operation while the vibrator is already on is undefined behavior. Clients should
+     * explicitly call off. IVibratorCallback.onComplete() support is required for this API.
+     *
+     * @param composite An array of primitives that represents a PWLE (Piecewise-Linear Envelope).
+     */
+    void composePwleV2(in PwleV2Primitive[] composite, in IVibratorCallback callback);
 }
diff --git a/vibrator/aidl/android/hardware/vibrator/PwleV2OutputMapEntry.aidl b/vibrator/aidl/android/hardware/vibrator/PwleV2OutputMapEntry.aidl
new file mode 100644
index 0000000..a8db87c
--- /dev/null
+++ b/vibrator/aidl/android/hardware/vibrator/PwleV2OutputMapEntry.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.vibrator;
+
+@VintfStability
+parcelable PwleV2OutputMapEntry {
+    /**
+     * Absolute frequency point in the units of hertz
+     *
+     */
+    float frequencyHz;
+
+    /**
+     * Max output acceleration for the specified frequency in units of Gs.
+     *
+     * This value represents the maximum safe output acceleration (in Gs) achievable at the
+     * specified frequency, typically determined during calibration. The actual output acceleration
+     * is assumed to scale linearly with the input amplitude within the range of [0, 1].
+     */
+    float maxOutputAccelerationGs;
+}
diff --git a/vibrator/aidl/android/hardware/vibrator/PwleV2Primitive.aidl b/vibrator/aidl/android/hardware/vibrator/PwleV2Primitive.aidl
new file mode 100644
index 0000000..bd7bec6
--- /dev/null
+++ b/vibrator/aidl/android/hardware/vibrator/PwleV2Primitive.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.vibrator;
+
+@VintfStability
+parcelable PwleV2Primitive {
+    /**
+     * Input amplitude ranges from 0.0 (inclusive) to 1.0 (inclusive), representing the relative
+     * input value. Actual output acceleration depends on frequency and device response curve
+     * (see IVibrator.getPwleV2FrequencyToOutputAccelerationMap for max values).
+     *
+     * Input amplitude linearly maps to output acceleration (e.g., 0.5 amplitude yields half the
+     * max acceleration for that frequency).
+     *
+     * 0.0 represents no output acceleration amplitude
+     * 1.0 represents the maximum achievable strength for each frequency, as determined by the
+     *     actuator response curve
+     */
+    float amplitude;
+
+    /**
+     * Absolute frequency point in the units of hertz
+     *
+     * Values are within the continuous inclusive frequency range defined by
+     * IVibrator#getPwleV2FrequencyToOutputAccelerationMap.
+     */
+    float frequencyHz;
+
+    /* Total time from the previous PWLE point to the current one in units of milliseconds. */
+    int timeMillis;
+}
diff --git a/vibrator/aidl/android/hardware/vibrator/VendorEffect.aidl b/vibrator/aidl/android/hardware/vibrator/VendorEffect.aidl
new file mode 100644
index 0000000..6b1af53
--- /dev/null
+++ b/vibrator/aidl/android/hardware/vibrator/VendorEffect.aidl
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.vibrator;
+
+import android.hardware.vibrator.EffectStrength;
+import android.os.PersistableBundle;
+
+@VintfStability
+parcelable VendorEffect {
+    /**
+     * Vendor data describing the haptic effect. Expected fields should be defined by the vendor.
+     *
+     * Vendors can use this as a platform extension point for experimental hardware capabilities,
+     * but they are strongly discouraged from using it as an alternative to the AOSP support for
+     * stable vibrator APIs. Implemenitng vendor-specific custom effects outside the platform APIs
+     * will hinder portability for the code and overall user experience.
+     *
+     * Vendors are encouraged to upstream new capabilities to the IVibrator surface once it has
+     * matured into a stable interface.
+     */
+    PersistableBundle vendorData;
+
+    /**
+     * The intensity of the haptic effect.
+     *
+     * This value is defined by discrete scale levels that represents the intensity of this haptic
+     * effect. This is a discrete representation of the scale parameter below.
+     */
+    EffectStrength strength = EffectStrength.MEDIUM;
+
+    /**
+     * The intensity of the haptic effect.
+     *
+     * This value is defined by continuous scale that represents the intensity of this haptic
+     * effect. The vendor implementation can follow the platform scaling function or customize the
+     * implementation to their needs. This is a continuous representation of the strength parameter
+     * above.
+     *
+     * Values in [0,1) should scale down. Values > 1 should scale up within hardware bounds.
+     */
+    float scale;
+
+    /**
+     * The dynamic scale parameter provided by the vendor vibrator controller.
+     *
+     * This value is the same provided by the vendor to the platform IVibratorControlService and
+     * should be applied on top of the effect intensity provided by the strength/scale fields.
+     * The vendor can use this to dynamically adapt the haptic effect intensity to the device state.
+     *
+     * See frameworks/hardware/interfaces/vibrator for more documentation on vendor vibrator
+     * controller, and ScaleParam for more about this scale parameter.
+     *
+     * Values in [0,1) should scale down. Values > 1 should scale up within hardware bounds.
+     */
+    float vendorScale;
+}
diff --git a/vibrator/aidl/default/Android.bp b/vibrator/aidl/default/Android.bp
index 596c1a6..b25a5ba 100644
--- a/vibrator/aidl/default/Android.bp
+++ b/vibrator/aidl/default/Android.bp
@@ -15,7 +15,7 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
-        "android.hardware.vibrator-V2-ndk",
+        "android.hardware.vibrator-V3-ndk",
     ],
     export_include_dirs: ["include"],
     srcs: [
@@ -49,7 +49,7 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
-        "android.hardware.vibrator-V2-ndk",
+        "android.hardware.vibrator-V3-ndk",
     ],
     static_libs: [
         "libvibratorexampleimpl",
@@ -62,7 +62,7 @@
     host_supported: true,
     defaults: ["service_fuzzer_defaults"],
     static_libs: [
-        "android.hardware.vibrator-V2-ndk",
+        "android.hardware.vibrator-V3-ndk",
         "liblog",
         "libvibratorexampleimpl",
     ],
diff --git a/vibrator/aidl/default/Vibrator.cpp b/vibrator/aidl/default/Vibrator.cpp
index 01602ab..4f8c2b8 100644
--- a/vibrator/aidl/default/Vibrator.cpp
+++ b/vibrator/aidl/default/Vibrator.cpp
@@ -27,9 +27,12 @@
 static constexpr int32_t COMPOSE_DELAY_MAX_MS = 1000;
 static constexpr int32_t COMPOSE_SIZE_MAX = 256;
 static constexpr int32_t COMPOSE_PWLE_SIZE_MAX = 127;
+static constexpr int32_t COMPOSE_PWLE_V2_SIZE_MAX = 16;
 
 static constexpr float Q_FACTOR = 11.0;
 static constexpr int32_t COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS = 16383;
+static constexpr int32_t COMPOSE_PWLE_V2_PRIMITIVE_DURATION_MAX_MS = 1000;
+static constexpr int32_t COMPOSE_PWLE_V2_PRIMITIVE_DURATION_MIN_MS = 20;
 static constexpr float PWLE_LEVEL_MIN = 0.0;
 static constexpr float PWLE_LEVEL_MAX = 1.0;
 static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 1.0;
@@ -39,14 +42,30 @@
 static constexpr float PWLE_BW_MAP_SIZE =
         1 + ((PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ);
 
+// Service specific error code used for vendor vibration effects.
+static constexpr int32_t ERROR_CODE_INVALID_DURATION = 1;
+
 ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
     LOG(VERBOSE) << "Vibrator reporting capabilities";
-    *_aidl_return = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK |
-                    IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_EXTERNAL_CONTROL |
-                    IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL | IVibrator::CAP_COMPOSE_EFFECTS |
-                    IVibrator::CAP_ALWAYS_ON_CONTROL | IVibrator::CAP_GET_RESONANT_FREQUENCY |
-                    IVibrator::CAP_GET_Q_FACTOR | IVibrator::CAP_FREQUENCY_CONTROL |
-                    IVibrator::CAP_COMPOSE_PWLE_EFFECTS;
+    std::lock_guard lock(mMutex);
+    if (mCapabilities == 0) {
+        if (!getInterfaceVersion(&mVersion).isOk()) {
+            return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_STATE));
+        }
+        mCapabilities = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK |
+                        IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_EXTERNAL_CONTROL |
+                        IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL | IVibrator::CAP_COMPOSE_EFFECTS |
+                        IVibrator::CAP_ALWAYS_ON_CONTROL | IVibrator::CAP_GET_RESONANT_FREQUENCY |
+                        IVibrator::CAP_GET_Q_FACTOR | IVibrator::CAP_FREQUENCY_CONTROL |
+                        IVibrator::CAP_COMPOSE_PWLE_EFFECTS;
+
+        if (mVersion >= 3) {
+            mCapabilities |= (IVibrator::CAP_PERFORM_VENDOR_EFFECTS |
+                              IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2);
+        }
+    }
+
+    *_aidl_return = mCapabilities;
     return ndk::ScopedAStatus::ok();
 }
 
@@ -102,6 +121,47 @@
     return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus Vibrator::performVendorEffect(
+        const VendorEffect& effect, const std::shared_ptr<IVibratorCallback>& callback) {
+    LOG(VERBOSE) << "Vibrator perform vendor effect";
+    int32_t capabilities = 0;
+    if (!getCapabilities(&capabilities).isOk()) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    if ((capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS) == 0) {
+        return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+    }
+    EffectStrength strength = effect.strength;
+    if (strength != EffectStrength::LIGHT && strength != EffectStrength::MEDIUM &&
+        strength != EffectStrength::STRONG) {
+        return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
+    }
+    float scale = effect.scale;
+    if (scale <= 0) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    float vendorScale = effect.vendorScale;
+    if (vendorScale <= 0) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+
+    int32_t durationMs = 0;
+    if (!effect.vendorData.getInt("DURATION_MS", &durationMs) || durationMs <= 0) {
+        return ndk::ScopedAStatus::fromServiceSpecificError(ERROR_CODE_INVALID_DURATION);
+    }
+
+    if (callback != nullptr) {
+        std::thread([callback, durationMs] {
+            LOG(VERBOSE) << "Starting perform on another thread for durationMs:" << durationMs;
+            usleep(durationMs * 1000);
+            LOG(VERBOSE) << "Notifying perform vendor effect complete";
+            callback->onComplete();
+        }).detach();
+    }
+
+    return ndk::ScopedAStatus::ok();
+}
+
 ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_return) {
     *_aidl_return = {Effect::CLICK, Effect::TICK};
     return ndk::ScopedAStatus::ok();
@@ -412,6 +472,122 @@
     return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus Vibrator::getPwleV2FrequencyToOutputAccelerationMap(
+        std::vector<PwleV2OutputMapEntry>* _aidl_return) {
+    std::vector<PwleV2OutputMapEntry> frequencyToOutputAccelerationMap;
+
+    std::vector<std::pair<float, float>> frequencyToOutputAccelerationData = {
+            {30.0f, 0.01f},  {46.0f, 0.09f},  {50.0f, 0.1f},   {55.0f, 0.12f},  {62.0f, 0.66f},
+            {83.0f, 0.82f},  {85.0f, 0.85f},  {92.0f, 1.05f},  {107.0f, 1.63f}, {115.0f, 1.72f},
+            {123.0f, 1.81f}, {135.0f, 2.23f}, {144.0f, 2.47f}, {145.0f, 2.5f},  {150.0f, 3.0f},
+            {175.0f, 2.51f}, {181.0f, 2.41f}, {190.0f, 2.28f}, {200.0f, 2.08f}, {204.0f, 1.96f},
+            {205.0f, 1.9f},  {224.0f, 1.7f},  {235.0f, 1.5f},  {242.0f, 1.46f}, {253.0f, 1.41f},
+            {263.0f, 1.39f}, {65.0f, 1.38f},  {278.0f, 1.37f}, {294.0f, 1.35f}, {300.0f, 1.34f}};
+    for (const auto& entry : frequencyToOutputAccelerationData) {
+        frequencyToOutputAccelerationMap.push_back(
+                PwleV2OutputMapEntry(/*frequency=*/entry.first,
+                                     /*maxOutputAcceleration=*/entry.second));
+    }
+
+    *_aidl_return = frequencyToOutputAccelerationMap;
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getPwleV2PrimitiveDurationMaxMillis(int32_t* maxDurationMs) {
+    *maxDurationMs = COMPOSE_PWLE_V2_PRIMITIVE_DURATION_MAX_MS;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getPwleV2CompositionSizeMax(int32_t* maxSize) {
+    *maxSize = COMPOSE_PWLE_V2_SIZE_MAX;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getPwleV2PrimitiveDurationMinMillis(int32_t* minDurationMs) {
+    *minDurationMs = COMPOSE_PWLE_V2_PRIMITIVE_DURATION_MIN_MS;
+    return ndk::ScopedAStatus::ok();
+}
+
+float getPwleV2FrequencyMinHz(std::vector<PwleV2OutputMapEntry> frequencyToOutputAccelerationMap) {
+    if (frequencyToOutputAccelerationMap.empty()) {
+        return 0.0f;
+    }
+
+    float minFrequency = frequencyToOutputAccelerationMap[0].frequencyHz;
+
+    for (const auto& entry : frequencyToOutputAccelerationMap) {
+        if (entry.frequencyHz < minFrequency) {
+            minFrequency = entry.frequencyHz;
+        }
+    }
+
+    return minFrequency;
+}
+
+float getPwleV2FrequencyMaxHz(std::vector<PwleV2OutputMapEntry> frequencyToOutputAccelerationMap) {
+    if (frequencyToOutputAccelerationMap.empty()) {
+        return 0.0f;
+    }
+
+    float maxFrequency = frequencyToOutputAccelerationMap[0].frequencyHz;
+
+    for (const auto& entry : frequencyToOutputAccelerationMap) {
+        if (entry.frequencyHz > maxFrequency) {
+            maxFrequency = entry.frequencyHz;
+        }
+    }
+
+    return maxFrequency;
+}
+
+ndk::ScopedAStatus Vibrator::composePwleV2(const std::vector<PwleV2Primitive>& composite,
+                                           const std::shared_ptr<IVibratorCallback>& callback) {
+    int32_t capabilities = 0;
+    if (!getCapabilities(&capabilities).isOk()) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    if ((capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2) == 0) {
+        return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+    }
+
+    int compositionSizeMax;
+    getPwleV2CompositionSizeMax(&compositionSizeMax);
+    if (composite.size() <= 0 || composite.size() > compositionSizeMax) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+
+    int32_t totalEffectDuration = 0;
+    std::vector<PwleV2OutputMapEntry> frequencyToOutputAccelerationMap;
+    getPwleV2FrequencyToOutputAccelerationMap(&frequencyToOutputAccelerationMap);
+    float minFrequency = getPwleV2FrequencyMinHz(frequencyToOutputAccelerationMap);
+    float maxFrequency = getPwleV2FrequencyMaxHz(frequencyToOutputAccelerationMap);
+
+    for (auto& e : composite) {
+        if (e.timeMillis < 0.0f || e.timeMillis > COMPOSE_PWLE_V2_PRIMITIVE_DURATION_MAX_MS) {
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+        }
+        if (e.amplitude < 0.0f || e.amplitude > 1.0f) {
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+        }
+        if (e.frequencyHz < minFrequency || e.frequencyHz > maxFrequency) {
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+        }
+        totalEffectDuration += e.timeMillis;
+    }
+
+    std::thread([totalEffectDuration, callback] {
+        LOG(VERBOSE) << "Starting composePwleV2 on another thread";
+        usleep(totalEffectDuration * 1000);
+        if (callback != nullptr) {
+            LOG(VERBOSE) << "Notifying compose PWLE V2 complete";
+            callback->onComplete();
+        }
+    }).detach();
+
+    return ndk::ScopedAStatus::ok();
+}
+
 }  // namespace vibrator
 }  // namespace hardware
 }  // namespace android
diff --git a/vibrator/aidl/default/android.hardware.vibrator.xml b/vibrator/aidl/default/android.hardware.vibrator.xml
index b5bd3dd..b730046 100644
--- a/vibrator/aidl/default/android.hardware.vibrator.xml
+++ b/vibrator/aidl/default/android.hardware.vibrator.xml
@@ -1,12 +1,12 @@
 <manifest version="1.0" type="device">
     <hal format="aidl">
         <name>android.hardware.vibrator</name>
-        <version>2</version>
+        <version>3</version>
         <fqname>IVibrator/default</fqname>
     </hal>
     <hal format="aidl">
         <name>android.hardware.vibrator</name>
-        <version>2</version>
+        <version>3</version>
         <fqname>IVibratorManager/default</fqname>
     </hal>
 </manifest>
diff --git a/vibrator/aidl/default/include/vibrator-impl/Vibrator.h b/vibrator/aidl/default/include/vibrator-impl/Vibrator.h
index 4203bf2..28bc763 100644
--- a/vibrator/aidl/default/include/vibrator-impl/Vibrator.h
+++ b/vibrator/aidl/default/include/vibrator-impl/Vibrator.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <aidl/android/hardware/vibrator/BnVibrator.h>
+#include <android-base/thread_annotations.h>
 
 namespace aidl {
 namespace android {
@@ -31,6 +32,9 @@
     ndk::ScopedAStatus perform(Effect effect, EffectStrength strength,
                                const std::shared_ptr<IVibratorCallback>& callback,
                                int32_t* _aidl_return) override;
+    ndk::ScopedAStatus performVendorEffect(
+            const VendorEffect& effect,
+            const std::shared_ptr<IVibratorCallback>& callback) override;
     ndk::ScopedAStatus getSupportedEffects(std::vector<Effect>* _aidl_return) override;
     ndk::ScopedAStatus setAmplitude(float amplitude) override;
     ndk::ScopedAStatus setExternalControl(bool enabled) override;
@@ -54,7 +58,18 @@
     ndk::ScopedAStatus getSupportedBraking(std::vector<Braking>* supported) override;
     ndk::ScopedAStatus composePwle(const std::vector<PrimitivePwle> &composite,
                                    const std::shared_ptr<IVibratorCallback> &callback) override;
+    ndk::ScopedAStatus getPwleV2FrequencyToOutputAccelerationMap(
+            std::vector<PwleV2OutputMapEntry>* _aidl_return) override;
+    ndk::ScopedAStatus getPwleV2PrimitiveDurationMaxMillis(int32_t* maxDurationMs) override;
+    ndk::ScopedAStatus getPwleV2PrimitiveDurationMinMillis(int32_t* minDurationMs) override;
+    ndk::ScopedAStatus getPwleV2CompositionSizeMax(int32_t* maxSize) override;
+    ndk::ScopedAStatus composePwleV2(const std::vector<PwleV2Primitive>& composite,
+                                     const std::shared_ptr<IVibratorCallback>& callback) override;
 
+  private:
+    mutable std::mutex mMutex;
+    int32_t mVersion GUARDED_BY(mMutex) = 0;  // current Hal version
+    int32_t mCapabilities GUARDED_BY(mMutex) = 0;
 };
 
 }  // namespace vibrator
diff --git a/vibrator/aidl/vts/Android.bp b/vibrator/aidl/vts/Android.bp
index b6d2fb2..a48bb2e 100644
--- a/vibrator/aidl/vts/Android.bp
+++ b/vibrator/aidl/vts/Android.bp
@@ -17,10 +17,10 @@
     tidy_timeout_srcs: ["VtsHalVibratorTargetTest.cpp"],
     srcs: ["VtsHalVibratorTargetTest.cpp"],
     shared_libs: [
-        "libbinder",
+        "libbinder_ndk",
     ],
     static_libs: [
-        "android.hardware.vibrator-V2-cpp",
+        "android.hardware.vibrator-V3-ndk",
     ],
     test_suites: [
         "general-tests",
@@ -36,10 +36,10 @@
     ],
     srcs: ["VtsHalVibratorManagerTargetTest.cpp"],
     shared_libs: [
-        "libbinder",
+        "libbinder_ndk",
     ],
     static_libs: [
-        "android.hardware.vibrator-V2-cpp",
+        "android.hardware.vibrator-V3-ndk",
     ],
     test_suites: [
         "general-tests",
diff --git a/vibrator/aidl/vts/VtsHalVibratorManagerTargetTest.cpp b/vibrator/aidl/vts/VtsHalVibratorManagerTargetTest.cpp
index e8ed26a..3c2a360 100644
--- a/vibrator/aidl/vts/VtsHalVibratorManagerTargetTest.cpp
+++ b/vibrator/aidl/vts/VtsHalVibratorManagerTargetTest.cpp
@@ -15,42 +15,40 @@
  */
 #include <aidl/Gtest.h>
 #include <aidl/Vintf.h>
+#include <aidl/android/hardware/vibrator/BnVibratorCallback.h>
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+#include <aidl/android/hardware/vibrator/IVibratorManager.h>
 
-#include <android/hardware/vibrator/BnVibratorCallback.h>
-#include <android/hardware/vibrator/IVibrator.h>
-#include <android/hardware/vibrator/IVibratorManager.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
 
 #include <cmath>
 #include <future>
 
-using android::ProcessState;
-using android::sp;
-using android::String16;
-using android::binder::Status;
-using android::hardware::vibrator::BnVibratorCallback;
-using android::hardware::vibrator::CompositeEffect;
-using android::hardware::vibrator::CompositePrimitive;
-using android::hardware::vibrator::Effect;
-using android::hardware::vibrator::EffectStrength;
-using android::hardware::vibrator::IVibrator;
-using android::hardware::vibrator::IVibratorManager;
+#include "test_utils.h"
+
+using aidl::android::hardware::vibrator::BnVibratorCallback;
+using aidl::android::hardware::vibrator::CompositeEffect;
+using aidl::android::hardware::vibrator::CompositePrimitive;
+using aidl::android::hardware::vibrator::Effect;
+using aidl::android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::IVibrator;
+using aidl::android::hardware::vibrator::IVibratorManager;
 using std::chrono::high_resolution_clock;
 
-const std::vector<Effect> kEffects{android::enum_range<Effect>().begin(),
-                                   android::enum_range<Effect>().end()};
-const std::vector<EffectStrength> kEffectStrengths{android::enum_range<EffectStrength>().begin(),
-                                                   android::enum_range<EffectStrength>().end()};
-const std::vector<CompositePrimitive> kPrimitives{android::enum_range<CompositePrimitive>().begin(),
-                                                  android::enum_range<CompositePrimitive>().end()};
+const std::vector<Effect> kEffects{ndk::enum_range<Effect>().begin(),
+                                   ndk::enum_range<Effect>().end()};
+const std::vector<EffectStrength> kEffectStrengths{ndk::enum_range<EffectStrength>().begin(),
+                                                   ndk::enum_range<EffectStrength>().end()};
+const std::vector<CompositePrimitive> kPrimitives{ndk::enum_range<CompositePrimitive>().begin(),
+                                                  ndk::enum_range<CompositePrimitive>().end()};
 
 class CompletionCallback : public BnVibratorCallback {
   public:
     CompletionCallback(const std::function<void()>& callback) : mCallback(callback) {}
-    Status onComplete() override {
+    ndk::ScopedAStatus onComplete() override {
         mCallback();
-        return Status::ok();
+        return ndk::ScopedAStatus::ok();
     }
 
   private:
@@ -60,55 +58,50 @@
 class VibratorAidl : public testing::TestWithParam<std::string> {
   public:
     virtual void SetUp() override {
-        manager = android::waitForDeclaredService<IVibratorManager>(String16(GetParam().c_str()));
+        auto serviceName = GetParam().c_str();
+        manager = IVibratorManager::fromBinder(
+                ndk::SpAIBinder(AServiceManager_waitForService(serviceName)));
         ASSERT_NE(manager, nullptr);
-        ASSERT_TRUE(manager->getCapabilities(&capabilities).isOk());
-        EXPECT_TRUE(manager->getVibratorIds(&vibratorIds).isOk());
+        EXPECT_OK(manager->getCapabilities(&capabilities));
+        EXPECT_OK(manager->getVibratorIds(&vibratorIds));
     }
 
-    sp<IVibratorManager> manager;
+    std::shared_ptr<IVibratorManager> manager;
     int32_t capabilities;
     std::vector<int32_t> vibratorIds;
 };
 
-inline bool isUnknownOrUnsupported(Status status) {
-    return status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
-           status.transactionError() == android::UNKNOWN_TRANSACTION;
-}
-
 TEST_P(VibratorAidl, ValidateExistingVibrators) {
-    sp<IVibrator> vibrator;
-    for (auto& id : vibratorIds) {
-        EXPECT_TRUE(manager->getVibrator(id, &vibrator).isOk());
+    std::shared_ptr<IVibrator> vibrator;
+    for (int32_t id : vibratorIds) {
+        EXPECT_OK(manager->getVibrator(id, &vibrator));
         ASSERT_NE(vibrator, nullptr);
     }
 }
 
 TEST_P(VibratorAidl, GetVibratorWithInvalidId) {
     int32_t invalidId = *max_element(vibratorIds.begin(), vibratorIds.end()) + 1;
-    sp<IVibrator> vibrator;
-    EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
-              manager->getVibrator(invalidId, &vibrator).exceptionCode());
+    std::shared_ptr<IVibrator> vibrator;
+    EXPECT_ILLEGAL_ARGUMENT(manager->getVibrator(invalidId, &vibrator));
     ASSERT_EQ(vibrator, nullptr);
 }
 
 TEST_P(VibratorAidl, ValidatePrepareSyncedExistingVibrators) {
     if (!(capabilities & IVibratorManager::CAP_SYNC)) return;
     if (vibratorIds.empty()) return;
-    EXPECT_TRUE(manager->prepareSynced(vibratorIds).isOk());
-    EXPECT_TRUE(manager->cancelSynced().isOk());
+    EXPECT_OK(manager->prepareSynced(vibratorIds));
+    EXPECT_OK(manager->cancelSynced());
 }
 
 TEST_P(VibratorAidl, PrepareSyncedEmptySetIsInvalid) {
     if (!(capabilities & IVibratorManager::CAP_SYNC)) return;
     std::vector<int32_t> emptyIds;
-    EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, manager->prepareSynced(emptyIds).exceptionCode());
+    EXPECT_ILLEGAL_ARGUMENT(manager->prepareSynced(emptyIds));
 }
 
 TEST_P(VibratorAidl, PrepareSyncedNotSupported) {
     if (!(capabilities & IVibratorManager::CAP_SYNC)) {
-        Status status = manager->prepareSynced(vibratorIds);
-        EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+        EXPECT_UNKNOWN_OR_UNSUPPORTED(manager->prepareSynced(vibratorIds));
     }
 }
 
@@ -117,15 +110,14 @@
     if (!(capabilities & IVibratorManager::CAP_SYNC)) return;
     if (!(capabilities & IVibratorManager::CAP_PREPARE_ON)) {
         uint32_t durationMs = 250;
-        EXPECT_TRUE(manager->prepareSynced(vibratorIds).isOk());
-        sp<IVibrator> vibrator;
-        for (auto& id : vibratorIds) {
-            EXPECT_TRUE(manager->getVibrator(id, &vibrator).isOk());
+        EXPECT_OK(manager->prepareSynced(vibratorIds));
+        std::shared_ptr<IVibrator> vibrator;
+        for (int32_t id : vibratorIds) {
+            EXPECT_OK(manager->getVibrator(id, &vibrator));
             ASSERT_NE(vibrator, nullptr);
-            Status status = vibrator->on(durationMs, nullptr);
-            EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+            EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->on(durationMs, nullptr));
         }
-        EXPECT_TRUE(manager->cancelSynced().isOk());
+        EXPECT_OK(manager->cancelSynced());
     }
 }
 
@@ -133,16 +125,16 @@
     if (vibratorIds.empty()) return;
     if (!(capabilities & IVibratorManager::CAP_SYNC)) return;
     if (!(capabilities & IVibratorManager::CAP_PREPARE_ON)) {
-        EXPECT_TRUE(manager->prepareSynced(vibratorIds).isOk());
-        sp<IVibrator> vibrator;
-        for (auto& id : vibratorIds) {
-            EXPECT_TRUE(manager->getVibrator(id, &vibrator).isOk());
+        EXPECT_OK(manager->prepareSynced(vibratorIds));
+        std::shared_ptr<IVibrator> vibrator;
+        for (int32_t id : vibratorIds) {
+            EXPECT_OK(manager->getVibrator(id, &vibrator));
             ASSERT_NE(vibrator, nullptr);
             int32_t lengthMs = 0;
-            Status status = vibrator->perform(kEffects[0], kEffectStrengths[0], nullptr, &lengthMs);
-            EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+            EXPECT_UNKNOWN_OR_UNSUPPORTED(
+                    vibrator->perform(kEffects[0], kEffectStrengths[0], nullptr, &lengthMs));
         }
-        EXPECT_TRUE(manager->cancelSynced().isOk());
+        EXPECT_OK(manager->cancelSynced());
     }
 }
 
@@ -157,15 +149,14 @@
         effect.scale = 1.0f;
         composite.emplace_back(effect);
 
-        EXPECT_TRUE(manager->prepareSynced(vibratorIds).isOk());
-        sp<IVibrator> vibrator;
-        for (auto& id : vibratorIds) {
-            EXPECT_TRUE(manager->getVibrator(id, &vibrator).isOk());
+        EXPECT_OK(manager->prepareSynced(vibratorIds));
+        std::shared_ptr<IVibrator> vibrator;
+        for (int32_t id : vibratorIds) {
+            EXPECT_OK(manager->getVibrator(id, &vibrator));
             ASSERT_NE(vibrator, nullptr);
-            Status status = vibrator->compose(composite, nullptr);
-            EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+            EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->compose(composite, nullptr));
         }
-        EXPECT_TRUE(manager->cancelSynced().isOk());
+        EXPECT_OK(manager->cancelSynced());
     }
 }
 
@@ -177,51 +168,58 @@
 
     std::promise<void> completionPromise;
     std::future<void> completionFuture{completionPromise.get_future()};
-    sp<CompletionCallback> callback =
-            new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
+    auto callback = ndk::SharedRefBase::make<CompletionCallback>(
+            [&completionPromise] { completionPromise.set_value(); });
     uint32_t durationMs = 250;
     std::chrono::milliseconds timeout{durationMs * 2};
 
-    EXPECT_TRUE(manager->prepareSynced(vibratorIds).isOk());
-    sp<IVibrator> vibrator;
-    for (auto& id : vibratorIds) {
-        EXPECT_TRUE(manager->getVibrator(id, &vibrator).isOk());
+    EXPECT_OK(manager->prepareSynced(vibratorIds));
+    std::shared_ptr<IVibrator> vibrator;
+    for (int32_t id : vibratorIds) {
+        EXPECT_OK(manager->getVibrator(id, &vibrator));
         ASSERT_NE(vibrator, nullptr);
-        EXPECT_TRUE(vibrator->on(durationMs, nullptr).isOk());
+        EXPECT_OK(vibrator->on(durationMs, nullptr));
     }
 
-    EXPECT_TRUE(manager->triggerSynced(callback).isOk());
+    EXPECT_OK(manager->triggerSynced(callback));
     EXPECT_EQ(completionFuture.wait_for(timeout), std::future_status::ready);
-    EXPECT_TRUE(manager->cancelSynced().isOk());
+    EXPECT_OK(manager->cancelSynced());
 }
 
 TEST_P(VibratorAidl, TriggerSyncNotSupported) {
     if (!(capabilities & IVibratorManager::CAP_SYNC)) {
-        Status status = manager->triggerSynced(nullptr);
-        EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+        EXPECT_UNKNOWN_OR_UNSUPPORTED(manager->triggerSynced(nullptr));
     }
 }
 
 TEST_P(VibratorAidl, TriggerCallbackNotSupported) {
     if (!(capabilities & IVibratorManager::CAP_SYNC)) return;
     if (!(capabilities & IVibratorManager::CAP_TRIGGER_CALLBACK)) {
-        sp<CompletionCallback> callback = new CompletionCallback([] {});
-        EXPECT_TRUE(manager->prepareSynced(vibratorIds).isOk());
-        Status status = manager->triggerSynced(callback);
-        EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
-        EXPECT_TRUE(manager->cancelSynced().isOk());
+        auto callback = ndk::SharedRefBase::make<CompletionCallback>([] {});
+        EXPECT_OK(manager->prepareSynced(vibratorIds));
+        EXPECT_UNKNOWN_OR_UNSUPPORTED(manager->triggerSynced(callback));
+        EXPECT_OK(manager->cancelSynced());
     }
 }
 
+std::vector<std::string> FindVibratorManagerNames() {
+    std::vector<std::string> names;
+    constexpr auto callback = [](const char* instance, void* context) {
+        std::string fullName = std::string(IVibratorManager::descriptor) + "/" + instance;
+        static_cast<std::vector<std::string>*>(context)->emplace_back(fullName);
+    };
+    AServiceManager_forEachDeclaredInstance(IVibratorManager::descriptor,
+                                            static_cast<void*>(&names), callback);
+    return names;
+}
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VibratorAidl);
-INSTANTIATE_TEST_SUITE_P(
-        Vibrator, VibratorAidl,
-        testing::ValuesIn(android::getAidlHalInstanceNames(IVibratorManager::descriptor)),
-        android::PrintInstanceNameToString);
+INSTANTIATE_TEST_SUITE_P(Vibrator, VibratorAidl, testing::ValuesIn(FindVibratorManagerNames()),
+                         android::PrintInstanceNameToString);
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
-    ProcessState::self()->setThreadPoolMaxThreadCount(1);
-    ProcessState::self()->startThreadPool();
+    ABinderProcess_setThreadPoolMaxThreadCount(1);
+    ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
 }
diff --git a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
index db474d6..bc017ae 100644
--- a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
+++ b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
@@ -15,38 +15,51 @@
  */
 #include <aidl/Gtest.h>
 #include <aidl/Vintf.h>
-#include <android/hardware/vibrator/BnVibratorCallback.h>
-#include <android/hardware/vibrator/IVibrator.h>
-#include <android/hardware/vibrator/IVibratorManager.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
+#include <aidl/android/hardware/vibrator/BnVibratorCallback.h>
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+#include <aidl/android/hardware/vibrator/IVibratorManager.h>
+
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <android/persistable_bundle_aidl.h>
 
 #include <cmath>
+#include <cstdlib>
+#include <ctime>
 #include <future>
+#include <iomanip>
+#include <iostream>
+#include <random>
 
-using android::ProcessState;
-using android::sp;
-using android::String16;
-using android::binder::Status;
-using android::hardware::vibrator::ActivePwle;
-using android::hardware::vibrator::BnVibratorCallback;
-using android::hardware::vibrator::Braking;
-using android::hardware::vibrator::BrakingPwle;
-using android::hardware::vibrator::CompositeEffect;
-using android::hardware::vibrator::CompositePrimitive;
-using android::hardware::vibrator::Effect;
-using android::hardware::vibrator::EffectStrength;
-using android::hardware::vibrator::IVibrator;
-using android::hardware::vibrator::IVibratorManager;
-using android::hardware::vibrator::PrimitivePwle;
+#include "persistable_bundle_utils.h"
+#include "pwle_v2_utils.h"
+#include "test_utils.h"
+
+using aidl::android::hardware::vibrator::ActivePwle;
+using aidl::android::hardware::vibrator::BnVibratorCallback;
+using aidl::android::hardware::vibrator::Braking;
+using aidl::android::hardware::vibrator::BrakingPwle;
+using aidl::android::hardware::vibrator::CompositeEffect;
+using aidl::android::hardware::vibrator::CompositePrimitive;
+using aidl::android::hardware::vibrator::Effect;
+using aidl::android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::IVibrator;
+using aidl::android::hardware::vibrator::IVibratorManager;
+using aidl::android::hardware::vibrator::PrimitivePwle;
+using aidl::android::hardware::vibrator::PwleV2OutputMapEntry;
+using aidl::android::hardware::vibrator::PwleV2Primitive;
+using aidl::android::hardware::vibrator::VendorEffect;
+using aidl::android::os::PersistableBundle;
 using std::chrono::high_resolution_clock;
 
 using namespace ::std::chrono_literals;
 
-const std::vector<Effect> kEffects{android::enum_range<Effect>().begin(),
-                                   android::enum_range<Effect>().end()};
-const std::vector<EffectStrength> kEffectStrengths{android::enum_range<EffectStrength>().begin(),
-                                                   android::enum_range<EffectStrength>().end()};
+namespace pwle_v2_utils = aidl::android::hardware::vibrator::testing::pwlev2;
+
+const std::vector<Effect> kEffects{ndk::enum_range<Effect>().begin(),
+                                   ndk::enum_range<Effect>().end()};
+const std::vector<EffectStrength> kEffectStrengths{ndk::enum_range<EffectStrength>().begin(),
+                                                   ndk::enum_range<EffectStrength>().end()};
 
 const std::vector<Effect> kInvalidEffects = {
     static_cast<Effect>(static_cast<int32_t>(kEffects.front()) - 1),
@@ -59,8 +72,7 @@
 };
 
 const std::vector<CompositePrimitive> kCompositePrimitives{
-    android::enum_range<CompositePrimitive>().begin(),
-    android::enum_range<CompositePrimitive>().end()};
+        ndk::enum_range<CompositePrimitive>().begin(), ndk::enum_range<CompositePrimitive>().end()};
 
 const std::vector<CompositePrimitive> kRequiredPrimitives = {
         CompositePrimitive::CLICK,      CompositePrimitive::LIGHT_TICK,
@@ -74,14 +86,39 @@
 };
 
 // Timeout to wait for vibration callback completion.
-static constexpr auto VIBRATION_CALLBACK_TIMEOUT = 100ms;
+static constexpr std::chrono::milliseconds VIBRATION_CALLBACK_TIMEOUT = 100ms;
+
+static constexpr int32_t VENDOR_EFFECTS_MIN_VERSION = 3;
+static constexpr int32_t PWLE_V2_MIN_VERSION = 3;
+
+static std::vector<std::string> findVibratorManagerNames() {
+    std::vector<std::string> names;
+    constexpr auto callback = [](const char* instance, void* context) {
+        auto fullName = std::string(IVibratorManager::descriptor) + "/" + instance;
+        static_cast<std::vector<std::string>*>(context)->emplace_back(fullName);
+    };
+    AServiceManager_forEachDeclaredInstance(IVibratorManager::descriptor,
+                                            static_cast<void*>(&names), callback);
+    return names;
+}
+
+static std::vector<std::string> findUnmanagedVibratorNames() {
+    std::vector<std::string> names;
+    constexpr auto callback = [](const char* instance, void* context) {
+        auto fullName = std::string(IVibrator::descriptor) + "/" + instance;
+        static_cast<std::vector<std::string>*>(context)->emplace_back(fullName);
+    };
+    AServiceManager_forEachDeclaredInstance(IVibrator::descriptor, static_cast<void*>(&names),
+                                            callback);
+    return names;
+}
 
 class CompletionCallback : public BnVibratorCallback {
   public:
     CompletionCallback(const std::function<void()> &callback) : mCallback(callback) {}
-    Status onComplete() override {
+    ndk::ScopedAStatus onComplete() override {
         mCallback();
-        return Status::ok();
+        return ndk::ScopedAStatus::ok();
     }
 
   private:
@@ -93,88 +130,89 @@
     virtual void SetUp() override {
         int32_t managerIdx = std::get<0>(GetParam());
         int32_t vibratorId = std::get<1>(GetParam());
-        auto managerAidlNames = android::getAidlHalInstanceNames(IVibratorManager::descriptor);
 
         if (managerIdx < 0) {
             // Testing a unmanaged vibrator, using vibratorId as index from registered HALs
-            auto vibratorAidlNames = android::getAidlHalInstanceNames(IVibrator::descriptor);
-            ASSERT_LT(vibratorId, vibratorAidlNames.size());
-            auto vibratorName = String16(vibratorAidlNames[vibratorId].c_str());
-            vibrator = android::waitForDeclaredService<IVibrator>(vibratorName);
+            std::vector<std::string> vibratorNames = findUnmanagedVibratorNames();
+            ASSERT_LT(vibratorId, vibratorNames.size());
+            vibrator = IVibrator::fromBinder(ndk::SpAIBinder(
+                    AServiceManager_waitForService(vibratorNames[vibratorId].c_str())));
         } else {
             // Testing a managed vibrator, using vibratorId to retrieve it from the manager
-            ASSERT_LT(managerIdx, managerAidlNames.size());
-            auto managerName = String16(managerAidlNames[managerIdx].c_str());
-            auto vibratorManager = android::waitForDeclaredService<IVibratorManager>(managerName);
-            auto vibratorResult = vibratorManager->getVibrator(vibratorId, &vibrator);
-            ASSERT_TRUE(vibratorResult.isOk());
+            std::vector<std::string> managerNames = findVibratorManagerNames();
+            ASSERT_LT(managerIdx, managerNames.size());
+            auto vibratorManager = IVibratorManager::fromBinder(ndk::SpAIBinder(
+                    AServiceManager_waitForService(managerNames[managerIdx].c_str())));
+            EXPECT_OK(vibratorManager->getVibrator(vibratorId, &vibrator))
+                    << "\n  For vibrator id: " << vibratorId;
         }
 
         ASSERT_NE(vibrator, nullptr);
-        ASSERT_TRUE(vibrator->getCapabilities(&capabilities).isOk());
+        EXPECT_OK(vibrator->getInterfaceVersion(&version));
+        EXPECT_OK(vibrator->getCapabilities(&capabilities));
     }
 
     virtual void TearDown() override {
         // Reset vibrator state between tests.
-        EXPECT_TRUE(vibrator->off().isOk());
+        EXPECT_OK(vibrator->off());
     }
 
-    sp<IVibrator> vibrator;
+    std::shared_ptr<IVibrator> vibrator;
+    int32_t version;
     int32_t capabilities;
 };
 
-inline bool isUnknownOrUnsupported(Status status) {
-    return status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
-           status.transactionError() == android::UNKNOWN_TRANSACTION;
-}
-
-static float getResonantFrequencyHz(sp<IVibrator> vibrator, int32_t capabilities) {
+static float getResonantFrequencyHz(const std::shared_ptr<IVibrator>& vibrator,
+                                    int32_t capabilities) {
     float resonantFrequencyHz;
-    Status status = vibrator->getResonantFrequency(&resonantFrequencyHz);
+    ndk::ScopedAStatus status = vibrator->getResonantFrequency(&resonantFrequencyHz);
     if (capabilities & IVibrator::CAP_GET_RESONANT_FREQUENCY) {
+        EXPECT_OK(std::move(status));
         EXPECT_GT(resonantFrequencyHz, 0);
-        EXPECT_EQ(status.exceptionCode(), Status::EX_NONE);
     } else {
-        EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+        EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status));
     }
     return resonantFrequencyHz;
 }
 
-static float getFrequencyResolutionHz(sp<IVibrator> vibrator, int32_t capabilities) {
+static float getFrequencyResolutionHz(const std::shared_ptr<IVibrator>& vibrator,
+                                      int32_t capabilities) {
     float freqResolutionHz;
-    Status status = vibrator->getFrequencyResolution(&freqResolutionHz);
+    ndk::ScopedAStatus status = vibrator->getFrequencyResolution(&freqResolutionHz);
     if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
+        EXPECT_OK(std::move(status));
         EXPECT_GT(freqResolutionHz, 0);
-        EXPECT_EQ(status.exceptionCode(), Status::EX_NONE);
     } else {
-        EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+        EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status));
     }
     return freqResolutionHz;
 }
 
-static float getFrequencyMinimumHz(sp<IVibrator> vibrator, int32_t capabilities) {
+static float getFrequencyMinimumHz(const std::shared_ptr<IVibrator>& vibrator,
+                                   int32_t capabilities) {
     float freqMinimumHz;
-    Status status = vibrator->getFrequencyMinimum(&freqMinimumHz);
+    ndk::ScopedAStatus status = vibrator->getFrequencyMinimum(&freqMinimumHz);
     if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
-        EXPECT_EQ(status.exceptionCode(), Status::EX_NONE);
+        EXPECT_OK(std::move(status));
 
         float resonantFrequencyHz = getResonantFrequencyHz(vibrator, capabilities);
 
         EXPECT_GT(freqMinimumHz, 0);
         EXPECT_LE(freqMinimumHz, resonantFrequencyHz);
     } else {
-        EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+        EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status));
     }
     return freqMinimumHz;
 }
 
-static float getFrequencyMaximumHz(sp<IVibrator> vibrator, int32_t capabilities) {
+static float getFrequencyMaximumHz(const std::shared_ptr<IVibrator>& vibrator,
+                                   int32_t capabilities) {
     std::vector<float> bandwidthAmplitudeMap;
-    Status status = vibrator->getBandwidthAmplitudeMap(&bandwidthAmplitudeMap);
+    ndk::ScopedAStatus status = vibrator->getBandwidthAmplitudeMap(&bandwidthAmplitudeMap);
     if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
-        EXPECT_EQ(status.exceptionCode(), Status::EX_NONE);
+        EXPECT_OK(std::move(status));
     } else {
-        EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+        EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status));
     }
 
     float freqMaximumHz = ((bandwidthAmplitudeMap.size() - 1) *
@@ -191,7 +229,8 @@
     return 1.0;
 }
 
-static ActivePwle composeValidActivePwle(sp<IVibrator> vibrator, int32_t capabilities) {
+static ActivePwle composeValidActivePwle(const std::shared_ptr<IVibrator>& vibrator,
+                                         int32_t capabilities) {
     float frequencyHz;
     if (capabilities & IVibrator::CAP_GET_RESONANT_FREQUENCY) {
         frequencyHz = getResonantFrequencyHz(vibrator, capabilities);
@@ -212,9 +251,9 @@
 }
 
 TEST_P(VibratorAidl, OnThenOffBeforeTimeout) {
-    EXPECT_TRUE(vibrator->on(2000, nullptr /*callback*/).isOk());
+    EXPECT_OK(vibrator->on(2000, nullptr /*callback*/));
     sleep(1);
-    EXPECT_TRUE(vibrator->off().isOk());
+    EXPECT_OK(vibrator->off());
 }
 
 TEST_P(VibratorAidl, OnWithCallback) {
@@ -223,26 +262,25 @@
 
     std::promise<void> completionPromise;
     std::future<void> completionFuture{completionPromise.get_future()};
-    sp<CompletionCallback> callback =
-        new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
+    auto callback = ndk::SharedRefBase::make<CompletionCallback>(
+            [&completionPromise] { completionPromise.set_value(); });
     uint32_t durationMs = 250;
     auto timeout = std::chrono::milliseconds(durationMs) + VIBRATION_CALLBACK_TIMEOUT;
-    EXPECT_TRUE(vibrator->on(durationMs, callback).isOk());
+    EXPECT_OK(vibrator->on(durationMs, callback));
     EXPECT_EQ(completionFuture.wait_for(timeout), std::future_status::ready);
-    EXPECT_TRUE(vibrator->off().isOk());
+    EXPECT_OK(vibrator->off());
 }
 
 TEST_P(VibratorAidl, OnCallbackNotSupported) {
     if (!(capabilities & IVibrator::CAP_ON_CALLBACK)) {
-        sp<CompletionCallback> callback = new CompletionCallback([] {});
-        Status status = vibrator->on(250, callback);
-        EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+        auto callback = ndk::SharedRefBase::make<CompletionCallback>([] {});
+        EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->on(250, callback));
     }
 }
 
 TEST_P(VibratorAidl, ValidateEffect) {
     std::vector<Effect> supported;
-    ASSERT_TRUE(vibrator->getSupportedEffects(&supported).isOk());
+    EXPECT_OK(vibrator->getSupportedEffects(&supported));
 
     for (Effect effect : kEffects) {
         bool isEffectSupported =
@@ -250,15 +288,18 @@
 
         for (EffectStrength strength : kEffectStrengths) {
             int32_t lengthMs = 0;
-            Status status = vibrator->perform(effect, strength, nullptr /*callback*/, &lengthMs);
+            ndk::ScopedAStatus status =
+                    vibrator->perform(effect, strength, nullptr /*callback*/, &lengthMs);
 
             if (isEffectSupported) {
-                EXPECT_TRUE(status.isOk()) << toString(effect) << " " << toString(strength);
+                EXPECT_OK(std::move(status))
+                        << "\n  For effect: " << toString(effect) << " " << toString(strength);
                 EXPECT_GT(lengthMs, 0);
                 usleep(lengthMs * 1000);
+                EXPECT_OK(vibrator->off());
             } else {
-                EXPECT_TRUE(isUnknownOrUnsupported(status))
-                        << status << " " << toString(effect) << " " << toString(strength);
+                EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status))
+                        << "\n  For effect: " << toString(effect) << " " << toString(strength);
             }
         }
     }
@@ -269,7 +310,7 @@
         return;
 
     std::vector<Effect> supported;
-    ASSERT_TRUE(vibrator->getSupportedEffects(&supported).isOk());
+    EXPECT_OK(vibrator->getSupportedEffects(&supported));
 
     for (Effect effect : kEffects) {
         bool isEffectSupported =
@@ -278,25 +319,26 @@
         for (EffectStrength strength : kEffectStrengths) {
             std::promise<void> completionPromise;
             std::future<void> completionFuture{completionPromise.get_future()};
-            sp<CompletionCallback> callback =
-                new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
+            auto callback = ndk::SharedRefBase::make<CompletionCallback>(
+                    [&completionPromise] { completionPromise.set_value(); });
             int lengthMs = 0;
-            Status status = vibrator->perform(effect, strength, callback, &lengthMs);
+            ndk::ScopedAStatus status = vibrator->perform(effect, strength, callback, &lengthMs);
 
             if (isEffectSupported) {
-                EXPECT_TRUE(status.isOk());
+                EXPECT_OK(std::move(status))
+                        << "\n  For effect: " << toString(effect) << " " << toString(strength);
                 EXPECT_GT(lengthMs, 0);
             } else {
-                EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+                EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status))
+                        << "\n  For effect: " << toString(effect) << " " << toString(strength);
             }
 
-            if (!status.isOk())
-                continue;
+            if (lengthMs <= 0) continue;
 
             auto timeout = std::chrono::milliseconds(lengthMs) + VIBRATION_CALLBACK_TIMEOUT;
             EXPECT_EQ(completionFuture.wait_for(timeout), std::future_status::ready);
 
-            EXPECT_TRUE(vibrator->off().isOk());
+            EXPECT_OK(vibrator->off());
         }
     }
 }
@@ -307,10 +349,10 @@
 
     for (Effect effect : kEffects) {
         for (EffectStrength strength : kEffectStrengths) {
-            sp<CompletionCallback> callback = new CompletionCallback([] {});
+            auto callback = ndk::SharedRefBase::make<CompletionCallback>([] {});
             int lengthMs;
-            Status status = vibrator->perform(effect, strength, callback, &lengthMs);
-            EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+            EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->perform(effect, strength, callback, &lengthMs))
+                    << "\n  For effect: " << toString(effect) << " " << toString(strength);
         }
     }
 }
@@ -319,53 +361,182 @@
     for (Effect effect : kInvalidEffects) {
         for (EffectStrength strength : kEffectStrengths) {
             int32_t lengthMs;
-            Status status = vibrator->perform(effect, strength, nullptr /*callback*/, &lengthMs);
-            EXPECT_TRUE(isUnknownOrUnsupported(status))
-                    << status << toString(effect) << " " << toString(strength);
+            EXPECT_UNKNOWN_OR_UNSUPPORTED(
+                    vibrator->perform(effect, strength, nullptr /*callback*/, &lengthMs))
+                    << "\n  For effect: " << toString(effect) << " " << toString(strength);
         }
     }
     for (Effect effect : kEffects) {
         for (EffectStrength strength : kInvalidEffectStrengths) {
             int32_t lengthMs;
-            Status status = vibrator->perform(effect, strength, nullptr /*callback*/, &lengthMs);
-            EXPECT_TRUE(isUnknownOrUnsupported(status))
-                    << status << " " << toString(effect) << " " << toString(strength);
+            EXPECT_UNKNOWN_OR_UNSUPPORTED(
+                    vibrator->perform(effect, strength, nullptr /*callback*/, &lengthMs))
+                    << "\n  For effect: " << toString(effect) << " " << toString(strength);
         }
     }
 }
 
+TEST_P(VibratorAidl, PerformVendorEffectSupported) {
+    if ((capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS) == 0) return;
+
+    float scale = 0.0f;
+    float vendorScale = 0.0f;
+    for (EffectStrength strength : kEffectStrengths) {
+        PersistableBundle vendorData;
+        ::aidl::android::hardware::vibrator::testing::fillBasicData(&vendorData);
+
+        PersistableBundle nestedData;
+        ::aidl::android::hardware::vibrator::testing::fillBasicData(&nestedData);
+        vendorData.putPersistableBundle("test_nested_bundle", nestedData);
+
+        VendorEffect effect;
+        effect.vendorData = vendorData;
+        effect.strength = strength;
+        effect.scale = scale;
+        effect.vendorScale = vendorScale;
+        scale += 0.5f;
+        vendorScale += 0.2f;
+
+        auto callback = ndk::SharedRefBase::make<CompletionCallback>([] {});
+        ndk::ScopedAStatus status = vibrator->performVendorEffect(effect, callback);
+
+        // No expectations on the actual status, the effect might be refused with illegal argument
+        // or the vendor might return a service-specific error code.
+        EXPECT_TRUE(status.getExceptionCode() != EX_UNSUPPORTED_OPERATION &&
+                    status.getStatus() != STATUS_UNKNOWN_TRANSACTION)
+                << status << "\n For vendor effect with strength" << toString(strength)
+                << " and scale " << effect.scale;
+
+        if (status.isOk()) {
+            // Generic vendor data should not trigger vibrations, but if it does trigger one
+            // then we make sure the vibrator is reset by triggering off().
+            EXPECT_OK(vibrator->off());
+        }
+    }
+}
+
+TEST_P(VibratorAidl, PerformVendorEffectStability) {
+    if ((capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS) == 0) return;
+
+    // Run some iterations of performVendorEffect with randomized vendor data to check basic
+    // stability of the implementation.
+    uint8_t iterations = 200;
+
+    for (EffectStrength strength : kEffectStrengths) {
+        float scale = 0.5f;
+        float vendorScale = 0.2f;
+        for (uint8_t i = 0; i < iterations; i++) {
+            PersistableBundle vendorData;
+            ::aidl::android::hardware::vibrator::testing::fillRandomData(&vendorData);
+
+            VendorEffect effect;
+            effect.vendorData = vendorData;
+            effect.strength = strength;
+            effect.scale = scale;
+            effect.vendorScale = vendorScale;
+            scale *= 2;
+            vendorScale *= 1.5f;
+
+            auto callback = ndk::SharedRefBase::make<CompletionCallback>([] {});
+            ndk::ScopedAStatus status = vibrator->performVendorEffect(effect, callback);
+
+            // No expectations on the actual status, the effect might be refused with illegal
+            // argument or the vendor might return a service-specific error code.
+            EXPECT_TRUE(status.getExceptionCode() != EX_UNSUPPORTED_OPERATION &&
+                        status.getStatus() != STATUS_UNKNOWN_TRANSACTION)
+                    << status << "\n For random vendor effect with strength " << toString(strength)
+                    << " and scale " << effect.scale;
+
+            if (status.isOk()) {
+                // Random vendor data should not trigger vibrations, but if it does trigger one
+                // then we make sure the vibrator is reset by triggering off().
+                EXPECT_OK(vibrator->off());
+            }
+        }
+    }
+}
+
+TEST_P(VibratorAidl, PerformVendorEffectEmptyVendorData) {
+    if ((capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS) == 0) return;
+
+    for (EffectStrength strength : kEffectStrengths) {
+        VendorEffect effect;
+        effect.strength = strength;
+        effect.scale = 1.0f;
+        effect.vendorScale = 1.0f;
+
+        ndk::ScopedAStatus status = vibrator->performVendorEffect(effect, nullptr /*callback*/);
+
+        EXPECT_TRUE(status.getExceptionCode() == EX_SERVICE_SPECIFIC)
+                << status << "\n For vendor effect with strength " << toString(strength)
+                << " and scale " << effect.scale;
+    }
+}
+
+TEST_P(VibratorAidl, PerformVendorEffectInvalidScale) {
+    if ((capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS) == 0) return;
+
+    VendorEffect effect;
+    effect.strength = EffectStrength::MEDIUM;
+
+    effect.scale = -1.0f;
+    effect.vendorScale = 1.0f;
+    EXPECT_ILLEGAL_ARGUMENT(vibrator->performVendorEffect(effect, nullptr /*callback*/));
+
+    effect.scale = 1.0f;
+    effect.vendorScale = -1.0f;
+    EXPECT_ILLEGAL_ARGUMENT(vibrator->performVendorEffect(effect, nullptr /*callback*/));
+}
+
+TEST_P(VibratorAidl, PerformVendorEffectUnsupported) {
+    if (version < VENDOR_EFFECTS_MIN_VERSION) {
+        EXPECT_EQ(capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS, 0)
+                << "Vibrator version " << version << " should not report vendor effects capability";
+    }
+    if (capabilities & IVibrator::CAP_PERFORM_VENDOR_EFFECTS) return;
+
+    for (EffectStrength strength : kEffectStrengths) {
+        VendorEffect effect;
+        effect.strength = strength;
+        effect.scale = 1.0f;
+        effect.vendorScale = 1.0f;
+
+        EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->performVendorEffect(effect, nullptr /*callback*/))
+                << "\n  For vendor effect with strength " << toString(strength);
+    }
+}
+
 TEST_P(VibratorAidl, ChangeVibrationAmplitude) {
     if (capabilities & IVibrator::CAP_AMPLITUDE_CONTROL) {
-        EXPECT_EQ(Status::EX_NONE, vibrator->setAmplitude(0.1f).exceptionCode());
-        EXPECT_TRUE(vibrator->on(2000, nullptr /*callback*/).isOk());
-        EXPECT_EQ(Status::EX_NONE, vibrator->setAmplitude(0.5f).exceptionCode());
+        EXPECT_OK(vibrator->setAmplitude(0.1f));
+        EXPECT_OK(vibrator->on(2000, nullptr /*callback*/));
+        EXPECT_OK(vibrator->setAmplitude(0.5f));
         sleep(1);
-        EXPECT_EQ(Status::EX_NONE, vibrator->setAmplitude(1.0f).exceptionCode());
+        EXPECT_OK(vibrator->setAmplitude(1.0f));
         sleep(1);
-        EXPECT_TRUE(vibrator->off().isOk());
+        EXPECT_OK(vibrator->off());
     }
 }
 
 TEST_P(VibratorAidl, AmplitudeOutsideRangeFails) {
     if (capabilities & IVibrator::CAP_AMPLITUDE_CONTROL) {
-        EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, vibrator->setAmplitude(-1).exceptionCode());
-        EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, vibrator->setAmplitude(0).exceptionCode());
-        EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, vibrator->setAmplitude(1.1).exceptionCode());
+        EXPECT_ILLEGAL_ARGUMENT(vibrator->setAmplitude(-1));
+        EXPECT_ILLEGAL_ARGUMENT(vibrator->setAmplitude(0));
+        EXPECT_ILLEGAL_ARGUMENT(vibrator->setAmplitude(1.1));
     }
 }
 
 TEST_P(VibratorAidl, AmplitudeReturnsUnsupportedMatchingCapabilities) {
     if ((capabilities & IVibrator::CAP_AMPLITUDE_CONTROL) == 0) {
-        Status status = vibrator->setAmplitude(1);
-        EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+        EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->setAmplitude(1));
     }
 }
 
 TEST_P(VibratorAidl, ChangeVibrationExternalControl) {
     if (capabilities & IVibrator::CAP_EXTERNAL_CONTROL) {
-        EXPECT_TRUE(vibrator->setExternalControl(true).isOk());
+        EXPECT_OK(vibrator->setExternalControl(true));
         sleep(1);
-        EXPECT_TRUE(vibrator->setExternalControl(false).isOk());
+        EXPECT_OK(vibrator->setExternalControl(false));
         sleep(1);
     }
 }
@@ -375,15 +546,15 @@
         (capabilities & IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL) > 0;
 
     if (capabilities & IVibrator::CAP_EXTERNAL_CONTROL) {
-        EXPECT_TRUE(vibrator->setExternalControl(true).isOk());
+        EXPECT_OK(vibrator->setExternalControl(true));
 
-        Status amplitudeStatus = vibrator->setAmplitude(0.5);
         if (supportsExternalAmplitudeControl) {
-            EXPECT_TRUE(amplitudeStatus.isOk());
+            EXPECT_OK(vibrator->setAmplitude(0.5));
         } else {
-            EXPECT_TRUE(isUnknownOrUnsupported(amplitudeStatus)) << amplitudeStatus;
+            EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->setAmplitude(0.5));
         }
-        EXPECT_TRUE(vibrator->setExternalControl(false).isOk());
+
+        EXPECT_OK(vibrator->setExternalControl(false));
     } else {
         EXPECT_FALSE(supportsExternalAmplitudeControl);
     }
@@ -391,18 +562,16 @@
 
 TEST_P(VibratorAidl, ExternalControlUnsupportedMatchingCapabilities) {
     if ((capabilities & IVibrator::CAP_EXTERNAL_CONTROL) == 0) {
-        Status status = vibrator->setExternalControl(true);
-        EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+        EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->setExternalControl(true));
     }
 }
 
 TEST_P(VibratorAidl, GetSupportedPrimitives) {
     if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) {
         std::vector<CompositePrimitive> supported;
+        EXPECT_OK(vibrator->getSupportedPrimitives(&supported));
 
-        EXPECT_EQ(Status::EX_NONE, vibrator->getSupportedPrimitives(&supported).exceptionCode());
-
-        for (auto primitive : kCompositePrimitives) {
+        for (CompositePrimitive primitive : kCompositePrimitives) {
             bool isPrimitiveSupported =
                 std::find(supported.begin(), supported.end(), primitive) != supported.end();
             bool isPrimitiveRequired =
@@ -417,22 +586,23 @@
 TEST_P(VibratorAidl, GetPrimitiveDuration) {
     if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) {
         std::vector<CompositePrimitive> supported;
-        ASSERT_TRUE(vibrator->getSupportedPrimitives(&supported).isOk());
+        EXPECT_OK(vibrator->getSupportedPrimitives(&supported));
 
-        for (auto primitive : kCompositePrimitives) {
+        for (CompositePrimitive primitive : kCompositePrimitives) {
             bool isPrimitiveSupported =
                 std::find(supported.begin(), supported.end(), primitive) != supported.end();
             int32_t duration;
 
-            Status status = vibrator->getPrimitiveDuration(primitive, &duration);
-
             if (isPrimitiveSupported) {
-                EXPECT_EQ(Status::EX_NONE, status.exceptionCode());
+                EXPECT_OK(vibrator->getPrimitiveDuration(primitive, &duration))
+                        << "\n  For primitive: " << toString(primitive) << " " << duration;
                 if (primitive != CompositePrimitive::NOOP) {
-                    ASSERT_GT(duration, 0) << toString(primitive) << " " << duration;
+                    ASSERT_GT(duration, 0)
+                            << "\n  For primitive: " << toString(primitive) << " " << duration;
                 }
             } else {
-                EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+                EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->getPrimitiveDuration(primitive, &duration))
+                        << "\n  For primitive: " << toString(primitive);
             }
         }
     }
@@ -446,14 +616,14 @@
     std::vector<CompositePrimitive> supported;
     int32_t maxDelay, maxSize;
 
-    ASSERT_TRUE(vibrator->getSupportedPrimitives(&supported).isOk());
-    EXPECT_EQ(Status::EX_NONE, vibrator->getCompositionDelayMax(&maxDelay).exceptionCode());
-    EXPECT_EQ(Status::EX_NONE, vibrator->getCompositionSizeMax(&maxSize).exceptionCode());
+    EXPECT_OK(vibrator->getSupportedPrimitives(&supported));
+    EXPECT_OK(vibrator->getCompositionDelayMax(&maxDelay));
+    EXPECT_OK(vibrator->getCompositionSizeMax(&maxSize));
 
     std::vector<CompositeEffect> composite;
 
     for (int i = 0; i < supported.size(); i++) {
-        auto primitive = supported[i];
+        CompositePrimitive primitive = supported[i];
         float t = static_cast<float>(i + 1) / supported.size();
         CompositeEffect effect;
 
@@ -467,8 +637,8 @@
     }
 
     if (composite.size() != 0) {
-        EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, nullptr).exceptionCode());
-        EXPECT_TRUE(vibrator->off().isOk());
+        EXPECT_OK(vibrator->compose(composite, nullptr));
+        EXPECT_OK(vibrator->off());
     }
 }
 
@@ -477,12 +647,12 @@
         GTEST_SKIP() << "CAP_COMPOSE_EFFECTS not supported";
     }
 
-    auto unsupported = kInvalidPrimitives;
+    std::vector<CompositePrimitive> unsupported(kInvalidPrimitives);
     std::vector<CompositePrimitive> supported;
 
-    ASSERT_TRUE(vibrator->getSupportedPrimitives(&supported).isOk());
+    EXPECT_OK(vibrator->getSupportedPrimitives(&supported));
 
-    for (auto primitive : kCompositePrimitives) {
+    for (CompositePrimitive primitive : kCompositePrimitives) {
         bool isPrimitiveSupported =
                 std::find(supported.begin(), supported.end(), primitive) != supported.end();
 
@@ -491,16 +661,15 @@
         }
     }
 
-    for (auto primitive : unsupported) {
+    for (CompositePrimitive primitive : unsupported) {
         std::vector<CompositeEffect> composite(1);
 
-        for (auto& effect : composite) {
+        for (CompositeEffect& effect : composite) {
             effect.delayMs = 0;
             effect.primitive = primitive;
             effect.scale = 1.0f;
         }
-        Status status = vibrator->compose(composite, nullptr);
-        EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+        EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->compose(composite, nullptr));
     }
 }
 
@@ -516,18 +685,18 @@
     effect.primitive = CompositePrimitive::CLICK;
 
     effect.scale = std::nextafter(0.0f, -1.0f);
-    EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, vibrator->compose(composite, nullptr).exceptionCode());
+    EXPECT_ILLEGAL_ARGUMENT(vibrator->compose(composite, nullptr));
 
     effect.scale = 0.0f;
-    EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, nullptr).exceptionCode());
-    EXPECT_TRUE(vibrator->off().isOk());
+    EXPECT_OK(vibrator->compose(composite, nullptr));
+    EXPECT_OK(vibrator->off());
 
     effect.scale = 1.0f;
-    EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, nullptr).exceptionCode());
-    EXPECT_TRUE(vibrator->off().isOk());
+    EXPECT_OK(vibrator->compose(composite, nullptr));
+    EXPECT_OK(vibrator->off());
 
     effect.scale = std::nextafter(1.0f, 2.0f);
-    EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, vibrator->compose(composite, nullptr).exceptionCode());
+    EXPECT_ILLEGAL_ARGUMENT(vibrator->compose(composite, nullptr));
 }
 
 TEST_P(VibratorAidl, ComposeDelayBoundary) {
@@ -537,7 +706,7 @@
 
     int32_t maxDelay;
 
-    EXPECT_EQ(Status::EX_NONE, vibrator->getCompositionDelayMax(&maxDelay).exceptionCode());
+    EXPECT_OK(vibrator->getCompositionDelayMax(&maxDelay));
 
     std::vector<CompositeEffect> composite(1);
     CompositeEffect& effect = composite[0];
@@ -546,19 +715,19 @@
     effect.scale = 1.0f;
 
     effect.delayMs = 0;
-    EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, nullptr).exceptionCode());
-    EXPECT_TRUE(vibrator->off().isOk());
+    EXPECT_OK(vibrator->compose(composite, nullptr));
+    EXPECT_OK(vibrator->off());
 
     effect.delayMs = 1;
-    EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, nullptr).exceptionCode());
-    EXPECT_TRUE(vibrator->off().isOk());
+    EXPECT_OK(vibrator->compose(composite, nullptr));
+    EXPECT_OK(vibrator->off());
 
     effect.delayMs = maxDelay;
-    EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, nullptr).exceptionCode());
-    EXPECT_TRUE(vibrator->off().isOk());
+    EXPECT_OK(vibrator->compose(composite, nullptr));
+    EXPECT_OK(vibrator->off());
 
     effect.delayMs = maxDelay + 1;
-    EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, vibrator->compose(composite, nullptr).exceptionCode());
+    EXPECT_ILLEGAL_ARGUMENT(vibrator->compose(composite, nullptr));
 }
 
 TEST_P(VibratorAidl, ComposeSizeBoundary) {
@@ -568,7 +737,7 @@
 
     int32_t maxSize;
 
-    EXPECT_EQ(Status::EX_NONE, vibrator->getCompositionSizeMax(&maxSize).exceptionCode());
+    EXPECT_OK(vibrator->getCompositionSizeMax(&maxSize));
 
     std::vector<CompositeEffect> composite(maxSize);
     CompositeEffect effect;
@@ -578,11 +747,11 @@
     effect.scale = 1.0f;
 
     std::fill(composite.begin(), composite.end(), effect);
-    EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, nullptr).exceptionCode());
-    EXPECT_TRUE(vibrator->off().isOk());
+    EXPECT_OK(vibrator->compose(composite, nullptr));
+    EXPECT_OK(vibrator->off());
 
     composite.emplace_back(effect);
-    EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, vibrator->compose(composite, nullptr).exceptionCode());
+    EXPECT_ILLEGAL_ARGUMENT(vibrator->compose(composite, nullptr));
 }
 
 TEST_P(VibratorAidl, ComposeCallback) {
@@ -591,18 +760,17 @@
     }
 
     std::vector<CompositePrimitive> supported;
+    EXPECT_OK(vibrator->getSupportedPrimitives(&supported));
 
-    ASSERT_TRUE(vibrator->getSupportedPrimitives(&supported).isOk());
-
-    for (auto primitive : supported) {
+    for (CompositePrimitive primitive : supported) {
         if (primitive == CompositePrimitive::NOOP) {
             continue;
         }
 
         std::promise<void> completionPromise;
         std::future<void> completionFuture{completionPromise.get_future()};
-        sp<CompletionCallback> callback =
-                new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
+        auto callback = ndk::SharedRefBase::make<CompletionCallback>(
+                [&completionPromise] { completionPromise.set_value(); });
         CompositeEffect effect;
         std::vector<CompositeEffect> composite;
         int32_t durationMs;
@@ -615,50 +783,50 @@
         effect.scale = 1.0f;
         composite.emplace_back(effect);
 
-        EXPECT_EQ(Status::EX_NONE,
-                  vibrator->getPrimitiveDuration(primitive, &durationMs).exceptionCode())
-                << toString(primitive);
+        EXPECT_OK(vibrator->getPrimitiveDuration(primitive, &durationMs))
+                << "\n  For primitive: " << toString(primitive);
         duration = std::chrono::milliseconds(durationMs);
 
         start = high_resolution_clock::now();
-        EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, callback).exceptionCode())
-                << toString(primitive);
+        EXPECT_OK(vibrator->compose(composite, callback))
+                << "\n  For primitive: " << toString(primitive);
 
         EXPECT_EQ(completionFuture.wait_for(duration + VIBRATION_CALLBACK_TIMEOUT),
                   std::future_status::ready)
-                << toString(primitive);
+                << "\n  For primitive: " << toString(primitive);
         end = high_resolution_clock::now();
 
         elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
-        EXPECT_GE(elapsed.count(), duration.count()) << toString(primitive);
+        EXPECT_GE(elapsed.count(), duration.count())
+                << "\n  For primitive: " << toString(primitive);
 
-        EXPECT_TRUE(vibrator->off().isOk());
+        EXPECT_OK(vibrator->off()) << "\n  For primitive: " << toString(primitive);
     }
 }
 
 TEST_P(VibratorAidl, AlwaysOn) {
     if (capabilities & IVibrator::CAP_ALWAYS_ON_CONTROL) {
         std::vector<Effect> supported;
-        ASSERT_TRUE(vibrator->getSupportedAlwaysOnEffects(&supported).isOk());
+        EXPECT_OK(vibrator->getSupportedAlwaysOnEffects(&supported));
 
         for (Effect effect : kEffects) {
             bool isEffectSupported =
                 std::find(supported.begin(), supported.end(), effect) != supported.end();
 
             for (EffectStrength strength : kEffectStrengths) {
-                Status status = vibrator->alwaysOnEnable(0, effect, strength);
+                ndk::ScopedAStatus status = vibrator->alwaysOnEnable(0, effect, strength);
 
                 if (isEffectSupported) {
-                    EXPECT_EQ(Status::EX_NONE, status.exceptionCode())
-                        << toString(effect) << " " << toString(strength);
+                    EXPECT_OK(std::move(status))
+                            << "\n  For effect: " << toString(effect) << " " << toString(strength);
                 } else {
-                    EXPECT_TRUE(isUnknownOrUnsupported(status))
-                            << status << " " << toString(effect) << " " << toString(strength);
+                    EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status))
+                            << "\n  For effect: " << toString(effect) << " " << toString(strength);
                 }
             }
         }
 
-        EXPECT_EQ(Status::EX_NONE, vibrator->alwaysOnDisable(0).exceptionCode());
+        EXPECT_OK(vibrator->alwaysOnDisable(0));
     }
 }
 
@@ -668,12 +836,12 @@
 
 TEST_P(VibratorAidl, GetQFactor) {
     float qFactor;
-    Status status = vibrator->getQFactor(&qFactor);
+    ndk::ScopedAStatus status = vibrator->getQFactor(&qFactor);
     if (capabilities & IVibrator::CAP_GET_Q_FACTOR) {
+        EXPECT_OK(std::move(status));
         ASSERT_GT(qFactor, 0);
-        EXPECT_EQ(status.exceptionCode(), Status::EX_NONE);
     } else {
-        EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+        EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status));
     }
 }
 
@@ -687,9 +855,9 @@
 
 TEST_P(VibratorAidl, GetBandwidthAmplitudeMap) {
     std::vector<float> bandwidthAmplitudeMap;
-    Status status = vibrator->getBandwidthAmplitudeMap(&bandwidthAmplitudeMap);
+    ndk::ScopedAStatus status = vibrator->getBandwidthAmplitudeMap(&bandwidthAmplitudeMap);
     if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
-        EXPECT_EQ(status.exceptionCode(), Status::EX_NONE);
+        EXPECT_OK(std::move(status));
         ASSERT_FALSE(bandwidthAmplitudeMap.empty());
 
         int minMapSize = (getResonantFrequencyHz(vibrator, capabilities) -
@@ -702,42 +870,42 @@
             ASSERT_LE(e, 1.0);
         }
     } else {
-        EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+        EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status));
     }
 }
 
 TEST_P(VibratorAidl, GetPwlePrimitiveDurationMax) {
     int32_t durationMs;
-    Status status = vibrator->getPwlePrimitiveDurationMax(&durationMs);
+    ndk::ScopedAStatus status = vibrator->getPwlePrimitiveDurationMax(&durationMs);
     if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
+        EXPECT_OK(std::move(status));
         ASSERT_NE(durationMs, 0);
-        EXPECT_EQ(status.exceptionCode(), Status::EX_NONE);
     } else {
-        EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+        EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status));
     }
 }
 
 TEST_P(VibratorAidl, GetPwleCompositionSizeMax) {
     int32_t maxSize;
-    Status status = vibrator->getPwleCompositionSizeMax(&maxSize);
+    ndk::ScopedAStatus status = vibrator->getPwleCompositionSizeMax(&maxSize);
     if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
+        EXPECT_OK(std::move(status));
         ASSERT_NE(maxSize, 0);
-        EXPECT_EQ(status.exceptionCode(), Status::EX_NONE);
     } else {
-        EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+        EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status));
     }
 }
 
 TEST_P(VibratorAidl, GetSupportedBraking) {
     std::vector<Braking> supported;
-    Status status = vibrator->getSupportedBraking(&supported);
+    ndk::ScopedAStatus status = vibrator->getSupportedBraking(&supported);
     if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
         bool isDefaultNoneSupported =
             std::find(supported.begin(), supported.end(), Braking::NONE) != supported.end();
+        EXPECT_OK(std::move(status));
         ASSERT_TRUE(isDefaultNoneSupported);
-        EXPECT_EQ(status.exceptionCode(), Status::EX_NONE);
     } else {
-        EXPECT_TRUE(isUnknownOrUnsupported(status)) << status;
+        EXPECT_UNKNOWN_OR_UNSUPPORTED(std::move(status));
     }
 }
 
@@ -746,7 +914,7 @@
         ActivePwle firstActive = composeValidActivePwle(vibrator, capabilities);
 
         std::vector<Braking> supported;
-        ASSERT_TRUE(vibrator->getSupportedBraking(&supported).isOk());
+        EXPECT_OK(vibrator->getSupportedBraking(&supported));
         bool isClabSupported =
             std::find(supported.begin(), supported.end(), Braking::CLAB) != supported.end();
         BrakingPwle firstBraking;
@@ -765,11 +933,11 @@
         secondBraking.braking = Braking::NONE;
         secondBraking.duration = 10;
 
-        auto pwleQueue =
-            std::vector<PrimitivePwle>{firstActive, firstBraking, secondActive, secondBraking};
+        std::vector<PrimitivePwle> pwleQueue = {firstActive, firstBraking, secondActive,
+                                                secondBraking};
 
-        EXPECT_EQ(Status::EX_NONE, vibrator->composePwle(pwleQueue, nullptr).exceptionCode());
-        EXPECT_TRUE(vibrator->off().isOk());
+        EXPECT_OK(vibrator->composePwle(pwleQueue, nullptr));
+        EXPECT_OK(vibrator->off());
     }
 }
 
@@ -780,8 +948,8 @@
 
     std::promise<void> completionPromise;
     std::future<void> completionFuture{completionPromise.get_future()};
-    sp<CompletionCallback> callback =
-        new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
+    auto callback = ndk::SharedRefBase::make<CompletionCallback>(
+            [&completionPromise] { completionPromise.set_value(); });
     int32_t segmentDurationMaxMs;
     vibrator->getPwlePrimitiveDurationMax(&segmentDurationMaxMs);
     uint32_t durationMs = segmentDurationMaxMs * 2 + 100;  // Sum of 2 active and 1 braking below
@@ -790,27 +958,25 @@
     ActivePwle active = composeValidActivePwle(vibrator, capabilities);
 
     std::vector<Braking> supported;
-    ASSERT_TRUE(vibrator->getSupportedBraking(&supported).isOk());
+    EXPECT_OK(vibrator->getSupportedBraking(&supported));
     bool isClabSupported =
         std::find(supported.begin(), supported.end(), Braking::CLAB) != supported.end();
     BrakingPwle braking;
     braking.braking = isClabSupported ? Braking::CLAB : Braking::NONE;
     braking.duration = 100;
 
-    auto pwleQueue = std::vector<PrimitivePwle>{active, braking, active};
+    std::vector<PrimitivePwle> pwleQueue = {active, braking, active};
 
-    EXPECT_TRUE(vibrator->composePwle(pwleQueue, callback).isOk());
+    EXPECT_OK(vibrator->composePwle(pwleQueue, callback));
     EXPECT_EQ(completionFuture.wait_for(timeout), std::future_status::ready);
-    EXPECT_TRUE(vibrator->off().isOk());
+    EXPECT_OK(vibrator->off());
 }
 
 TEST_P(VibratorAidl, ComposePwleSegmentBoundary) {
     if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
         std::vector<PrimitivePwle> pwleQueue;
         // test empty queue
-        EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
-                  vibrator->composePwle(pwleQueue, nullptr).exceptionCode());
-        EXPECT_TRUE(vibrator->off().isOk());
+        EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwle(pwleQueue, nullptr));
 
         ActivePwle active = composeValidActivePwle(vibrator, capabilities);
 
@@ -824,9 +990,7 @@
             pwleQueue.emplace_back(std::move(pwle));
         }
 
-        EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
-                  vibrator->composePwle(pwleQueue, nullptr).exceptionCode());
-        EXPECT_TRUE(vibrator->off().isOk());
+        EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwle(pwleQueue, nullptr));
     }
 }
 
@@ -836,20 +1000,16 @@
         active.startAmplitude = getAmplitudeMax() + 1.0;  // Amplitude greater than allowed
         active.endAmplitude = getAmplitudeMax() + 1.0;    // Amplitude greater than allowed
 
-        auto pwleQueueGreater = std::vector<PrimitivePwle>{active};
+        std::vector<PrimitivePwle> pwleQueueGreater = {active};
 
-        EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
-                  vibrator->composePwle(pwleQueueGreater, nullptr).exceptionCode());
-        EXPECT_TRUE(vibrator->off().isOk());
+        EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwle(pwleQueueGreater, nullptr));
 
         active.startAmplitude = getAmplitudeMin() - 1.0;  // Amplitude less than allowed
         active.endAmplitude = getAmplitudeMin() - 1.0;    // Amplitude less than allowed
 
-        auto pwleQueueLess = std::vector<PrimitivePwle>{active};
+        std::vector<PrimitivePwle> pwleQueueLess = {active};
 
-        EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
-                  vibrator->composePwle(pwleQueueLess, nullptr).exceptionCode());
-        EXPECT_TRUE(vibrator->off().isOk());
+        EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwle(pwleQueueLess, nullptr));
     }
 }
 
@@ -865,20 +1025,16 @@
             freqMaximumHz + freqResolutionHz;                    // Frequency greater than allowed
         active.endFrequency = freqMaximumHz + freqResolutionHz;  // Frequency greater than allowed
 
-        auto pwleQueueGreater = std::vector<PrimitivePwle>{active};
+        std::vector<PrimitivePwle> pwleQueueGreater = {active};
 
-        EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
-                  vibrator->composePwle(pwleQueueGreater, nullptr).exceptionCode());
-        EXPECT_TRUE(vibrator->off().isOk());
+        EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwle(pwleQueueGreater, nullptr));
 
         active.startFrequency = freqMinimumHz - freqResolutionHz;  // Frequency less than allowed
         active.endFrequency = freqMinimumHz - freqResolutionHz;    // Frequency less than allowed
 
-        auto pwleQueueLess = std::vector<PrimitivePwle>{active};
+        std::vector<PrimitivePwle> pwleQueueLess = {active};
 
-        EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
-                  vibrator->composePwle(pwleQueueLess, nullptr).exceptionCode());
-        EXPECT_TRUE(vibrator->off().isOk());
+        EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwle(pwleQueueLess, nullptr));
     }
 }
 
@@ -890,32 +1046,196 @@
         vibrator->getPwlePrimitiveDurationMax(&segmentDurationMaxMs);
         active.duration = segmentDurationMaxMs + 10;  // Segment duration greater than allowed
 
-        auto pwleQueue = std::vector<PrimitivePwle>{active};
+        std::vector<PrimitivePwle> pwleQueue = {active};
 
-        EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
-                  vibrator->composePwle(pwleQueue, nullptr).exceptionCode());
-        EXPECT_TRUE(vibrator->off().isOk());
+        EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwle(pwleQueue, nullptr));
     }
 }
 
+TEST_P(VibratorAidl, PwleV2FrequencyToOutputAccelerationMapHasValidFrequencyRange) {
+    if (!(capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2)) {
+        GTEST_SKIP() << "PWLE V2 not supported, skipping test";
+        return;
+    }
+
+    std::vector<PwleV2OutputMapEntry> frequencyToOutputAccelerationMap;
+    ndk::ScopedAStatus status =
+            vibrator->getPwleV2FrequencyToOutputAccelerationMap(&frequencyToOutputAccelerationMap);
+    EXPECT_OK(std::move(status));
+    ASSERT_FALSE(frequencyToOutputAccelerationMap.empty());
+    auto sharpnessRange =
+            pwle_v2_utils::getPwleV2SharpnessRange(vibrator, frequencyToOutputAccelerationMap);
+    // Validate the curve provides a usable sharpness range, which is a range of frequencies
+    // that are supported by the device.
+    ASSERT_TRUE(sharpnessRange.first >= 0);
+    // Validate that the sharpness range is a valid interval, not a single point.
+    ASSERT_TRUE(sharpnessRange.first < sharpnessRange.second);
+}
+
+TEST_P(VibratorAidl, GetPwleV2PrimitiveDurationMaxMillis) {
+    if (!(capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2)) {
+        GTEST_SKIP() << "PWLE V2 not supported, skipping test";
+        return;
+    }
+
+    int32_t durationMs;
+    ndk::ScopedAStatus status = vibrator->getPwleV2PrimitiveDurationMaxMillis(&durationMs);
+    EXPECT_OK(std::move(status));
+    ASSERT_GT(durationMs, 0);  // Ensure greater than zero
+    ASSERT_GE(durationMs, pwle_v2_utils::COMPOSE_PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS);
+}
+
+TEST_P(VibratorAidl, GetPwleV2CompositionSizeMax) {
+    if (!(capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2)) {
+        GTEST_SKIP() << "PWLE V2 not supported, skipping test";
+        return;
+    }
+
+    int32_t maxSize;
+    ndk::ScopedAStatus status = vibrator->getPwleV2CompositionSizeMax(&maxSize);
+    EXPECT_OK(std::move(status));
+    ASSERT_GT(maxSize, 0);  // Ensure greater than zero
+    ASSERT_GE(maxSize, pwle_v2_utils::COMPOSE_PWLE_V2_MIN_REQUIRED_SIZE);
+}
+
+TEST_P(VibratorAidl, GetPwleV2PrimitiveDurationMinMillis) {
+    if (!(capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2)) {
+        GTEST_SKIP() << "PWLE V2 not supported, skipping test";
+        return;
+    }
+
+    int32_t durationMs;
+    ndk::ScopedAStatus status = vibrator->getPwleV2PrimitiveDurationMinMillis(&durationMs);
+    EXPECT_OK(std::move(status));
+    ASSERT_GT(durationMs, 0);  // Ensure greater than zero
+    ASSERT_LE(durationMs, pwle_v2_utils::COMPOSE_PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS);
+}
+
+TEST_P(VibratorAidl, ComposeValidPwleV2Effect) {
+    if (!(capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2)) {
+        GTEST_SKIP() << "PWLE V2 not supported, skipping test";
+        return;
+    }
+
+    EXPECT_OK(vibrator->composePwleV2(pwle_v2_utils::composeValidPwleV2Effect(vibrator), nullptr));
+    EXPECT_OK(vibrator->off());
+}
+
+TEST_P(VibratorAidl, ComposePwleV2Unsupported) {
+    if (version < PWLE_V2_MIN_VERSION) {
+        EXPECT_EQ(capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2, 0)
+                << "Vibrator version " << version << " should not report PWLE V2 capability.";
+    }
+    if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2) return;
+
+    std::vector<PwleV2Primitive> pwleEffect{
+            PwleV2Primitive(/*amplitude=*/1.0f, /*frequencyHz=*/100.0f, /*timeMillis=*/50)};
+
+    EXPECT_UNKNOWN_OR_UNSUPPORTED(vibrator->composePwleV2(pwleEffect, nullptr));
+}
+
+TEST_P(VibratorAidl, ComposeValidPwleV2EffectWithCallback) {
+    if (!(capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2)) {
+        GTEST_SKIP() << "PWLE V2 not supported, skipping test";
+        return;
+    }
+
+    std::promise<void> completionPromise;
+    std::future<void> completionFuture{completionPromise.get_future()};
+    auto callback = ndk::SharedRefBase::make<CompletionCallback>(
+            [&completionPromise] { completionPromise.set_value(); });
+
+    int32_t minDuration;
+    EXPECT_OK(vibrator->getPwleV2PrimitiveDurationMinMillis(&minDuration));
+    auto timeout = std::chrono::milliseconds(minDuration) + VIBRATION_CALLBACK_TIMEOUT;
+    float minFrequency = pwle_v2_utils::getPwleV2FrequencyMinHz(vibrator);
+
+    EXPECT_OK(vibrator->composePwleV2(
+            {PwleV2Primitive(/*amplitude=*/0.5, minFrequency, minDuration)}, callback));
+    EXPECT_EQ(completionFuture.wait_for(timeout), std::future_status::ready);
+    EXPECT_OK(vibrator->off());
+}
+
+TEST_P(VibratorAidl, composePwleV2EffectWithTooManyPoints) {
+    if (!(capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2)) {
+        GTEST_SKIP() << "PWLE V2 not supported, skipping test";
+        return;
+    }
+
+    EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwleV2(
+            pwle_v2_utils::composePwleV2EffectWithTooManyPoints(vibrator), nullptr));
+}
+
+TEST_P(VibratorAidl, composeInvalidPwleV2Effect) {
+    if (!(capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS_V2)) {
+        GTEST_SKIP() << "PWLE V2 not supported, skipping test";
+        return;
+    }
+
+    // Retrieve min and max durations
+    int32_t minDurationMs, maxDurationMs;
+    EXPECT_OK(vibrator->getPwleV2PrimitiveDurationMinMillis(&minDurationMs));
+    EXPECT_OK(vibrator->getPwleV2PrimitiveDurationMaxMillis(&maxDurationMs));
+
+    std::vector<PwleV2Primitive> composePwle;
+
+    // Negative amplitude
+    composePwle.push_back(PwleV2Primitive(/*amplitude=*/-0.8f, /*frequency=*/100, minDurationMs));
+    EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwleV2(composePwle, nullptr))
+            << "Composing PWLE V2 effect with negative amplitude should fail";
+    composePwle.clear();
+
+    // Amplitude exceeding 1.0
+    composePwle.push_back(PwleV2Primitive(/*amplitude=*/1.2f, /*frequency=*/100, minDurationMs));
+    EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwleV2(composePwle, nullptr))
+            << "Composing PWLE V2 effect with amplitude greater than 1.0 should fail";
+    composePwle.clear();
+
+    // Duration exceeding maximum
+    composePwle.push_back(
+            PwleV2Primitive(/*amplitude=*/0.2f, /*frequency=*/100, maxDurationMs + 10));
+    EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwleV2(composePwle, nullptr))
+            << "Composing PWLE V2 effect with duration exceeding maximum should fail";
+    composePwle.clear();
+
+    // Negative duration
+    composePwle.push_back(PwleV2Primitive(/*amplitude=*/0.2f, /*frequency=*/100, /*time=*/-1));
+    EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwleV2(composePwle, nullptr))
+            << "Composing PWLE V2 effect with negative duration should fail";
+    composePwle.clear();
+
+    // Frequency below minimum
+    float minFrequency = pwle_v2_utils::getPwleV2FrequencyMinHz(vibrator);
+    composePwle.push_back(PwleV2Primitive(/*amplitude=*/0.2f, minFrequency - 1, minDurationMs));
+    EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwleV2(composePwle, nullptr))
+            << "Composing PWLE V2 effect with frequency below minimum should fail";
+    composePwle.clear();
+
+    // Frequency above maximum
+    float maxFrequency = pwle_v2_utils::getPwleV2FrequencyMaxHz(vibrator);
+    composePwle.push_back(PwleV2Primitive(/*amplitude=*/0.2f, maxFrequency + 1, minDurationMs));
+    EXPECT_ILLEGAL_ARGUMENT(vibrator->composePwleV2(composePwle, nullptr))
+            << "Composing PWLE V2 effect with frequency above maximum should fail";
+}
+
 std::vector<std::tuple<int32_t, int32_t>> GenerateVibratorMapping() {
     std::vector<std::tuple<int32_t, int32_t>> tuples;
-    auto managerAidlNames = android::getAidlHalInstanceNames(IVibratorManager::descriptor);
-    std::vector<int32_t> vibratorIds;
 
-    for (int i = 0; i < managerAidlNames.size(); i++) {
-        auto managerName = String16(managerAidlNames[i].c_str());
-        auto vibratorManager = android::waitForDeclaredService<IVibratorManager>(managerName);
+    std::vector<std::string> managerNames = findVibratorManagerNames();
+    std::vector<int32_t> vibratorIds;
+    for (int i = 0; i < managerNames.size(); i++) {
+        auto vibratorManager = IVibratorManager::fromBinder(
+                ndk::SpAIBinder(AServiceManager_waitForService(managerNames[i].c_str())));
         if (vibratorManager->getVibratorIds(&vibratorIds).isOk()) {
-            for (auto &vibratorId : vibratorIds) {
-                tuples.push_back(std::make_tuple(i, vibratorId));
+            for (int32_t vibratorId : vibratorIds) {
+                tuples.emplace_back(i, vibratorId);
             }
         }
     }
 
-    auto vibratorAidlNames = android::getAidlHalInstanceNames(IVibrator::descriptor);
-    for (int i = 0; i < vibratorAidlNames.size(); i++) {
-        tuples.push_back(std::make_tuple(-1, i));
+    std::vector<std::string> vibratorNames = findUnmanagedVibratorNames();
+    for (int i = 0; i < vibratorNames.size(); i++) {
+        tuples.emplace_back(-1, i);
     }
 
     return tuples;
@@ -935,8 +1255,11 @@
                          PrintGeneratedTest);
 
 int main(int argc, char **argv) {
+    // Random values are used in the implementation.
+    std::srand(std::time(nullptr));
+
     ::testing::InitGoogleTest(&argc, argv);
-    ProcessState::self()->setThreadPoolMaxThreadCount(1);
-    ProcessState::self()->startThreadPool();
+    ABinderProcess_setThreadPoolMaxThreadCount(1);
+    ABinderProcess_startThreadPool();
     return RUN_ALL_TESTS();
 }
diff --git a/vibrator/aidl/vts/persistable_bundle_utils.h b/vibrator/aidl/vts/persistable_bundle_utils.h
new file mode 100644
index 0000000..a765a49
--- /dev/null
+++ b/vibrator/aidl/vts/persistable_bundle_utils.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef VIBRATOR_HAL_PERSISTABLE_BUNDLE_UTILS_H
+#define VIBRATOR_HAL_PERSISTABLE_BUNDLE_UTILS_H
+
+#include <android/persistable_bundle_aidl.h>
+
+#include <cstdlib>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+namespace testing {
+
+using aidl::android::os::PersistableBundle;
+
+namespace {
+
+template <typename T>
+T nextValue() {
+    return static_cast<T>(std::rand());
+}
+
+template <>
+std::string nextValue() {
+    std::string str;
+    uint8_t entryCount = nextValue<uint8_t>();
+    for (uint8_t i = 0; i < entryCount; i++) {
+        str.push_back(nextValue<char>());
+    }
+    return str;
+}
+
+template <typename T>
+T nextValue(T limit) {
+    assert(limit > 0);
+    return static_cast<T>(std::rand()) / (static_cast<T>(RAND_MAX / limit));
+}
+
+template <typename T>
+void fillVector(std::vector<T>* values) {
+    uint8_t entryCount = nextValue<uint8_t>();
+    for (uint8_t i = 0; i < entryCount; i++) {
+        values->push_back(nextValue<T>());
+    }
+}
+
+const std::vector<std::function<void(PersistableBundle*, const std::string&)>>
+        sPersistableBundleSetters = {[](PersistableBundle* bundle, const std::string& key) -> void {
+                                         bundle->putBoolean(key, nextValue<bool>());
+                                     },
+                                     [](PersistableBundle* bundle, const std::string& key) -> void {
+                                         bundle->putInt(key, nextValue<int32_t>());
+                                     },
+                                     [](PersistableBundle* bundle, const std::string& key) -> void {
+                                         bundle->putLong(key, nextValue<int64_t>());
+                                     },
+                                     [](PersistableBundle* bundle, const std::string& key) -> void {
+                                         bundle->putDouble(key, nextValue<double>());
+                                     },
+                                     [](PersistableBundle* bundle, const std::string& key) -> void {
+                                         bundle->putString(key, nextValue<std::string>());
+                                     },
+                                     [](PersistableBundle* bundle, const std::string& key) -> void {
+                                         std::vector<bool> value;
+                                         fillVector<bool>(&value);
+                                         bundle->putBooleanVector(key, value);
+                                     },
+                                     [](PersistableBundle* bundle, const std::string& key) -> void {
+                                         std::vector<int32_t> value;
+                                         fillVector<int32_t>(&value);
+                                         bundle->putIntVector(key, value);
+                                     },
+                                     [](PersistableBundle* bundle, const std::string& key) -> void {
+                                         std::vector<int64_t> value;
+                                         fillVector<int64_t>(&value);
+                                         bundle->putLongVector(key, value);
+                                     },
+                                     [](PersistableBundle* bundle, const std::string& key) -> void {
+                                         std::vector<double> value;
+                                         fillVector<double>(&value);
+                                         bundle->putDoubleVector(key, value);
+                                     },
+                                     [](PersistableBundle* bundle, const std::string& key) -> void {
+                                         std::vector<std::string> value;
+                                         fillVector<std::string>(&value);
+                                         bundle->putStringVector(key, value);
+                                     }};
+
+}  // namespace
+
+void fillBasicData(PersistableBundle* bundle) {
+    bundle->putBoolean("test_bool", true);
+    bundle->putInt("test_int", 2147483647);
+    bundle->putLong("test_long", 2147483647L);
+    bundle->putDouble("test_double", 1.23);
+    bundle->putString("test_string", "test data");
+    bundle->putBooleanVector("test_bool_vector", {true, false, false});
+    bundle->putIntVector("test_int_vector", {1, 2, 3, 4});
+    bundle->putLongVector("test_long_vector", {100L, 200L, 300L});
+    bundle->putDoubleVector("test_double_vector", {1.1, 2.2});
+    bundle->putStringVector("test_string_vector", {"test", "val"});
+}
+
+void fillRandomData(PersistableBundle* bundle) {
+    uint8_t entryCount = nextValue<uint8_t>();
+    for (uint8_t i = 0; i < entryCount; i++) {
+        std::string key(nextValue<std::string>());
+        uint8_t setterIdx = nextValue<uint8_t>(sPersistableBundleSetters.size() - 1);
+        sPersistableBundleSetters[setterIdx](bundle, key);
+    }
+}
+
+}  // namespace testing
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
+
+#endif  // VIBRATOR_HAL_PERSISTABLE_BUNDLE_UTILS_H
diff --git a/vibrator/aidl/vts/pwle_v2_utils.h b/vibrator/aidl/vts/pwle_v2_utils.h
new file mode 100644
index 0000000..2163908
--- /dev/null
+++ b/vibrator/aidl/vts/pwle_v2_utils.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VIBRATOR_HAL_PWLE_V2_UTILS_H
+#define VIBRATOR_HAL_PWLE_V2_UTILS_H
+
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+#include "test_utils.h"
+
+using aidl::android::hardware::vibrator::IVibrator;
+using aidl::android::hardware::vibrator::PwleV2OutputMapEntry;
+using aidl::android::hardware::vibrator::PwleV2Primitive;
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+namespace testing {
+namespace pwlev2 {
+
+static constexpr int32_t COMPOSE_PWLE_V2_MIN_REQUIRED_SIZE = 16;
+static constexpr int32_t COMPOSE_PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS = 1000;
+static constexpr int32_t COMPOSE_PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS = 20;
+static constexpr int32_t COMPOSE_PWLE_V2_MIN_REQUIRED_SENSITIVITY_DB_SL = 10;
+
+namespace {
+/**
+ * Returns a vector of (frequency in Hz, acceleration in dB) pairs, where the acceleration
+ * value denotes the minimum output required at the corresponding frequency to be perceptible
+ * by a human.
+ */
+static std::vector<std::pair<float, float>> getMinPerceptibleLevel() {
+    return {{0.4f, -97.81f},   {2.0f, -69.86f},   {3.0f, -62.81f},    {4.0f, -58.81f},
+            {5.0f, -56.69f},   {6.0f, -54.77f},   {7.2f, -52.85f},    {8.0f, -51.77f},
+            {8.64f, -50.84f},  {10.0f, -48.90f},  {10.37f, -48.52f},  {12.44f, -46.50f},
+            {14.93f, -44.43f}, {15.0f, -44.35f},  {17.92f, -41.96f},  {20.0f, -40.36f},
+            {21.5f, -39.60f},  {25.0f, -37.48f},  {25.8f, -36.93f},   {30.0f, -34.31f},
+            {35.0f, -33.13f},  {40.0f, -32.81f},  {50.0f, -31.94f},   {60.0f, -31.77f},
+            {70.0f, -31.59f},  {72.0f, -31.55f},  {80.0f, -31.77f},   {86.4f, -31.94f},
+            {90.0f, -31.73f},  {100.0f, -31.90f}, {103.68f, -31.77f}, {124.42f, -31.70f},
+            {149.3f, -31.38f}, {150.0f, -31.35f}, {179.16f, -31.02f}, {200.0f, -30.86f},
+            {215.0f, -30.35f}, {250.0f, -28.98f}, {258.0f, -28.68f},  {300.0f, -26.81f},
+            {400.0f, -19.81f}};
+}
+
+static float interpolateLinearly(const std::vector<float>& xAxis, const std::vector<float>& yAxis,
+                                 float x) {
+    EXPECT_TRUE(!xAxis.empty());
+    EXPECT_TRUE(xAxis.size() == yAxis.size());
+
+    if (x <= xAxis.front()) return yAxis.front();
+    if (x >= xAxis.back()) return yAxis.back();
+
+    auto it = std::upper_bound(xAxis.begin(), xAxis.end(), x);
+    int i = std::distance(xAxis.begin(), it) - 1;  // Index of the lower bound
+
+    const float& x0 = xAxis[i];
+    const float& y0 = yAxis[i];
+    const float& x1 = xAxis[i + 1];
+    const float& y1 = yAxis[i + 1];
+
+    return y0 + (x - x0) * (y1 - y0) / (x1 - x0);
+}
+
+static float minPerceptibleDbCurve(float frequency) {
+    // Initialize minPerceptibleMap only once
+    static auto minPerceptibleMap = []() -> std::function<float(float)> {
+        static std::vector<float> minPerceptibleFrequencies;
+        static std::vector<float> minPerceptibleAccelerations;
+
+        auto minPerceptibleLevel = getMinPerceptibleLevel();
+        // Sort the 'minPerceptibleLevel' data in ascending order based on the
+        // frequency values (first element of each pair).
+        std::sort(minPerceptibleLevel.begin(), minPerceptibleLevel.end(),
+                  [](const auto& a, const auto& b) { return a.first < b.first; });
+
+        for (const auto& entry : minPerceptibleLevel) {
+            minPerceptibleFrequencies.push_back(entry.first);
+            minPerceptibleAccelerations.push_back(entry.second);
+        }
+
+        return [&](float freq) {
+            return interpolateLinearly(minPerceptibleFrequencies, minPerceptibleAccelerations,
+                                       freq);
+        };
+    }();
+
+    return minPerceptibleMap(frequency);
+}
+
+static float convertSensitivityLevelToDecibel(int sl, float frequency) {
+    return sl + minPerceptibleDbCurve(frequency);
+}
+
+static float convertDecibelToAcceleration(float db) {
+    return std::pow(10.0f, db / 20.0f);
+}
+}  // namespace
+
+static float convertSensitivityLevelToAcceleration(int sl, float frequency) {
+    return pwlev2::convertDecibelToAcceleration(
+            pwlev2::convertSensitivityLevelToDecibel(sl, frequency));
+}
+
+static float getPwleV2FrequencyMinHz(const std::shared_ptr<IVibrator>& vibrator) {
+    std::vector<PwleV2OutputMapEntry> frequencyToOutputAccelerationMap;
+    EXPECT_OK(
+            vibrator->getPwleV2FrequencyToOutputAccelerationMap(&frequencyToOutputAccelerationMap));
+    EXPECT_TRUE(!frequencyToOutputAccelerationMap.empty());
+    // We can't use ASSERT_TRUE() above because this is a non-void function,
+    // but we need to return to assure we don't crash from a null dereference.
+    if (frequencyToOutputAccelerationMap.empty()) {
+        return std::numeric_limits<float>::quiet_NaN();
+    }
+
+    auto entry = std::min_element(
+            frequencyToOutputAccelerationMap.begin(), frequencyToOutputAccelerationMap.end(),
+            [](const auto& a, const auto& b) { return a.frequencyHz < b.frequencyHz; });
+
+    return entry->frequencyHz;
+}
+
+static float getPwleV2FrequencyMaxHz(const std::shared_ptr<IVibrator>& vibrator) {
+    std::vector<PwleV2OutputMapEntry> frequencyToOutputAccelerationMap;
+    EXPECT_OK(
+            vibrator->getPwleV2FrequencyToOutputAccelerationMap(&frequencyToOutputAccelerationMap));
+    EXPECT_TRUE(!frequencyToOutputAccelerationMap.empty());
+    // We can't use ASSERT_TRUE() above because this is a non-void function,
+    // but we need to return to assure we don't crash from a null dereference.
+    if (frequencyToOutputAccelerationMap.empty()) {
+        return std::numeric_limits<float>::quiet_NaN();
+    }
+
+    auto entry = std::max_element(
+            frequencyToOutputAccelerationMap.begin(), frequencyToOutputAccelerationMap.end(),
+            [](const auto& a, const auto& b) { return a.frequencyHz < b.frequencyHz; });
+
+    return entry->frequencyHz;
+}
+
+static std::vector<PwleV2Primitive> composeValidPwleV2Effect(
+        const std::shared_ptr<IVibrator>& vibrator) {
+    int32_t minDurationMs;
+    EXPECT_OK(vibrator->getPwleV2PrimitiveDurationMinMillis(&minDurationMs));
+    int32_t maxDurationMs;
+    EXPECT_OK(vibrator->getPwleV2PrimitiveDurationMaxMillis(&maxDurationMs));
+    float minFrequency = getPwleV2FrequencyMinHz(vibrator);
+    float maxFrequency = getPwleV2FrequencyMaxHz(vibrator);
+    int32_t maxCompositionSize;
+    EXPECT_OK(vibrator->getPwleV2CompositionSizeMax(&maxCompositionSize));
+
+    std::vector<PwleV2Primitive> pwleEffect;
+
+    pwleEffect.emplace_back(0.1f, minFrequency, minDurationMs);
+    pwleEffect.emplace_back(0.5f, maxFrequency, maxDurationMs);
+
+    float variedFrequency = (minFrequency + maxFrequency) / 2.0f;
+    for (int i = 0; i < maxCompositionSize - 2; i++) {
+        pwleEffect.emplace_back(0.7f, variedFrequency, minDurationMs);
+    }
+
+    return pwleEffect;
+}
+
+static std::vector<PwleV2Primitive> composePwleV2EffectWithTooManyPoints(
+        const std::shared_ptr<IVibrator>& vibrator) {
+    int32_t minDurationMs, maxCompositionSize;
+    EXPECT_OK(vibrator->getPwleV2PrimitiveDurationMinMillis(&minDurationMs));
+    EXPECT_OK(vibrator->getPwleV2CompositionSizeMax(&maxCompositionSize));
+    float maxFrequency = getPwleV2FrequencyMaxHz(vibrator);
+
+    std::vector<PwleV2Primitive> pwleEffect(maxCompositionSize + 1);  // +1 to exceed the limit
+
+    std::fill(pwleEffect.begin(), pwleEffect.end(),
+              PwleV2Primitive(/*amplitude=*/0.2f, maxFrequency, minDurationMs));
+
+    return pwleEffect;
+}
+
+static std::pair<float, float> getPwleV2SharpnessRange(
+        const std::shared_ptr<IVibrator>& vibrator,
+        std::vector<PwleV2OutputMapEntry> freqToOutputAccelerationMap) {
+    std::pair<float, float> sharpnessRange = {-1, -1};
+
+    // Sort the entries by frequency in ascending order
+    std::sort(freqToOutputAccelerationMap.begin(), freqToOutputAccelerationMap.end(),
+              [](const auto& a, const auto& b) { return a.frequencyHz < b.frequencyHz; });
+
+    for (const auto& entry : freqToOutputAccelerationMap) {
+        float minAcceptableOutputAcceleration = convertSensitivityLevelToAcceleration(
+                pwlev2::COMPOSE_PWLE_V2_MIN_REQUIRED_SENSITIVITY_DB_SL, entry.frequencyHz);
+
+        if (sharpnessRange.first < 0 &&
+            minAcceptableOutputAcceleration <= entry.maxOutputAccelerationGs) {
+            sharpnessRange.first = entry.frequencyHz;  // Found the lower bound
+        } else if (sharpnessRange.first >= 0 &&
+                   minAcceptableOutputAcceleration >= entry.maxOutputAccelerationGs) {
+            sharpnessRange.second = entry.frequencyHz;  // Found the upper bound
+            return sharpnessRange;
+        }
+    }
+
+    if (sharpnessRange.first >= 0) {
+        // If only the lower bound was found, set the upper bound to the max frequency.
+        sharpnessRange.second = getPwleV2FrequencyMaxHz(vibrator);
+    }
+
+    return sharpnessRange;
+}
+}  // namespace pwlev2
+}  // namespace testing
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
+#endif  // VIBRATOR_HAL_PWLE_V2_UTILS_H
diff --git a/vibrator/aidl/vts/test_utils.h b/vibrator/aidl/vts/test_utils.h
new file mode 100644
index 0000000..aaf3211
--- /dev/null
+++ b/vibrator/aidl/vts/test_utils.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef VIBRATOR_HAL_TEST_UTILS_H
+#define VIBRATOR_HAL_TEST_UTILS_H
+
+#include <android/binder_auto_utils.h>
+#include <gtest/gtest.h>
+
+#if !defined(EXPECT_OK)
+#define EXPECT_OK(expression)                                                \
+    GTEST_AMBIGUOUS_ELSE_BLOCKER_                                            \
+    if (const ::ndk::ScopedAStatus&& _status = (expression); _status.isOk()) \
+        ;                                                                    \
+    else                                                                     \
+        ADD_FAILURE() << "Expected STATUS_OK for: " << #expression << "\n  Actual: " << _status
+#else
+#error Macro EXPECT_OK already defined unexpectedly
+#endif
+
+#if !defined(EXPECT_UNKNOWN_OR_UNSUPPORTED)
+#define EXPECT_UNKNOWN_OR_UNSUPPORTED(expression)                                                \
+    GTEST_AMBIGUOUS_ELSE_BLOCKER_                                                                \
+    if (const ::ndk::ScopedAStatus&& _status = (expression);                                     \
+        _status.getExceptionCode() == EX_UNSUPPORTED_OPERATION ||                                \
+        _status.getStatus() == STATUS_UNKNOWN_TRANSACTION)                                       \
+        ;                                                                                        \
+    else                                                                                         \
+        ADD_FAILURE() << "Expected STATUS_UNKNOWN_TRANSACTION or EX_UNSUPPORTED_OPERATION for: " \
+                      << #expression << "\n  Actual: " << _status
+#else
+#error Macro EXPECT_UNKNOWN_OR_UNSUPPORTED already defined unexpectedly
+#endif
+
+#if !defined(EXPECT_ILLEGAL_ARGUMENT)
+#define EXPECT_ILLEGAL_ARGUMENT(expression)                                  \
+    GTEST_AMBIGUOUS_ELSE_BLOCKER_                                            \
+    if (const ::ndk::ScopedAStatus&& _status = (expression);                 \
+        _status.getExceptionCode() == EX_ILLEGAL_ARGUMENT)                   \
+        ;                                                                    \
+    else                                                                     \
+        ADD_FAILURE() << "Expected EX_ILLEGAL_ARGUMENT for: " << #expression \
+                      << "\n  Actual: " << _status
+#else
+#error Macro EXPECT_ILLEGAL_ARGUMENT already defined unexpectedly
+#endif
+
+#endif  // VIBRATOR_HAL_TEST_UTILS_H
diff --git a/vibrator/bench/Android.bp b/vibrator/bench/Android.bp
index 87bdab4..cd56516 100644
--- a/vibrator/bench/Android.bp
+++ b/vibrator/bench/Android.bp
@@ -30,12 +30,12 @@
         "benchmark.cpp",
     ],
     shared_libs: [
-        "android.hardware.vibrator-V2-cpp",
+        "android.hardware.vibrator-V3-ndk",
         "android.hardware.vibrator@1.0",
         "android.hardware.vibrator@1.1",
         "android.hardware.vibrator@1.2",
         "android.hardware.vibrator@1.3",
-        "libbinder",
+        "libbinder_ndk",
         "libhardware",
         "libhidlbase",
         "libutils",
diff --git a/vibrator/bench/benchmark.cpp b/vibrator/bench/benchmark.cpp
index deaa6f2..8fe9cf7 100644
--- a/vibrator/bench/benchmark.cpp
+++ b/vibrator/bench/benchmark.cpp
@@ -16,15 +16,14 @@
 
 #include "benchmark/benchmark.h"
 
+#include <aidl/android/hardware/vibrator/BnVibratorCallback.h>
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
 #include <android/hardware/vibrator/1.3/IVibrator.h>
-#include <android/hardware/vibrator/BnVibratorCallback.h>
-#include <android/hardware/vibrator/IVibrator.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
 #include <future>
 
-using ::android::enum_range;
-using ::android::sp;
 using ::android::hardware::hidl_enum_range;
 using ::android::hardware::Return;
 using ::android::hardware::details::hidl_enum_values;
@@ -33,10 +32,11 @@
 using ::benchmark::kMicrosecond;
 using ::benchmark::State;
 using ::benchmark::internal::Benchmark;
+using ::ndk::enum_range;
 
 using namespace ::std::chrono_literals;
 
-namespace Aidl = ::android::hardware::vibrator;
+namespace Aidl = ::aidl::android::hardware::vibrator;
 namespace V1_0 = ::android::hardware::vibrator::V1_0;
 namespace V1_1 = ::android::hardware::vibrator::V1_1;
 namespace V1_2 = ::android::hardware::vibrator::V1_2;
@@ -56,8 +56,8 @@
 class BaseBench : public Fixture {
   public:
     void SetUp(State& /*state*/) override {
-        android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
-        android::ProcessState::self()->startThreadPool();
+        ABinderProcess_setThreadPoolMaxThreadCount(1);
+        ABinderProcess_startThreadPool();
     }
 
     void TearDown(State& /*state*/) override {
@@ -75,7 +75,7 @@
     auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); }
 
   protected:
-    sp<I> mVibrator;
+    std::shared_ptr<I> mVibrator;
 };
 
 template <typename I>
@@ -83,7 +83,12 @@
   public:
     void SetUp(State& state) override {
         BaseBench<I>::SetUp(state);
-        this->mVibrator = I::getService();
+        auto service = I::getService();
+        if (service) {
+            this->mVibrator = std::shared_ptr<I>(service.release());
+        } else {
+            this->mVibrator = nullptr;
+        }
     }
 
   protected:
@@ -356,7 +361,9 @@
   public:
     void SetUp(State& state) override {
         BaseBench::SetUp(state);
-        this->mVibrator = android::waitForVintfService<Aidl::IVibrator>();
+        auto serviceName = std::string(Aidl::IVibrator::descriptor) + "/default";
+        this->mVibrator = Aidl::IVibrator::fromBinder(
+                ndk::SpAIBinder(AServiceManager_waitForService(serviceName.c_str())));
     }
 
     void TearDown(State& state) override {
@@ -373,14 +380,24 @@
         return (deviceCapabilities & capabilities) == capabilities;
     }
 
-    bool shouldSkipWithError(State& state, const android::binder::Status&& status) {
+    bool shouldSkipWithError(State& state, const ndk::ScopedAStatus&& status) {
         if (!status.isOk()) {
-            state.SkipWithError(status.toString8().c_str());
+            state.SkipWithError(status.getMessage());
             return true;
         }
         return false;
     }
 
+    void waitForComplete(std::future<void>& callbackFuture) {
+        // Wait until the HAL has finished processing previous vibration before starting a new one,
+        // so the HAL state is consistent on each run and metrics are less noisy. Some of the newest
+        // HAL implementations are waiting on previous vibration cleanup and might be significantly
+        // slower, so make sure we measure vibrations on a clean slate.
+        if (callbackFuture.valid()) {
+            callbackFuture.wait_for(VIBRATION_CALLBACK_TIMEOUT);
+        }
+    }
+
     static void SlowBenchConfig(Benchmark* b) { b->Iterations(VIBRATION_ITERATIONS); }
 };
 
@@ -397,18 +414,12 @@
     HalCallback() = default;
     ~HalCallback() = default;
 
-    android::binder::Status onComplete() override {
+    ndk::ScopedAStatus onComplete() override {
         mPromise.set_value();
-        return android::binder::Status::ok();
+        return ndk::ScopedAStatus::ok();
     }
 
-    void waitForComplete() {
-        // Wait until the HAL has finished processing previous vibration before starting a new one,
-        // so the HAL state is consistent on each run and metrics are less noisy. Some of the newest
-        // HAL implementations are waiting on previous vibration cleanup and might be significantly
-        // slower, so make sure we measure vibrations on a clean slate.
-        mPromise.get_future().wait_for(VIBRATION_CALLBACK_TIMEOUT);
-    }
+    std::future<void> getFuture() { return mPromise.get_future(); }
 
   private:
     std::promise<void> mPromise;
@@ -418,7 +429,11 @@
     auto ms = MAX_ON_DURATION_MS;
 
     for (auto _ : state) {
-        auto cb = hasCapabilities(Aidl::IVibrator::CAP_ON_CALLBACK) ? new HalCallback() : nullptr;
+        auto cb = hasCapabilities(Aidl::IVibrator::CAP_ON_CALLBACK)
+                          ? ndk::SharedRefBase::make<HalCallback>()
+                          : nullptr;
+        // Grab the future before callback promise is destroyed by the HAL.
+        auto cbFuture = cb ? cb->getFuture() : std::future<void>();
 
         // Test
         if (shouldSkipWithError(state, mVibrator->on(ms, cb))) {
@@ -430,9 +445,7 @@
         if (shouldSkipWithError(state, mVibrator->off())) {
             return;
         }
-        if (cb) {
-            cb->waitForComplete();
-        }
+        waitForComplete(cbFuture);
         state.ResumeTiming();
     }
 });
@@ -441,7 +454,11 @@
     auto ms = MAX_ON_DURATION_MS;
 
     for (auto _ : state) {
-        auto cb = hasCapabilities(Aidl::IVibrator::CAP_ON_CALLBACK) ? new HalCallback() : nullptr;
+        auto cb = hasCapabilities(Aidl::IVibrator::CAP_ON_CALLBACK)
+                          ? ndk::SharedRefBase::make<HalCallback>()
+                          : nullptr;
+        // Grab the future before callback promise is destroyed by the HAL.
+        auto cbFuture = cb ? cb->getFuture() : std::future<void>();
 
         // Setup
         state.PauseTiming();
@@ -457,9 +474,7 @@
 
         // Cleanup
         state.PauseTiming();
-        if (cb) {
-            cb->waitForComplete();
-        }
+        waitForComplete(cbFuture);
         state.ResumeTiming();
     }
 });
@@ -483,7 +498,9 @@
         return;
     }
 
-    auto cb = hasCapabilities(Aidl::IVibrator::CAP_ON_CALLBACK) ? new HalCallback() : nullptr;
+    auto cb = hasCapabilities(Aidl::IVibrator::CAP_ON_CALLBACK)
+                      ? ndk::SharedRefBase::make<HalCallback>()
+                      : nullptr;
     if (shouldSkipWithError(state, mVibrator->on(ms, cb))) {
         return;
     }
@@ -685,8 +702,11 @@
     int32_t lengthMs = 0;
 
     for (auto _ : state) {
-        auto cb = hasCapabilities(Aidl::IVibrator::CAP_PERFORM_CALLBACK) ? new HalCallback()
-                                                                         : nullptr;
+        auto cb = hasCapabilities(Aidl::IVibrator::CAP_PERFORM_CALLBACK)
+                          ? ndk::SharedRefBase::make<HalCallback>()
+                          : nullptr;
+        // Grab the future before callback promise is destroyed by the HAL.
+        auto cbFuture = cb ? cb->getFuture() : std::future<void>();
 
         // Test
         if (shouldSkipWithError(state, mVibrator->perform(effect, strength, cb, &lengthMs))) {
@@ -698,9 +718,7 @@
         if (shouldSkipWithError(state, mVibrator->off())) {
             return;
         }
-        if (cb) {
-            cb->waitForComplete();
-        }
+        waitForComplete(cbFuture);
         state.ResumeTiming();
     }
 });
@@ -799,7 +817,9 @@
     effects.push_back(effect);
 
     for (auto _ : state) {
-        auto cb = new HalCallback();
+        auto cb = ndk::SharedRefBase::make<HalCallback>();
+        // Grab the future before callback promise is moved and destroyed by the HAL.
+        auto cbFuture = cb->getFuture();
 
         // Test
         if (shouldSkipWithError(state, mVibrator->compose(effects, cb))) {
@@ -811,7 +831,7 @@
         if (shouldSkipWithError(state, mVibrator->off())) {
             return;
         }
-        cb->waitForComplete();
+        waitForComplete(cbFuture);
         state.ResumeTiming();
     }
 });
diff --git a/wifi/aidl/default/aidl_struct_util.cpp b/wifi/aidl/default/aidl_struct_util.cpp
index d82450e..d99edaa 100644
--- a/wifi/aidl/default/aidl_struct_util.cpp
+++ b/wifi/aidl/default/aidl_struct_util.cpp
@@ -3394,6 +3394,7 @@
     *legacy_request = {};
 
     legacy_request->service_instance_id = aidl_request.bootstrappingInstanceId;
+    legacy_request->bootstrapping_instance_id = aidl_request.bootstrappingInstanceId;
     legacy_request->rsp_code = aidl_request.acceptRequest ? NAN_BOOTSTRAPPING_REQUEST_ACCEPT
                                                           : NAN_BOOTSTRAPPING_REQUEST_REJECT;
     legacy_request->publish_subscribe_id = static_cast<uint8_t>(aidl_request.discoverySessionId);
diff --git a/wifi/aidl/default/wifi_chip.cpp b/wifi/aidl/default/wifi_chip.cpp
index 9b9c565..fccfc15 100644
--- a/wifi/aidl/default/wifi_chip.cpp
+++ b/wifi/aidl/default/wifi_chip.cpp
@@ -62,7 +62,9 @@
 std::vector<std::string> getNames(std::vector<std::shared_ptr<Iface>>& ifaces) {
     std::vector<std::string> names;
     for (const auto& iface : ifaces) {
-        names.emplace_back(iface->getName());
+        if (iface) {
+            names.emplace_back(iface->getName());
+        }
     }
     return names;
 }
@@ -971,6 +973,10 @@
     }
     std::shared_ptr<WifiNanIface> iface =
             WifiNanIface::create(ifname, is_dedicated_iface, legacy_hal_, iface_util_);
+    if (!iface) {
+        LOG(ERROR) << "Unable to create NAN iface";
+        return {nullptr, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+    }
     nan_ifaces_.push_back(iface);
     for (const auto& callback : event_cb_handler_.getCallbacks()) {
         if (!callback->onIfaceAdded(IfaceType::NAN_IFACE, ifname).isOk()) {
diff --git a/wifi/aidl/vts/functional/wifi_aidl_test_utils.cpp b/wifi/aidl/vts/functional/wifi_aidl_test_utils.cpp
index 21d50ac..c68d8fd 100644
--- a/wifi/aidl/vts/functional/wifi_aidl_test_utils.cpp
+++ b/wifi/aidl/vts/functional/wifi_aidl_test_utils.cpp
@@ -177,8 +177,7 @@
     return iface;
 }
 
-std::shared_ptr<IWifiApIface> getWifiApIface(const char* instance_name) {
-    std::shared_ptr<IWifiChip> wifi_chip = getWifiChip(instance_name);
+std::shared_ptr<IWifiApIface> getWifiApIface(std::shared_ptr<IWifiChip> wifi_chip) {
     if (!wifi_chip.get()) {
         return nullptr;
     }
@@ -193,6 +192,11 @@
     return iface;
 }
 
+std::shared_ptr<IWifiApIface> getWifiApIface(const char* instance_name) {
+    std::shared_ptr<IWifiChip> wifi_chip = getWifiChip(instance_name);
+    return getWifiApIface(wifi_chip);
+}
+
 std::shared_ptr<IWifiApIface> getBridgedWifiApIface(std::shared_ptr<IWifiChip> wifi_chip) {
     if (!wifi_chip.get()) {
         return nullptr;
diff --git a/wifi/aidl/vts/functional/wifi_aidl_test_utils.h b/wifi/aidl/vts/functional/wifi_aidl_test_utils.h
index 1369dd4..9b47a9f 100644
--- a/wifi/aidl/vts/functional/wifi_aidl_test_utils.h
+++ b/wifi/aidl/vts/functional/wifi_aidl_test_utils.h
@@ -40,6 +40,7 @@
 std::shared_ptr<IWifiStaIface> getWifiStaIface(const char* instance_name);
 std::shared_ptr<IWifiNanIface> getWifiNanIface(const char* instance_name);
 std::shared_ptr<IWifiApIface> getWifiApIface(const char* instance_name);
+std::shared_ptr<IWifiApIface> getWifiApIface(std::shared_ptr<IWifiChip> wifi_chip);
 std::shared_ptr<IWifiApIface> getBridgedWifiApIface(const char* instance_name);
 std::shared_ptr<IWifiApIface> getBridgedWifiApIface(std::shared_ptr<IWifiChip> wifi_chip);
 // Configure the chip in a mode to support the creation of the provided iface type.
diff --git a/wifi/aidl/vts/functional/wifi_ap_iface_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_ap_iface_aidl_test.cpp
index d39cfb4..a58fd5b 100644
--- a/wifi/aidl/vts/functional/wifi_ap_iface_aidl_test.cpp
+++ b/wifi/aidl/vts/functional/wifi_ap_iface_aidl_test.cpp
@@ -37,12 +37,21 @@
                 "/system/bin/cmd wifi get-softap-supported-features",
                 "wifi_softap_bridged_ap_supported");
         stopWifiService(getInstanceName());
+
+        wifi_chip_ = getWifiChip(getInstanceName());
+        ASSERT_NE(nullptr, wifi_chip_.get());
+
+        bool isApSupported = doesChipSupportConcurrencyType(wifi_chip_, IfaceConcurrencyType::AP);
+        if (!isApSupported) {
+            GTEST_SKIP() << "AP interfaces are not supported";
+        }
     }
 
     void TearDown() override { stopWifiService(getInstanceName()); }
 
   protected:
     bool isBridgedSupport_ = false;
+    std::shared_ptr<IWifiChip> wifi_chip_;
     const char* getInstanceName() { return GetParam().c_str(); }
 };
 
@@ -50,7 +59,7 @@
  * SetMacAddress
  */
 TEST_P(WifiApIfaceAidlTest, SetMacAddress) {
-    std::shared_ptr<IWifiApIface> wifi_ap_iface = getWifiApIface(getInstanceName());
+    std::shared_ptr<IWifiApIface> wifi_ap_iface = getWifiApIface(wifi_chip_);
     ASSERT_NE(nullptr, wifi_ap_iface.get());
     std::array<uint8_t, 6> mac = {0x12, 0x22, 0x33, 0x52, 0x10, 0x44};
     EXPECT_TRUE(wifi_ap_iface->setMacAddress(mac).isOk());
@@ -60,7 +69,7 @@
  * SetCountryCode
  */
 TEST_P(WifiApIfaceAidlTest, SetCountryCode) {
-    std::shared_ptr<IWifiApIface> wifi_ap_iface = getWifiApIface(getInstanceName());
+    std::shared_ptr<IWifiApIface> wifi_ap_iface = getWifiApIface(wifi_chip_);
     ASSERT_NE(nullptr, wifi_ap_iface.get());
 
     const std::array<uint8_t, 2> country_code = {0x55, 0x53};
@@ -71,7 +80,7 @@
  * GetFactoryMacAddress
  */
 TEST_P(WifiApIfaceAidlTest, GetFactoryMacAddress) {
-    std::shared_ptr<IWifiApIface> wifi_ap_iface = getWifiApIface(getInstanceName());
+    std::shared_ptr<IWifiApIface> wifi_ap_iface = getWifiApIface(wifi_chip_);
     ASSERT_NE(nullptr, wifi_ap_iface.get());
 
     std::array<uint8_t, 6> mac;
@@ -84,7 +93,7 @@
  * GetBridgedInstances - non-bridged mode
  */
 TEST_P(WifiApIfaceAidlTest, GetBridgedInstances) {
-    std::shared_ptr<IWifiApIface> wifi_ap_iface = getWifiApIface(getInstanceName());
+    std::shared_ptr<IWifiApIface> wifi_ap_iface = getWifiApIface(wifi_chip_);
     ASSERT_NE(nullptr, wifi_ap_iface.get());
 
     std::vector<std::string> instances;
@@ -99,7 +108,7 @@
     if (!isBridgedSupport_) {
         GTEST_SKIP() << "Missing Bridged AP support";
     }
-    std::shared_ptr<IWifiApIface> wifi_ap_iface = getBridgedWifiApIface(getInstanceName());
+    std::shared_ptr<IWifiApIface> wifi_ap_iface = getBridgedWifiApIface(wifi_chip_);
     ASSERT_NE(nullptr, wifi_ap_iface.get());
 
     std::vector<std::string> instances;
@@ -111,7 +120,7 @@
  * ResetToFactoryMacAddress - non-bridged mode
  */
 TEST_P(WifiApIfaceAidlTest, ResetToFactoryMacAddress) {
-    std::shared_ptr<IWifiApIface> wifi_ap_iface = getWifiApIface(getInstanceName());
+    std::shared_ptr<IWifiApIface> wifi_ap_iface = getWifiApIface(wifi_chip_);
     ASSERT_NE(nullptr, wifi_ap_iface.get());
     EXPECT_TRUE(wifi_ap_iface->resetToFactoryMacAddress().isOk());
 }
@@ -123,7 +132,7 @@
     if (!isBridgedSupport_) {
         GTEST_SKIP() << "Missing Bridged AP support";
     }
-    std::shared_ptr<IWifiApIface> wifi_ap_iface = getBridgedWifiApIface(getInstanceName());
+    std::shared_ptr<IWifiApIface> wifi_ap_iface = getBridgedWifiApIface(wifi_chip_);
     ASSERT_NE(nullptr, wifi_ap_iface.get());
     EXPECT_TRUE(wifi_ap_iface->resetToFactoryMacAddress().isOk());
 }
diff --git a/wifi/hostapd/aidl/Android.bp b/wifi/hostapd/aidl/Android.bp
index 2e4d4d1..88f4ef2 100644
--- a/wifi/hostapd/aidl/Android.bp
+++ b/wifi/hostapd/aidl/Android.bp
@@ -65,5 +65,5 @@
         },
 
     ],
-    frozen: true,
+    frozen: false,
 }
diff --git a/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/BandMask.aidl b/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/BandMask.aidl
index b1e7f66..fa9f198 100644
--- a/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/BandMask.aidl
+++ b/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/BandMask.aidl
@@ -34,8 +34,8 @@
 package android.hardware.wifi.hostapd;
 @Backing(type="int") @VintfStability
 enum BandMask {
-  BAND_2_GHZ = 1,
-  BAND_5_GHZ = 2,
-  BAND_6_GHZ = 4,
-  BAND_60_GHZ = 8,
+  BAND_2_GHZ = (1 << 0) /* 1 */,
+  BAND_5_GHZ = (1 << 1) /* 2 */,
+  BAND_6_GHZ = (1 << 2) /* 4 */,
+  BAND_60_GHZ = (1 << 3) /* 8 */,
 }
diff --git a/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/EncryptionType.aidl b/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/EncryptionType.aidl
index a7b20fa..840b875 100644
--- a/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/EncryptionType.aidl
+++ b/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/EncryptionType.aidl
@@ -34,11 +34,11 @@
 package android.hardware.wifi.hostapd;
 @Backing(type="int") @VintfStability
 enum EncryptionType {
-  NONE = 0,
-  WPA = 1,
-  WPA2 = 2,
-  WPA3_SAE_TRANSITION = 3,
-  WPA3_SAE = 4,
-  WPA3_OWE_TRANSITION = 5,
-  WPA3_OWE = 6,
+  NONE,
+  WPA,
+  WPA2,
+  WPA3_SAE_TRANSITION,
+  WPA3_SAE,
+  WPA3_OWE_TRANSITION,
+  WPA3_OWE,
 }
diff --git a/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/Generation.aidl b/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/Generation.aidl
index 5bb0d32..a0c1886 100644
--- a/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/Generation.aidl
+++ b/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/Generation.aidl
@@ -34,7 +34,7 @@
 package android.hardware.wifi.hostapd;
 @Backing(type="int") @VintfStability
 enum Generation {
-  WIFI_STANDARD_UNKNOWN = -1,
+  WIFI_STANDARD_UNKNOWN = (-1) /* -1 */,
   WIFI_STANDARD_LEGACY = 0,
   WIFI_STANDARD_11N = 1,
   WIFI_STANDARD_11AC = 2,
diff --git a/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/HostapdStatusCode.aidl b/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/HostapdStatusCode.aidl
index 548e497..7edff15 100644
--- a/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/HostapdStatusCode.aidl
+++ b/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/HostapdStatusCode.aidl
@@ -34,10 +34,10 @@
 package android.hardware.wifi.hostapd;
 @Backing(type="int") @VintfStability
 enum HostapdStatusCode {
-  SUCCESS = 0,
-  FAILURE_UNKNOWN = 1,
-  FAILURE_ARGS_INVALID = 2,
-  FAILURE_IFACE_UNKNOWN = 3,
-  FAILURE_IFACE_EXISTS = 4,
-  FAILURE_CLIENT_UNKNOWN = 5,
+  SUCCESS,
+  FAILURE_UNKNOWN,
+  FAILURE_ARGS_INVALID,
+  FAILURE_IFACE_UNKNOWN,
+  FAILURE_IFACE_EXISTS,
+  FAILURE_CLIENT_UNKNOWN,
 }
diff --git a/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/IfaceParams.aidl b/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/IfaceParams.aidl
index 64367bb..8da3441 100644
--- a/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/IfaceParams.aidl
+++ b/wifi/hostapd/aidl/aidl_api/android.hardware.wifi.hostapd/current/android/hardware/wifi/hostapd/IfaceParams.aidl
@@ -38,4 +38,6 @@
   android.hardware.wifi.hostapd.HwModeParams hwModeParams;
   android.hardware.wifi.hostapd.ChannelParams[] channelParams;
   @nullable android.hardware.wifi.common.OuiKeyedData[] vendorData;
+  @nullable String[] instanceIdentities;
+  boolean isMlo;
 }
diff --git a/wifi/hostapd/aidl/android/hardware/wifi/hostapd/IfaceParams.aidl b/wifi/hostapd/aidl/android/hardware/wifi/hostapd/IfaceParams.aidl
index 3f8ee39..bb646e3 100644
--- a/wifi/hostapd/aidl/android/hardware/wifi/hostapd/IfaceParams.aidl
+++ b/wifi/hostapd/aidl/android/hardware/wifi/hostapd/IfaceParams.aidl
@@ -41,4 +41,12 @@
      * Optional vendor-specific configuration parameters.
      */
     @nullable OuiKeyedData[] vendorData;
+    /**
+     * The list of the instance identities.
+     */
+    @nullable String[] instanceIdentities;
+    /**
+     * Whether the current iface is MLO.
+     */
+    boolean isMlo;
 }
diff --git a/wifi/hostapd/aidl/vts/functional/Android.bp b/wifi/hostapd/aidl/vts/functional/Android.bp
index f614679..bf1b0d0 100644
--- a/wifi/hostapd/aidl/vts/functional/Android.bp
+++ b/wifi/hostapd/aidl/vts/functional/Android.bp
@@ -21,7 +21,7 @@
         "libvndksupport",
     ],
     static_libs: [
-        "android.hardware.wifi.hostapd-V2-ndk",
+        "android.hardware.wifi.hostapd-V3-ndk",
         "VtsHalWifiV1_0TargetTestUtil",
         "VtsHalWifiV1_5TargetTestUtil",
         "VtsHalWifiV1_6TargetTestUtil",
diff --git a/wifi/legacy_headers/include/hardware_legacy/wifi_nan.h b/wifi/legacy_headers/include/hardware_legacy/wifi_nan.h
index 55034d1..4e490d9 100644
--- a/wifi/legacy_headers/include/hardware_legacy/wifi_nan.h
+++ b/wifi/legacy_headers/include/hardware_legacy/wifi_nan.h
@@ -2972,11 +2972,17 @@
     u16 publish_subscribe_id;
 
     /*
-       This Id is the Peer Instance that is passed as
-       part of earlier MatchInd/FollowupInd message.
+      Same as the bootstrapping_instance_id
     */
     u32 service_instance_id;
 
+    /*
+      Unique Instance Id corresponding to a service/session.
+      This is similar to the publish_id generated on the
+      publisher side
+    */
+    u32 bootstrapping_instance_id;
+
     /* Discovery MAC addr of the peer/initiator */
     u8 peer_disc_mac_addr[NAN_MAC_ADDR_LEN];
 
diff --git a/wifi/supplicant/aidl/Android.bp b/wifi/supplicant/aidl/Android.bp
index b7242ed..8d16cb7 100644
--- a/wifi/supplicant/aidl/Android.bp
+++ b/wifi/supplicant/aidl/Android.bp
@@ -71,5 +71,5 @@
         },
 
     ],
-    frozen: true,
+    frozen: false,
 }
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 330f2aa..6bae4cf 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
@@ -41,4 +41,5 @@
   TRUST_ON_FIRST_USE = (1 << 4) /* 16 */,
   SET_TLS_MINIMUM_VERSION = (1 << 5) /* 32 */,
   TLS_V1_3 = (1 << 6) /* 64 */,
+  RSN_OVERRIDING = (1 << 7) /* 128 */,
 }
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/WpaDriverCapabilitiesMask.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/WpaDriverCapabilitiesMask.aidl
index a9434c4..b6e57c6 100644
--- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/WpaDriverCapabilitiesMask.aidl
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/WpaDriverCapabilitiesMask.aidl
@@ -50,4 +50,8 @@
      * TLS V1.3
      */
     TLS_V1_3 = 1 << 6,
+    /**
+     * RSN Overriding
+     */
+    RSN_OVERRIDING = 1 << 7,
 }
diff --git a/wifi/supplicant/aidl/vts/functional/Android.bp b/wifi/supplicant/aidl/vts/functional/Android.bp
index 4166850..4ffec3f 100644
--- a/wifi/supplicant/aidl/vts/functional/Android.bp
+++ b/wifi/supplicant/aidl/vts/functional/Android.bp
@@ -46,7 +46,7 @@
         "android.hardware.wifi.common-V1-ndk",
         "android.hardware.wifi.supplicant@1.0",
         "android.hardware.wifi.supplicant@1.1",
-        "android.hardware.wifi.supplicant-V3-ndk",
+        "android.hardware.wifi.supplicant-V4-ndk",
         "libwifi-system",
         "libwifi-system-iface",
         "VtsHalWifiV1_0TargetTestUtil",
@@ -84,7 +84,7 @@
         "android.hardware.wifi.common-V1-ndk",
         "android.hardware.wifi.supplicant@1.0",
         "android.hardware.wifi.supplicant@1.1",
-        "android.hardware.wifi.supplicant-V3-ndk",
+        "android.hardware.wifi.supplicant-V4-ndk",
         "libwifi-system",
         "libwifi-system-iface",
         "VtsHalWifiV1_0TargetTestUtil",
@@ -122,7 +122,7 @@
         "android.hardware.wifi.common-V1-ndk",
         "android.hardware.wifi.supplicant@1.0",
         "android.hardware.wifi.supplicant@1.1",
-        "android.hardware.wifi.supplicant-V3-ndk",
+        "android.hardware.wifi.supplicant-V4-ndk",
         "libwifi-system",
         "libwifi-system-iface",
         "VtsHalWifiV1_0TargetTestUtil",