Merge "wifi: Fix single AP iface isn't deleted" into tm-dev
diff --git a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
index 09446cd..38d6eff 100644
--- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
+++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
@@ -34,6 +34,7 @@
 
 #include <hwbinder/IPCThreadState.h>
 
+#include <android-base/expected.h>
 #include <android-base/logging.h>
 #include <system/audio_config.h>
 
@@ -130,6 +131,33 @@
     using IDevice = ::android::hardware::audio::CPP_VERSION::IDevice;
     using IDevicesFactory = ::android::hardware::audio::CPP_VERSION::IDevicesFactory;
 
+    static android::base::expected<std::vector<std::string>, std::string> getAllFactoryInstances() {
+        using ::android::hardware::audio::CPP_VERSION::IDevicesFactory;
+        const std::string factoryDescriptor = IDevicesFactory::descriptor;
+        // Make sure that the instance is the exact minor version.
+        // Using a 7.1 factory for 7.0 test is not always possible because
+        // 7.1 can be configured via the XML config to use features that are
+        // absent in 7.0.
+        auto instances = ::android::hardware::getAllHalInstanceNames(factoryDescriptor);
+        if (instances.empty()) return instances;
+        // Use the default instance for checking the implementation version.
+        auto defaultInstance = IDevicesFactory::getService("default");
+        if (defaultInstance == nullptr) {
+            return ::android::base::unexpected("Failed to obtain IDevicesFactory/default");
+        }
+        std::string actualDescriptor;
+        auto intDescRet = defaultInstance->interfaceDescriptor(
+                [&](const auto& descriptor) { actualDescriptor = descriptor; });
+        if (!intDescRet.isOk()) {
+            return ::android::base::unexpected("Failed to obtain interface descriptor: " +
+                                               intDescRet.description());
+        }
+        if (factoryDescriptor == actualDescriptor)
+            return instances;
+        else
+            return {};
+    }
+
     virtual ~HidlTest() = default;
     // public access to avoid annoyances when using this method in template classes
     // derived from test classes
@@ -174,9 +202,11 @@
 }
 
 TEST(CheckConfig, audioPolicyConfigurationValidation) {
-    const auto factories = ::android::hardware::getAllHalInstanceNames(
-            ::android::hardware::audio::CPP_VERSION::IDevicesFactory::descriptor);
-    if (factories.size() == 0) {
+    const auto factories = HidlTest::getAllFactoryInstances();
+    if (!factories.ok()) {
+        FAIL() << factories.error();
+    }
+    if (factories.value().size() == 0) {
         GTEST_SKIP() << "Skipping audioPolicyConfigurationValidation because no factory instances "
                         "are found.";
     }
@@ -205,11 +235,11 @@
 const std::vector<DeviceParameter>& getDeviceParameters() {
     static std::vector<DeviceParameter> parameters = [] {
         std::vector<DeviceParameter> result;
-        const auto factories = ::android::hardware::getAllHalInstanceNames(
-                ::android::hardware::audio::CPP_VERSION::IDevicesFactory::descriptor);
+        const auto factories = HidlTest::getAllFactoryInstances();
+        if (!factories.ok()) return result;
         const auto devices = getCachedPolicyConfig().getModulesWithDevicesNames();
         result.reserve(devices.size());
-        for (const auto& factoryName : factories) {
+        for (const auto& factoryName : factories.value()) {
             for (const auto& deviceName : devices) {
                 if (DeviceManager::getInstance().get(factoryName, deviceName) != nullptr) {
                     result.emplace_back(factoryName, deviceName);
@@ -224,9 +254,9 @@
 const std::vector<DeviceParameter>& getDeviceParametersForFactoryTests() {
     static std::vector<DeviceParameter> parameters = [] {
         std::vector<DeviceParameter> result;
-        const auto factories = ::android::hardware::getAllHalInstanceNames(
-                ::android::hardware::audio::CPP_VERSION::IDevicesFactory::descriptor);
-        for (const auto& factoryName : factories) {
+        const auto factories = HidlTest::getAllFactoryInstances();
+        if (!factories.ok()) return result;
+        for (const auto& factoryName : factories.value()) {
             result.emplace_back(factoryName,
                                 DeviceManager::getInstance().getPrimary(factoryName) != nullptr
                                         ? DeviceManager::kPrimaryDevice
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl
index dc9b876..47fc54b 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl
@@ -90,6 +90,14 @@
      * area ID) are not allowed in a single call. This function must return
      * {@link StatusCode#INVALID_ARG} for duplicate properties.
      *
+     * The {@link VehiclePropValue#timestamp} field in request is ignored. The
+     * {@link VehiclePropValue#timestamp} field in {@link GetValueResult} must
+     * be the system uptime since boot when the value changes for
+     * ON_CHANGE property or when the value is checked according to polling rate
+     * for CONTINUOUS property. Note that for CONTINUOUS property, VHAL client
+     * reading the property multiple times between the polling interval will get
+     * the same timestamp.
+     *
      * @param callback A callback interface, whose 'onGetValues' would be called
      *    after the value is fetched. Caller should use
      *    {@code android-automotive-large-parcelable} library to parse the
@@ -104,7 +112,7 @@
      * Set vehicle property values.
      *
      * The {@link IVehicleCallback#onSetValues} function would be called after
-     * the values set request are sent through vehicle bus or are failed to set.
+     * the values set request are sent through vehicle bus or failed to set.
      * If the bus protocol supports confirmation, the callback would be called
      * after getting the confirmation.
      *
@@ -152,11 +160,36 @@
      * Clients must be able to subscribe to multiple properties at a time
      * depending on data provided in options argument.
      *
-     * For one callback, the is only one subscription for one property.
+     * For one callback, there is only one subscription for one property.
      * A new subscription with a different sample rate would override the old
      * subscription. One property could be subscribed multiple times for
      * different callbacks.
      *
+     * If error is returned, some of the properties failed to subscribe.
+     * Caller is safe to try again, since subscribing to an already subscribed
+     * property is okay.
+     *
+     * The specified sample rate is just a guidance. It is not guaranteed that
+     * the sample rate is achievable depending on how the polling refresh rate
+     * is. The actual property event rate might be higher/lower than the
+     * specified sampleRate, for example, if the polling rate can be 5 times/s
+     * or 10 times/s, subscribing to a sample rate of 7 might use the 5 times/s
+     * polling rate, thus generating 5 events/s. We only require that on
+     * average, the {@code minSampleRate} and {@code maxSampleRate} can be
+     * achieved, all the sampleRate within min and max would on average
+     * generates events with rate >= {@code minSampleRate} and <=
+     * {@code maxSampleRate}.
+     *
+     * The {@link VehiclePropValue#timestamp} field for each property event must
+     * be the system uptime since boot when the value changes for
+     * ON_CHANGE property or when the value is checked according to polling rate
+     * for CONTINUOUS property. Note that for CONTINUOUS property, VHAL client
+     * reading the property multiple times between the polling interval will get
+     * the same timestamp.
+     * For example, if the polling rate for a property is 10 times/s, no matter
+     * what the sampleRate specified in {@code options}, the timestamp for
+     * the timestamp is updated 10 times/s.
+     *
      * @param callback The subscription callbacks.
      *    {@link IVehicleCallback#onPropertyEvent} would be called when a new
      *    property event arrives.
@@ -189,8 +222,13 @@
     /**
      * Unsubscribes from property events.
      *
-     * If 'callback' is not valid or 'propIds' were not subscribed for this
-     * 'callback', this method must return {@link StatusCode#INVALID_ARG}.
+     * If 'callback' is not valid this method must return
+     * {@link StatusCode#INVALID_ARG}. If a specified propId was not subscribed
+     * before, this method must ignore that propId.
+     *
+     * If error is returned, some of the properties failed to unsubscribe.
+     * Caller is safe to try again, since unsubscribing an already unsubscribed
+     * property is okay.
      *
      * @param callback The callback used in the previous subscription.
      * @param propIds The IDs for the properties to unsubscribe.
diff --git a/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h b/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h
index fda0da9..4d96fd3 100644
--- a/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h
+++ b/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h
@@ -292,8 +292,9 @@
                          .prop = toInt(VehicleProperty::EV_CHARGE_CURRENT_DRAW_LIMIT),
                          .access = VehiclePropertyAccess::READ_WRITE,
                          .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+                         .configArray = {/*max current draw allowed by vehicle in amperes=*/20},
                  },
-         .initialValue = {.floatValues = {(float)VehicleUnit::AMPERE}}},
+         .initialValue = {.floatValues = {(float)12.5}}},
 
         {.config =
                  {
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
index e799a28..34b2b24 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
@@ -17,10 +17,12 @@
 #ifndef android_hardware_automotive_vehicle_aidl_impl_fake_impl_hardware_include_FakeVehicleHardware_H_
 #define android_hardware_automotive_vehicle_aidl_impl_fake_impl_hardware_include_FakeVehicleHardware_H_
 
+#include <ConcurrentQueue.h>
 #include <DefaultConfig.h>
 #include <FakeObd2Frame.h>
 #include <FakeUserHal.h>
 #include <IVehicleHardware.h>
+#include <RecurrentTimer.h>
 #include <VehicleHalTypes.h>
 #include <VehiclePropertyStore.h>
 #include <android-base/parseint.h>
@@ -47,6 +49,8 @@
 
     explicit FakeVehicleHardware(std::unique_ptr<VehiclePropValuePool> valuePool);
 
+    ~FakeVehicleHardware();
+
     // Get all the property configs.
     std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
     getAllPropertyConfigs() const override;
@@ -82,6 +86,10 @@
     void registerOnPropertySetErrorEvent(
             std::unique_ptr<const PropertySetErrorCallback> callback) override;
 
+    // Update the sample rate for the [propId, areaId] pair.
+    aidl::android::hardware::automotive::vehicle::StatusCode updateSampleRate(
+            int32_t propId, int32_t areaId, float sampleRate) override;
+
   protected:
     // mValuePool is also used in mServerSidePropStore.
     const std::shared_ptr<VehiclePropValuePool> mValuePool;
@@ -97,13 +105,45 @@
     // Expose private methods to unit test.
     friend class FakeVehicleHardwareTestHelper;
 
+    template <class CallbackType, class RequestType>
+    struct RequestWithCallback {
+        RequestType request;
+        std::shared_ptr<const CallbackType> callback;
+    };
+
+    template <class CallbackType, class RequestType>
+    class PendingRequestHandler {
+      public:
+        PendingRequestHandler(FakeVehicleHardware* hardware);
+
+        void addRequest(RequestType request, std::shared_ptr<const CallbackType> callback);
+
+        void stop();
+
+      private:
+        FakeVehicleHardware* mHardware;
+        std::thread mThread;
+        ConcurrentQueue<RequestWithCallback<CallbackType, RequestType>> mRequests;
+
+        void handleRequestsOnce();
+    };
+
     const std::unique_ptr<obd2frame::FakeObd2Frame> mFakeObd2Frame;
     const std::unique_ptr<FakeUserHal> mFakeUserHal;
-    std::mutex mCallbackLock;
-    std::unique_ptr<const PropertyChangeCallback> mOnPropertyChangeCallback
-            GUARDED_BY(mCallbackLock);
-    std::unique_ptr<const PropertySetErrorCallback> mOnPropertySetErrorCallback
-            GUARDED_BY(mCallbackLock);
+    // RecurrentTimer is thread-safe.
+    std::unique_ptr<RecurrentTimer> mRecurrentTimer;
+    std::mutex mLock;
+    std::unique_ptr<const PropertyChangeCallback> mOnPropertyChangeCallback GUARDED_BY(mLock);
+    std::unique_ptr<const PropertySetErrorCallback> mOnPropertySetErrorCallback GUARDED_BY(mLock);
+    std::unordered_map<PropIdAreaId, std::shared_ptr<RecurrentTimer::Callback>, PropIdAreaIdHash>
+            mRecurrentActions GUARDED_BY(mLock);
+    // PendingRequestHandler is thread-safe.
+    mutable PendingRequestHandler<GetValuesCallback,
+                                  aidl::android::hardware::automotive::vehicle::GetValueRequest>
+            mPendingGetValueRequests;
+    mutable PendingRequestHandler<SetValuesCallback,
+                                  aidl::android::hardware::automotive::vehicle::SetValueRequest>
+            mPendingSetValueRequests;
 
     void init();
     // Stores the initial value to property store.
@@ -163,6 +203,10 @@
 
     android::base::Result<void> checkArgumentsSize(const std::vector<std::string>& options,
                                                    size_t minSize);
+    aidl::android::hardware::automotive::vehicle::GetValueResult handleGetValueRequest(
+            const aidl::android::hardware::automotive::vehicle::GetValueRequest& request);
+    aidl::android::hardware::automotive::vehicle::SetValueResult handleSetValueRequest(
+            const aidl::android::hardware::automotive::vehicle::SetValueRequest& request);
 };
 
 }  // namespace fake
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
index f8b64f2..7b3de58 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -66,6 +66,7 @@
 using ::android::base::Error;
 using ::android::base::ParseFloat;
 using ::android::base::Result;
+using ::android::base::ScopedLockAssertion;
 using ::android::base::StartsWith;
 using ::android::base::StringPrintf;
 
@@ -131,21 +132,24 @@
 }
 
 FakeVehicleHardware::FakeVehicleHardware()
-    : mValuePool(new VehiclePropValuePool),
-      mServerSidePropStore(new VehiclePropertyStore(mValuePool)),
-      mFakeObd2Frame(new obd2frame::FakeObd2Frame(mServerSidePropStore)),
-      mFakeUserHal(new FakeUserHal(mValuePool)) {
-    init();
-}
+    : FakeVehicleHardware(std::make_unique<VehiclePropValuePool>()) {}
 
 FakeVehicleHardware::FakeVehicleHardware(std::unique_ptr<VehiclePropValuePool> valuePool)
     : mValuePool(std::move(valuePool)),
       mServerSidePropStore(new VehiclePropertyStore(mValuePool)),
       mFakeObd2Frame(new obd2frame::FakeObd2Frame(mServerSidePropStore)),
-      mFakeUserHal(new FakeUserHal(mValuePool)) {
+      mFakeUserHal(new FakeUserHal(mValuePool)),
+      mRecurrentTimer(new RecurrentTimer()),
+      mPendingGetValueRequests(this),
+      mPendingSetValueRequests(this) {
     init();
 }
 
+FakeVehicleHardware::~FakeVehicleHardware() {
+    mPendingGetValueRequests.stop();
+    mPendingSetValueRequests.stop();
+}
+
 void FakeVehicleHardware::init() {
     for (auto& it : defaultconfig::getDefaultConfigs()) {
         VehiclePropConfig cfg = it.config;
@@ -430,37 +434,25 @@
 
 StatusCode FakeVehicleHardware::setValues(std::shared_ptr<const SetValuesCallback> callback,
                                           const std::vector<SetValueRequest>& requests) {
-    std::vector<SetValueResult> results;
     for (auto& request : requests) {
-        const VehiclePropValue& value = request.value;
-        int propId = value.prop;
-
         if (FAKE_VEHICLEHARDWARE_DEBUG) {
-            ALOGD("Set value for property ID: %d", propId);
+            ALOGD("Set value for property ID: %d", request.value.prop);
         }
 
-        SetValueResult setValueResult;
-        setValueResult.requestId = request.requestId;
-
-        if (auto result = setValue(value); !result.ok()) {
-            ALOGE("failed to set value, error: %s, code: %d", getErrorMsg(result).c_str(),
-                  getIntErrorCode(result));
-            setValueResult.status = getErrorCode(result);
-        } else {
-            setValueResult.status = StatusCode::OK;
-        }
-
-        results.push_back(std::move(setValueResult));
+        // In a real VHAL implementation, you could either send the setValue request to vehicle bus
+        // here in the binder thread, or you could send the request in setValue which runs in
+        // the handler thread. If you decide to send the setValue request here, you should not
+        // wait for the response here and the handler thread should handle the setValue response.
+        mPendingSetValueRequests.addRequest(request, callback);
     }
 
-    // In the real vhal, the values will be sent to Car ECU. We just pretend it is done here and
-    // send back the updated property values to client.
-    (*callback)(std::move(results));
-
     return StatusCode::OK;
 }
 
 VhalResult<void> FakeVehicleHardware::setValue(const VehiclePropValue& value) {
+    // In a real VHAL implementation, this will send the request to vehicle bus if not already
+    // sent in setValues, and wait for the response from vehicle bus.
+    // Here we are just updating mValuePool.
     bool isSpecialValue = false;
     auto setSpecialValueResult = maybeSetSpecialValue(value, &isSpecialValue);
 
@@ -487,41 +479,59 @@
     return {};
 }
 
-StatusCode FakeVehicleHardware::getValues(std::shared_ptr<const GetValuesCallback> callback,
-                                          const std::vector<GetValueRequest>& requests) const {
-    std::vector<GetValueResult> results;
-    for (auto& request : requests) {
-        const VehiclePropValue& value = request.prop;
+SetValueResult FakeVehicleHardware::handleSetValueRequest(const SetValueRequest& request) {
+    SetValueResult setValueResult;
+    setValueResult.requestId = request.requestId;
 
-        if (FAKE_VEHICLEHARDWARE_DEBUG) {
-            ALOGD("getValues(%d)", value.prop);
-        }
-
-        GetValueResult getValueResult;
-        getValueResult.requestId = request.requestId;
-
-        auto result = getValue(value);
-        if (!result.ok()) {
-            ALOGE("failed to get value, error: %s, code: %d", getErrorMsg(result).c_str(),
-                  getIntErrorCode(result));
-            getValueResult.status = getErrorCode(result);
-        } else {
-            getValueResult.status = StatusCode::OK;
-            getValueResult.prop = *result.value();
-        }
-        results.push_back(std::move(getValueResult));
+    if (auto result = setValue(request.value); !result.ok()) {
+        ALOGE("failed to set value, error: %s, code: %d", getErrorMsg(result).c_str(),
+              getIntErrorCode(result));
+        setValueResult.status = getErrorCode(result);
+    } else {
+        setValueResult.status = StatusCode::OK;
     }
 
-    // In a real VHAL implementation, getValue would be async and we would call the callback after
-    // we actually received the values from vehicle bus. Here we are getting the result
-    // synchronously so we could call the callback here.
-    (*callback)(std::move(results));
+    return setValueResult;
+}
+
+StatusCode FakeVehicleHardware::getValues(std::shared_ptr<const GetValuesCallback> callback,
+                                          const std::vector<GetValueRequest>& requests) const {
+    for (auto& request : requests) {
+        if (FAKE_VEHICLEHARDWARE_DEBUG) {
+            ALOGD("getValues(%d)", request.prop.prop);
+        }
+
+        // In a real VHAL implementation, you could either send the getValue request to vehicle bus
+        // here in the binder thread, or you could send the request in getValue which runs in
+        // the handler thread. If you decide to send the getValue request here, you should not
+        // wait for the response here and the handler thread should handle the getValue response.
+        mPendingGetValueRequests.addRequest(request, callback);
+    }
 
     return StatusCode::OK;
 }
 
+GetValueResult FakeVehicleHardware::handleGetValueRequest(const GetValueRequest& request) {
+    GetValueResult getValueResult;
+    getValueResult.requestId = request.requestId;
+
+    auto result = getValue(request.prop);
+    if (!result.ok()) {
+        ALOGE("failed to get value, error: %s, code: %d", getErrorMsg(result).c_str(),
+              getIntErrorCode(result));
+        getValueResult.status = getErrorCode(result);
+    } else {
+        getValueResult.status = StatusCode::OK;
+        getValueResult.prop = *result.value();
+    }
+    return getValueResult;
+}
+
 FakeVehicleHardware::ValueResultType FakeVehicleHardware::getValue(
         const VehiclePropValue& value) const {
+    // In a real VHAL implementation, this will send the request to vehicle bus if not already
+    // sent in getValues, and wait for the response from vehicle bus.
+    // Here we are just reading value from mValuePool.
     bool isSpecialValue = false;
     auto result = maybeGetSpecialValue(value, &isSpecialValue);
     if (isSpecialValue) {
@@ -567,6 +577,12 @@
         result.buffer = dumpSpecificProperty(options);
     } else if (EqualsIgnoreCase(option, "--set")) {
         result.buffer = dumpSetProperties(options);
+    } else if (EqualsIgnoreCase(option, kUserHalDumpOption)) {
+        if (options.size() == 1) {
+            result.buffer = mFakeUserHal->showDumpHelp();
+        } else {
+            result.buffer = mFakeUserHal->dump(options[1]);
+        }
     } else {
         result.buffer = StringPrintf("Invalid option: %s\n", option.c_str());
     }
@@ -584,7 +600,9 @@
            "[-b BYTES_VALUE] [-a AREA_ID] : sets the value of property PROP. "
            "Notice that the string, bytes and area value can be set just once, while the other can"
            " have multiple values (so they're used in the respective array), "
-           "BYTES_VALUE is in the form of 0xXXXX, e.g. 0xdeadbeef.\n";
+           "BYTES_VALUE is in the form of 0xXXXX, e.g. 0xdeadbeef.\n\n"
+           "Fake user HAL usage: \n" +
+           mFakeUserHal->showDumpHelp();
 }
 
 std::string FakeVehicleHardware::dumpAllProperties() {
@@ -837,18 +855,57 @@
 
 void FakeVehicleHardware::registerOnPropertyChangeEvent(
         std::unique_ptr<const PropertyChangeCallback> callback) {
-    std::scoped_lock<std::mutex> lockGuard(mCallbackLock);
+    std::scoped_lock<std::mutex> lockGuard(mLock);
     mOnPropertyChangeCallback = std::move(callback);
 }
 
 void FakeVehicleHardware::registerOnPropertySetErrorEvent(
         std::unique_ptr<const PropertySetErrorCallback> callback) {
-    std::scoped_lock<std::mutex> lockGuard(mCallbackLock);
+    std::scoped_lock<std::mutex> lockGuard(mLock);
     mOnPropertySetErrorCallback = std::move(callback);
 }
 
+StatusCode FakeVehicleHardware::updateSampleRate(int32_t propId, int32_t areaId, float sampleRate) {
+    // DefaultVehicleHal makes sure that sampleRate must be within minSampleRate and maxSampleRate.
+    // For fake implementation, we would write the same value with a new timestamp into propStore
+    // at sample rate.
+    std::scoped_lock<std::mutex> lockGuard(mLock);
+
+    PropIdAreaId propIdAreaId{
+            .propId = propId,
+            .areaId = areaId,
+    };
+    if (mRecurrentActions.find(propIdAreaId) != mRecurrentActions.end()) {
+        mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propIdAreaId]);
+    }
+    if (sampleRate == 0) {
+        return StatusCode::OK;
+    }
+    int64_t interval = static_cast<int64_t>(1'000'000'000. / sampleRate);
+    auto action = std::make_shared<RecurrentTimer::Callback>([this, propId, areaId] {
+        // Refresh the property value. In real implementation, this should poll the latest value
+        // from vehicle bus. Here, we are just refreshing the existing value with a new timestamp.
+        auto result = getValue(VehiclePropValue{
+                .prop = propId,
+                .areaId = areaId,
+        });
+        if (!result.ok()) {
+            // Failed to read current value, skip refreshing.
+            return;
+        }
+        result.value()->timestamp = elapsedRealtimeNano();
+        // Must remove the value before writing, otherwise, we would generate no update event since
+        // the value is the same.
+        mServerSidePropStore->removeValue(*result.value());
+        mServerSidePropStore->writeValue(std::move(result.value()));
+    });
+    mRecurrentTimer->registerTimerCallback(interval, action);
+    mRecurrentActions[propIdAreaId] = action;
+    return StatusCode::OK;
+}
+
 void FakeVehicleHardware::onValueChangeCallback(const VehiclePropValue& value) {
-    std::scoped_lock<std::mutex> lockGuard(mCallbackLock);
+    std::scoped_lock<std::mutex> lockGuard(mLock);
 
     if (mOnPropertyChangeCallback == nullptr) {
         return;
@@ -935,6 +992,60 @@
     return bytes;
 }
 
+template <class CallbackType, class RequestType>
+FakeVehicleHardware::PendingRequestHandler<CallbackType, RequestType>::PendingRequestHandler(
+        FakeVehicleHardware* hardware)
+    : mHardware(hardware), mThread([this] {
+          while (mRequests.waitForItems()) {
+              handleRequestsOnce();
+          }
+      }) {}
+
+template <class CallbackType, class RequestType>
+void FakeVehicleHardware::PendingRequestHandler<CallbackType, RequestType>::addRequest(
+        RequestType request, std::shared_ptr<const CallbackType> callback) {
+    mRequests.push({
+            request,
+            callback,
+    });
+}
+
+template <class CallbackType, class RequestType>
+void FakeVehicleHardware::PendingRequestHandler<CallbackType, RequestType>::stop() {
+    mRequests.deactivate();
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+}
+
+template <>
+void FakeVehicleHardware::PendingRequestHandler<FakeVehicleHardware::GetValuesCallback,
+                                                GetValueRequest>::handleRequestsOnce() {
+    std::unordered_map<std::shared_ptr<const GetValuesCallback>, std::vector<GetValueResult>>
+            callbackToResults;
+    for (const auto& rwc : mRequests.flush()) {
+        auto result = mHardware->handleGetValueRequest(rwc.request);
+        callbackToResults[rwc.callback].push_back(std::move(result));
+    }
+    for (const auto& [callback, results] : callbackToResults) {
+        (*callback)(std::move(results));
+    }
+}
+
+template <>
+void FakeVehicleHardware::PendingRequestHandler<FakeVehicleHardware::SetValuesCallback,
+                                                SetValueRequest>::handleRequestsOnce() {
+    std::unordered_map<std::shared_ptr<const SetValuesCallback>, std::vector<SetValueResult>>
+            callbackToResults;
+    for (const auto& rwc : mRequests.flush()) {
+        auto result = mHardware->handleSetValueRequest(rwc.request);
+        callbackToResults[rwc.callback].push_back(std::move(result));
+    }
+    for (const auto& [callback, results] : callbackToResults) {
+        (*callback)(std::move(results));
+    }
+}
+
 }  // namespace fake
 }  // namespace vehicle
 }  // namespace automotive
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
index 7a7fb37..3e8f634 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
@@ -25,12 +25,17 @@
 #include <android-base/expected.h>
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <utils/Log.h>
 #include <utils/SystemClock.h>
 
 #include <inttypes.h>
+#include <chrono>
+#include <condition_variable>
+#include <unordered_map>
+#include <unordered_set>
 #include <vector>
 
 namespace android {
@@ -53,6 +58,7 @@
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
 using ::android::base::expected;
+using ::android::base::ScopedLockAssertion;
 using ::android::base::StringPrintf;
 using ::android::base::unexpected;
 using ::testing::ContainerEq;
@@ -60,6 +66,8 @@
 using ::testing::Eq;
 using ::testing::WhenSortedBy;
 
+using std::chrono::milliseconds;
+
 constexpr int INVALID_PROP_ID = 0;
 constexpr char CAR_MAKE[] = "Default Car";
 
@@ -93,11 +101,51 @@
     FakeVehicleHardware* getHardware() { return &mHardware; }
 
     StatusCode setValues(const std::vector<SetValueRequest>& requests) {
-        return getHardware()->setValues(mSetValuesCallback, requests);
+        {
+            std::scoped_lock<std::mutex> lockGuard(mLock);
+            for (const auto& request : requests) {
+                mPendingSetValueRequests.insert(request.requestId);
+            }
+        }
+        if (StatusCode status = getHardware()->setValues(mSetValuesCallback, requests);
+            status != StatusCode::OK) {
+            return status;
+        }
+        std::unique_lock<std::mutex> lk(mLock);
+        // Wait for the onSetValueResults.
+        bool result = mCv.wait_for(lk, milliseconds(1000), [this] {
+            ScopedLockAssertion lockAssertion(mLock);
+            return mPendingSetValueRequests.size() == 0;
+        });
+        if (!result) {
+            ALOGE("wait for callbacks for setValues timed-out");
+            return StatusCode::INTERNAL_ERROR;
+        }
+        return StatusCode::OK;
     }
 
     StatusCode getValues(const std::vector<GetValueRequest>& requests) {
-        return getHardware()->getValues(mGetValuesCallback, requests);
+        {
+            std::scoped_lock<std::mutex> lockGuard(mLock);
+            for (const auto& request : requests) {
+                mPendingGetValueRequests.insert(request.requestId);
+            }
+        }
+        if (StatusCode status = getHardware()->getValues(mGetValuesCallback, requests);
+            status != StatusCode::OK) {
+            return status;
+        }
+        std::unique_lock<std::mutex> lk(mLock);
+        // Wait for the onGetValueResults.
+        bool result = mCv.wait_for(lk, milliseconds(1000), [this] {
+            ScopedLockAssertion lockAssertion(mLock);
+            return mPendingGetValueRequests.size() == 0;
+        });
+        if (!result) {
+            ALOGE("wait for callbacks for getValues timed-out");
+            return StatusCode::INTERNAL_ERROR;
+        }
+        return StatusCode::OK;
     }
 
     StatusCode setValue(const VehiclePropValue& value) {
@@ -158,30 +206,69 @@
     }
 
     void onSetValues(std::vector<SetValueResult> results) {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
         for (auto& result : results) {
             mSetValueResults.push_back(result);
+            mPendingSetValueRequests.erase(result.requestId);
         }
+        mCv.notify_all();
     }
 
-    const std::vector<SetValueResult>& getSetValueResults() { return mSetValueResults; }
+    const std::vector<SetValueResult>& getSetValueResults() {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
+        return mSetValueResults;
+    }
 
     void onGetValues(std::vector<GetValueResult> results) {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
         for (auto& result : results) {
             mGetValueResults.push_back(result);
+            mPendingGetValueRequests.erase(result.requestId);
         }
+        mCv.notify_all();
     }
 
-    const std::vector<GetValueResult>& getGetValueResults() { return mGetValueResults; }
+    const std::vector<GetValueResult>& getGetValueResults() {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
+        return mGetValueResults;
+    }
 
     void onPropertyChangeEvent(std::vector<VehiclePropValue> values) {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
         for (auto& value : values) {
             mChangedProperties.push_back(value);
+            PropIdAreaId propIdAreaId{
+                    .propId = value.prop,
+                    .areaId = value.areaId,
+            };
+            mEventCount[propIdAreaId]++;
         }
+        mCv.notify_all();
     }
 
-    const std::vector<VehiclePropValue>& getChangedProperties() { return mChangedProperties; }
+    const std::vector<VehiclePropValue>& getChangedProperties() {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
+        return mChangedProperties;
+    }
 
-    void clearChangedProperties() { mChangedProperties.clear(); }
+    bool waitForChangedProperties(int32_t propId, int32_t areaId, size_t count,
+                                  milliseconds timeout) {
+        PropIdAreaId propIdAreaId{
+                .propId = propId,
+                .areaId = areaId,
+        };
+        std::unique_lock<std::mutex> lk(mLock);
+        return mCv.wait_for(lk, timeout, [this, propIdAreaId, count] {
+            ScopedLockAssertion lockAssertion(mLock);
+            return mEventCount[propIdAreaId] >= count;
+        });
+    }
+
+    void clearChangedProperties() {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
+        mEventCount.clear();
+        mChangedProperties.clear();
+    }
 
     static void addSetValueRequest(std::vector<SetValueRequest>& requests,
                                    std::vector<SetValueResult>& expectedResults, int64_t requestId,
@@ -246,11 +333,16 @@
 
   private:
     FakeVehicleHardware mHardware;
-    std::vector<SetValueResult> mSetValueResults;
-    std::vector<GetValueResult> mGetValueResults;
-    std::vector<VehiclePropValue> mChangedProperties;
     std::shared_ptr<IVehicleHardware::SetValuesCallback> mSetValuesCallback;
     std::shared_ptr<IVehicleHardware::GetValuesCallback> mGetValuesCallback;
+    std::condition_variable mCv;
+    std::mutex mLock;
+    std::unordered_map<PropIdAreaId, size_t, PropIdAreaIdHash> mEventCount GUARDED_BY(mLock);
+    std::vector<SetValueResult> mSetValueResults GUARDED_BY(mLock);
+    std::vector<GetValueResult> mGetValueResults GUARDED_BY(mLock);
+    std::vector<VehiclePropValue> mChangedProperties GUARDED_BY(mLock);
+    std::unordered_set<int64_t> mPendingSetValueRequests GUARDED_BY(mLock);
+    std::unordered_set<int64_t> mPendingGetValueRequests GUARDED_BY(mLock);
 };
 
 TEST_F(FakeVehicleHardwareTest, testGetAllPropertyConfigs) {
@@ -1313,6 +1405,28 @@
     ASSERT_THAT(result.buffer, ContainsRegex("Invalid option: --invalid"));
 }
 
+TEST_F(FakeVehicleHardwareTest, testDumpFakeUserHalHelp) {
+    std::vector<std::string> options;
+    options.push_back("--user-hal");
+
+    DumpResult result = getHardware()->dump(options);
+    ASSERT_FALSE(result.callerShouldDumpState);
+    ASSERT_NE(result.buffer, "");
+    ASSERT_THAT(result.buffer, ContainsRegex("dumps state used for user management"));
+}
+
+TEST_F(FakeVehicleHardwareTest, testDumpFakeUserHal) {
+    std::vector<std::string> options;
+    options.push_back("--user-hal");
+    // Indent: " ".
+    options.push_back(" ");
+
+    DumpResult result = getHardware()->dump(options);
+    ASSERT_FALSE(result.callerShouldDumpState);
+    ASSERT_NE(result.buffer, "");
+    ASSERT_THAT(result.buffer, ContainsRegex(" No InitialUserInfo response\n"));
+}
+
 struct SetPropTestCase {
     std::string test_name;
     std::vector<std::string> options;
@@ -1510,6 +1624,35 @@
     ASSERT_EQ(result.value().value.byteValues, std::vector<uint8_t>({0x04, 0x03, 0x02, 0x01}));
 }
 
+TEST_F(FakeVehicleHardwareTest, testUpdateSampleRate) {
+    int32_t propSpeed = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
+    int32_t propSteering = toInt(VehicleProperty::PERF_STEERING_ANGLE);
+    int32_t areaId = 0;
+    getHardware()->updateSampleRate(propSpeed, areaId, 5);
+
+    ASSERT_TRUE(waitForChangedProperties(propSpeed, areaId, /*count=*/5, milliseconds(1500)))
+            << "not enough events generated for speed";
+
+    getHardware()->updateSampleRate(propSteering, areaId, 10);
+
+    ASSERT_TRUE(waitForChangedProperties(propSteering, areaId, /*count=*/10, milliseconds(1500)))
+            << "not enough events generated for steering";
+
+    int64_t timestamp = elapsedRealtimeNano();
+    // Disable refreshing for propSpeed.
+    getHardware()->updateSampleRate(propSpeed, areaId, 0);
+    clearChangedProperties();
+
+    ASSERT_TRUE(waitForChangedProperties(propSteering, areaId, /*count=*/5, milliseconds(1500)))
+            << "should still receive steering events after disable polling for speed";
+    auto updatedValues = getChangedProperties();
+    for (auto& value : updatedValues) {
+        ASSERT_GE(value.timestamp, timestamp);
+        ASSERT_EQ(value.prop, propSteering);
+        ASSERT_EQ(value.areaId, areaId);
+    }
+}
+
 }  // namespace fake
 }  // namespace vehicle
 }  // namespace automotive
diff --git a/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h b/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h
index 4a38827..759db41 100644
--- a/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h
@@ -80,6 +80,35 @@
             const std::vector<aidl::android::hardware::automotive::vehicle::GetValueRequest>&
                     requests) const = 0;
 
+    // Update the sampling rate for the specified property and the specified areaId (0 for global
+    // property) if server supports it. The property must be a continuous property.
+    // {@code sampleRate} means that for this specific property, the server must generate at least
+    // this many OnPropertyChange events per seconds.
+    // A sampleRate of 0 means the property is no longer subscribed and server does not need to
+    // generate any onPropertyEvent for this property.
+    // This would be called if sample rate is updated for a subscriber, a new subscriber is added
+    // or an existing subscriber is removed. For example:
+    // 1. We have no subscriber for speed.
+    // 2. A new subscriber is subscribing speed for 10 times/s, updsateSampleRate would be called
+    //    with sampleRate as 10. The impl is now polling vehicle speed from bus 10 times/s.
+    // 3. A new subscriber is subscribing speed for 5 times/s, because it is less than 10
+    //    times/sec, updateSampleRate would not be called.
+    // 4. The initial subscriber is removed, updateSampleRate would be called with sampleRate as
+    //    5, because now it only needs to report event 5times/sec. The impl can now poll vehicle
+    //    speed 5 times/s. If the impl is still polling at 10 times/s, that is okay as long as
+    //    the polling rate is larger than 5times/s. DefaultVehicleHal would ignore the additional
+    //    events.
+    // 5. The second subscriber is removed, updateSampleRate would be called with sampleRate as 0.
+    //    The impl can optionally disable the polling for vehicle speed.
+    //
+    // 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;
+    }
+
     // Dump debug information in the server.
     virtual DumpResult dump(const std::vector<std::string>& options) = 0;
 
diff --git a/automotive/vehicle/aidl/impl/vhal/include/RecurrentTimer.h b/automotive/vehicle/aidl/impl/utils/common/include/RecurrentTimer.h
similarity index 100%
rename from automotive/vehicle/aidl/impl/vhal/include/RecurrentTimer.h
rename to automotive/vehicle/aidl/impl/utils/common/include/RecurrentTimer.h
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h b/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
index 6d7d131..c94bad6 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/VehicleUtils.h
@@ -21,6 +21,7 @@
 
 #include <android-base/format.h>
 #include <android-base/result.h>
+#include <math/HashCombine.h>
 #include <utils/Log.h>
 
 namespace android {
@@ -310,6 +311,24 @@
     return toScopedAStatus(result, getErrorCode(result), additionalErrorMsg);
 }
 
+struct PropIdAreaId {
+    int32_t propId;
+    int32_t areaId;
+
+    inline bool operator==(const PropIdAreaId& other) const {
+        return areaId == other.areaId && propId == other.propId;
+    }
+};
+
+struct PropIdAreaIdHash {
+    inline size_t operator()(const PropIdAreaId& propIdAreaId) const {
+        size_t res = 0;
+        hashCombine(res, propIdAreaId.propId);
+        hashCombine(res, propIdAreaId.areaId);
+        return res;
+    }
+};
+
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/vhal/src/RecurrentTimer.cpp b/automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp
similarity index 100%
rename from automotive/vehicle/aidl/impl/vhal/src/RecurrentTimer.cpp
rename to automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp
diff --git a/automotive/vehicle/aidl/impl/vhal/test/RecurrentTimerTest.cpp b/automotive/vehicle/aidl/impl/utils/common/test/RecurrentTimerTest.cpp
similarity index 100%
rename from automotive/vehicle/aidl/impl/vhal/test/RecurrentTimerTest.cpp
rename to automotive/vehicle/aidl/impl/utils/common/test/RecurrentTimerTest.cpp
diff --git a/automotive/vehicle/aidl/impl/vhal/Android.bp b/automotive/vehicle/aidl/impl/vhal/Android.bp
index 49f48f7..5abcaf6 100644
--- a/automotive/vehicle/aidl/impl/vhal/Android.bp
+++ b/automotive/vehicle/aidl/impl/vhal/Android.bp
@@ -54,7 +54,6 @@
     srcs: [
         "src/ConnectedClient.cpp",
         "src/DefaultVehicleHal.cpp",
-        "src/RecurrentTimer.cpp",
         "src/SubscriptionManager.cpp",
     ],
     static_libs: [
diff --git a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
index f646b6b..9c29816 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
@@ -17,10 +17,11 @@
 #ifndef android_hardware_automotive_vehicle_aidl_impl_vhal_include_DefaultVehicleHal_H_
 #define android_hardware_automotive_vehicle_aidl_impl_vhal_include_DefaultVehicleHal_H_
 
-#include "ConnectedClient.h"
-#include "ParcelableUtils.h"
-#include "PendingRequestPool.h"
-#include "SubscriptionManager.h"
+#include <ConnectedClient.h>
+#include <ParcelableUtils.h>
+#include <PendingRequestPool.h>
+#include <RecurrentTimer.h>
+#include <SubscriptionManager.h>
 
 #include <ConcurrentQueue.h>
 #include <IVehicleHardware.h>
@@ -163,7 +164,7 @@
     static constexpr int64_t TIMEOUT_IN_NANO = 30'000'000'000;
     // heart beat event interval: 3s
     static constexpr int64_t HEART_BEAT_INTERVAL_IN_NANO = 3'000'000'000;
-    const std::shared_ptr<IVehicleHardware> mVehicleHardware;
+    std::unique_ptr<IVehicleHardware> mVehicleHardware;
 
     // mConfigsByPropId and mConfigFile are only modified during initialization, so no need to
     // lock guard them.
@@ -188,6 +189,8 @@
     // mBinderImpl is only going to be changed in test.
     std::unique_ptr<IBinder> mBinderImpl;
 
+    // Only initialized once.
+    std::shared_ptr<std::function<void()>> mRecurrentAction;
     // RecurrentTimer is thread-safe.
     RecurrentTimer mRecurrentTimer;
 
@@ -243,18 +246,12 @@
             std::unordered_map<const AIBinder*, std::shared_ptr<T>>* clients,
             const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool);
 
-    static void getValueFromHardwareCallCallback(
-            std::weak_ptr<IVehicleHardware> vehicleHardware,
-            std::shared_ptr<SubscribeIdByClient> subscribeIdByClient,
-            std::shared_ptr<SubscriptionClients> subscriptionClients, const CallbackType& callback,
-            const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value);
-
     static void onPropertyChangeEvent(
             std::weak_ptr<SubscriptionManager> subscriptionManager,
             const std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue>&
                     updatedValues);
 
-    static void checkHealth(std::weak_ptr<IVehicleHardware> hardware,
+    static void checkHealth(IVehicleHardware* hardware,
                             std::weak_ptr<SubscriptionManager> subscriptionManager);
 
     static void onBinderDied(void* cookie);
diff --git a/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h b/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
index b0d6701..7c8f1b4 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
@@ -17,15 +17,16 @@
 #ifndef android_hardware_automotive_vehicle_aidl_impl_vhal_include_SubscriptionManager_H_
 #define android_hardware_automotive_vehicle_aidl_impl_vhal_include_SubscriptionManager_H_
 
-#include "RecurrentTimer.h"
-
+#include <IVehicleHardware.h>
 #include <VehicleHalTypes.h>
+#include <VehicleUtils.h>
 
 #include <aidl/android/hardware/automotive/vehicle/IVehicleCallback.h>
 #include <android-base/result.h>
 #include <android-base/thread_annotations.h>
 
 #include <mutex>
+#include <optional>
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
@@ -35,43 +36,58 @@
 namespace automotive {
 namespace vehicle {
 
+// A class to represent all the subscription configs for a continuous [propId, areaId].
+class ContSubConfigs final {
+  public:
+    using ClientIdType = const AIBinder*;
+
+    void addClient(const ClientIdType& clientId, float sampleRate);
+    void removeClient(const ClientIdType& clientId);
+    float getMaxSampleRate();
+
+  private:
+    float mMaxSampleRate = 0.;
+    std::unordered_map<ClientIdType, float> mSampleRates;
+
+    void refreshMaxSampleRate();
+};
+
 // A thread-safe subscription manager that manages all VHAL subscriptions.
 class SubscriptionManager final {
   public:
     using ClientIdType = const AIBinder*;
     using CallbackType =
             std::shared_ptr<aidl::android::hardware::automotive::vehicle::IVehicleCallback>;
-    using GetValueFunc = std::function<void(
-            const CallbackType& callback,
-            const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value)>;
 
-    explicit SubscriptionManager(GetValueFunc&& action);
+    explicit SubscriptionManager(IVehicleHardware* hardware);
     ~SubscriptionManager();
 
     // Subscribes to properties according to {@code SubscribeOptions}. Note that all option must
     // contain non-empty areaIds field, which contains all area IDs to subscribe. As a result,
     // the options here is different from the options passed from VHAL client.
-    // Returns error if any of the subscribe options is not valid. If error is returned, no
-    // properties would be subscribed.
+    // Returns error if any of the subscribe options is not valid or one of the properties failed
+    // to subscribe. Part of the properties maybe be subscribed successfully if this function
+    // returns error. Caller is safe to retry since subscribing to an already subscribed property
+    // is okay.
     // Returns ok if all the options are parsed correctly and all the properties are subscribed.
-    android::base::Result<void> subscribe(
+    VhalResult<void> subscribe(
             const CallbackType& callback,
             const std::vector<aidl::android::hardware::automotive::vehicle::SubscribeOptions>&
                     options,
             bool isContinuousProperty);
 
     // Unsubscribes from the properties for the client.
-    // Returns error if the client was not subscribed before or one of the given property was not
-    // subscribed. If error is returned, no property would be unsubscribed.
+    // Returns error if the client was not subscribed before, or one of the given property was not
+    // subscribed, or one of the property failed to unsubscribe. Caller is safe to retry since
+    // unsubscribing to an already unsubscribed property is okay (it would be ignored).
     // Returns ok if all the requested properties for the client are unsubscribed.
-    android::base::Result<void> unsubscribe(ClientIdType client,
-                                            const std::vector<int32_t>& propIds);
+    VhalResult<void> unsubscribe(ClientIdType client, const std::vector<int32_t>& propIds);
 
     // Unsubscribes from all the properties for the client.
-    // Returns error if the client was not subscribed before. If error is returned, no property
-    // would be unsubscribed.
+    // Returns error if the client was not subscribed before or one of the subscribed properties
+    // for the client failed to unsubscribe. Caller is safe to retry.
     // Returns ok if all the properties for the client are unsubscribed.
-    android::base::Result<void> unsubscribe(ClientIdType client);
+    VhalResult<void> unsubscribe(ClientIdType client);
 
     // For a list of updated properties, returns a map that maps clients subscribing to
     // the updated properties to a list of updated values. This would only return on-change property
@@ -83,6 +99,11 @@
             const std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue>&
                     updatedValues);
 
+    // Gets the sample rate for the continuous property. Returns {@code std::nullopt} if the
+    // property has not been subscribed before or is not a continuous property.
+    std::optional<float> getSampleRate(const ClientIdType& clientId, int32_t propId,
+                                       int32_t areaId);
+
     // Checks whether the sample rate is valid.
     static bool checkSampleRate(float sampleRate);
 
@@ -90,65 +111,28 @@
     // Friend class for testing.
     friend class DefaultVehicleHalTest;
 
-    struct PropIdAreaId {
-        int32_t propId;
-        int32_t areaId;
-
-        bool operator==(const PropIdAreaId& other) const;
-    };
-
-    struct PropIdAreaIdHash {
-        size_t operator()(const PropIdAreaId& propIdAreaId) const;
-    };
-
-    // A class to represent a registered subscription.
-    class Subscription {
-      public:
-        Subscription() = default;
-
-        Subscription(const Subscription&) = delete;
-
-        virtual ~Subscription() = default;
-
-        virtual bool isOnChange();
-    };
-
-    // A subscription for OnContinuous property. The registered action would be called recurrently
-    // until this class is destructed.
-    class RecurrentSubscription final : public Subscription {
-      public:
-        explicit RecurrentSubscription(std::shared_ptr<RecurrentTimer> timer,
-                                       std::function<void()>&& action, int64_t interval);
-        ~RecurrentSubscription();
-
-        bool isOnChange() override;
-
-      private:
-        std::shared_ptr<std::function<void()>> mAction;
-        std::shared_ptr<RecurrentTimer> mTimer;
-    };
-
-    // A subscription for OnChange property.
-    class OnChangeSubscription final : public Subscription {
-      public:
-        bool isOnChange() override;
-    };
+    IVehicleHardware* mVehicleHardware;
 
     mutable std::mutex mLock;
     std::unordered_map<PropIdAreaId, std::unordered_map<ClientIdType, CallbackType>,
                        PropIdAreaIdHash>
             mClientsByPropIdArea GUARDED_BY(mLock);
-    std::unordered_map<ClientIdType, std::unordered_map<PropIdAreaId, std::unique_ptr<Subscription>,
-                                                        PropIdAreaIdHash>>
-            mSubscriptionsByClient GUARDED_BY(mLock);
-    // RecurrentTimer is thread-safe.
-    std::shared_ptr<RecurrentTimer> mTimer;
-    const GetValueFunc mGetValue;
+    std::unordered_map<ClientIdType, std::unordered_set<PropIdAreaId, PropIdAreaIdHash>>
+            mSubscribedPropsByClient GUARDED_BY(mLock);
+    std::unordered_map<PropIdAreaId, ContSubConfigs, PropIdAreaIdHash> mContSubConfigsByPropIdArea
+            GUARDED_BY(mLock);
 
-    static android::base::Result<int64_t> getInterval(float sampleRate);
+    VhalResult<void> updateSampleRateLocked(const ClientIdType& clientId,
+                                            const PropIdAreaId& propIdAreaId, float sampleRate)
+            REQUIRES(mLock);
+    VhalResult<void> removeSampleRateLocked(const ClientIdType& clientId,
+                                            const PropIdAreaId& propIdAreaId) REQUIRES(mLock);
 
     // Checks whether the manager is empty. For testing purpose.
     bool isEmpty();
+
+    // Get the interval in nanoseconds accroding to sample rate.
+    static android::base::Result<int64_t> getInterval(float sampleRate);
 };
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
index 82f2c1b..b191aef 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
@@ -144,15 +144,11 @@
     }
 
     mSubscriptionClients = std::make_shared<SubscriptionClients>(mPendingRequestPool);
+    mSubscriptionClients = std::make_shared<SubscriptionClients>(mPendingRequestPool);
 
     auto subscribeIdByClient = std::make_shared<SubscribeIdByClient>();
-    // Make a weak copy of IVehicleHardware because subscriptionManager uses IVehicleHardware and
-    // IVehicleHardware uses subscriptionManager. We want to avoid cyclic reference.
-    std::weak_ptr<IVehicleHardware> hardwareCopy = mVehicleHardware;
-    SubscriptionManager::GetValueFunc getValueFunc = std::bind(
-            &DefaultVehicleHal::getValueFromHardwareCallCallback, hardwareCopy, subscribeIdByClient,
-            mSubscriptionClients, std::placeholders::_1, std::placeholders::_2);
-    mSubscriptionManager = std::make_shared<SubscriptionManager>(std::move(getValueFunc));
+    IVehicleHardware* hardwarePtr = mVehicleHardware.get();
+    mSubscriptionManager = std::make_shared<SubscriptionManager>(hardwarePtr);
 
     std::weak_ptr<SubscriptionManager> subscriptionManagerCopy = mSubscriptionManager;
     mVehicleHardware->registerOnPropertyChangeEvent(
@@ -162,11 +158,11 @@
                     }));
 
     // Register heartbeat event.
-    mRecurrentTimer.registerTimerCallback(
-            HEART_BEAT_INTERVAL_IN_NANO,
-            std::make_shared<std::function<void()>>([hardwareCopy, subscriptionManagerCopy]() {
-                checkHealth(hardwareCopy, subscriptionManagerCopy);
-            }));
+    mRecurrentAction =
+            std::make_shared<std::function<void()>>([hardwarePtr, subscriptionManagerCopy]() {
+                checkHealth(hardwarePtr, subscriptionManagerCopy);
+            });
+    mRecurrentTimer.registerTimerCallback(HEART_BEAT_INTERVAL_IN_NANO, mRecurrentAction);
 
     mBinderImpl = std::make_unique<AIBinderImpl>();
     mOnBinderDiedUnlinkedHandlerThread = std::thread([this] { onBinderDiedUnlinkedHandler(); });
@@ -183,6 +179,13 @@
     if (mOnBinderDiedUnlinkedHandlerThread.joinable()) {
         mOnBinderDiedUnlinkedHandlerThread.join();
     }
+    // mRecurrentAction uses pointer to mVehicleHardware, so it has to be unregistered before
+    // mVehicleHardware.
+    mRecurrentTimer.unregisterTimerCallback(mRecurrentAction);
+    // mSubscriptionManager uses pointer to mVehicleHardware, so it has to be destroyed before
+    // mVehicleHardware.
+    mSubscriptionManager.reset();
+    mVehicleHardware.reset();
 }
 
 void DefaultVehicleHal::onPropertyChangeEvent(
@@ -294,43 +297,6 @@
         std::unordered_map<const AIBinder*, std::shared_ptr<SubscriptionClient>>* clients,
         const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool);
 
-void DefaultVehicleHal::getValueFromHardwareCallCallback(
-        std::weak_ptr<IVehicleHardware> vehicleHardware,
-        std::shared_ptr<SubscribeIdByClient> subscribeIdByClient,
-        std::shared_ptr<SubscriptionClients> subscriptionClients, const CallbackType& callback,
-        const VehiclePropValue& value) {
-    int64_t subscribeId = subscribeIdByClient->getId(callback);
-    auto client = subscriptionClients->getClient(callback);
-    if (client == nullptr) {
-        ALOGW("subscribe[%" PRId64 "]: the client has died", subscribeId);
-        return;
-    }
-    if (auto addRequestResult = client->addRequests({subscribeId}); !addRequestResult.ok()) {
-        ALOGE("subscribe[%" PRId64 "]: too many pending requests, ignore the getValue request",
-              subscribeId);
-        return;
-    }
-
-    std::vector<GetValueRequest> hardwareRequests = {{
-            .requestId = subscribeId,
-            .prop = value,
-    }};
-
-    std::shared_ptr<IVehicleHardware> hardware = vehicleHardware.lock();
-    if (hardware == nullptr) {
-        ALOGW("the IVehicleHardware is destroyed, DefaultVehicleHal is ending");
-        return;
-    }
-    if (StatusCode status = hardware->getValues(client->getResultCallback(), hardwareRequests);
-        status != StatusCode::OK) {
-        // If the hardware returns error, finish all the pending requests for this request because
-        // we never expect hardware to call callback for these requests.
-        client->tryFinishRequests({subscribeId});
-        ALOGE("subscribe[%" PRId64 "]: failed to get value from VehicleHardware, code: %d",
-              subscribeId, toInt(status));
-    }
-}
-
 void DefaultVehicleHal::setTimeout(int64_t timeoutInNano) {
     mPendingRequestPool = std::make_unique<PendingRequestPool>(timeoutInNano);
 }
@@ -705,12 +671,13 @@
 
         // Since we have already check the sample rates, the following functions must succeed.
         if (!onChangeSubscriptions.empty()) {
-            mSubscriptionManager->subscribe(callback, onChangeSubscriptions,
-                                            /*isContinuousProperty=*/false);
+            return toScopedAStatus(mSubscriptionManager->subscribe(callback, onChangeSubscriptions,
+                                                                   /*isContinuousProperty=*/false));
         }
         if (!continuousSubscriptions.empty()) {
-            mSubscriptionManager->subscribe(callback, continuousSubscriptions,
-                                            /*isContinuousProperty=*/true);
+            return toScopedAStatus(mSubscriptionManager->subscribe(callback,
+                                                                   continuousSubscriptions,
+                                                                   /*isContinuousProperty=*/true));
         }
     }
     return ScopedAStatus::ok();
@@ -718,8 +685,7 @@
 
 ScopedAStatus DefaultVehicleHal::unsubscribe(const CallbackType& callback,
                                              const std::vector<int32_t>& propIds) {
-    return toScopedAStatus(mSubscriptionManager->unsubscribe(callback->asBinder().get(), propIds),
-                           StatusCode::INVALID_ARG);
+    return toScopedAStatus(mSubscriptionManager->unsubscribe(callback->asBinder().get(), propIds));
 }
 
 ScopedAStatus DefaultVehicleHal::returnSharedMemory(const CallbackType&, int64_t) {
@@ -763,15 +729,9 @@
     return {};
 }
 
-void DefaultVehicleHal::checkHealth(std::weak_ptr<IVehicleHardware> hardware,
+void DefaultVehicleHal::checkHealth(IVehicleHardware* hardware,
                                     std::weak_ptr<SubscriptionManager> subscriptionManager) {
-    auto hardwarePtr = hardware.lock();
-    if (hardwarePtr == nullptr) {
-        ALOGW("the VehicleHardware is destroyed, DefaultVehicleHal is ending");
-        return;
-    }
-
-    StatusCode status = hardwarePtr->checkHealth();
+    StatusCode status = hardware->checkHealth();
     if (status != StatusCode::OK) {
         ALOGE("VHAL check health returns non-okay status");
         return;
diff --git a/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp b/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
index 21bfba6..2694401 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
@@ -16,8 +16,11 @@
 
 #include "SubscriptionManager.h"
 
-#include <math/HashCombine.h>
+#include <android-base/stringprintf.h>
 #include <utils/Log.h>
+#include <utils/SystemClock.h>
+
+#include <inttypes.h>
 
 namespace android {
 namespace hardware {
@@ -31,33 +34,21 @@
 }  // namespace
 
 using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback;
+using ::aidl::android::hardware::automotive::vehicle::StatusCode;
 using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
 using ::android::base::Error;
 using ::android::base::Result;
+using ::android::base::StringPrintf;
 using ::ndk::ScopedAStatus;
 
-bool SubscriptionManager::PropIdAreaId::operator==(const PropIdAreaId& other) const {
-    return areaId == other.areaId && propId == other.propId;
-}
-
-size_t SubscriptionManager::PropIdAreaIdHash::operator()(PropIdAreaId const& propIdAreaId) const {
-    size_t res = 0;
-    hashCombine(res, propIdAreaId.propId);
-    hashCombine(res, propIdAreaId.areaId);
-    return res;
-}
-
-SubscriptionManager::SubscriptionManager(GetValueFunc&& action)
-    : mTimer(std::make_shared<RecurrentTimer>()), mGetValue(std::move(action)) {}
+SubscriptionManager::SubscriptionManager(IVehicleHardware* hardware) : mVehicleHardware(hardware) {}
 
 SubscriptionManager::~SubscriptionManager() {
     std::scoped_lock<std::mutex> lockGuard(mLock);
 
     mClientsByPropIdArea.clear();
-    // RecurrentSubscription has reference to mGetValue, so it must be destroyed before mGetValue is
-    // destroyed.
-    mSubscriptionsByClient.clear();
+    mSubscribedPropsByClient.clear();
 }
 
 bool SubscriptionManager::checkSampleRate(float sampleRate) {
@@ -76,9 +67,84 @@
     return interval;
 }
 
-Result<void> SubscriptionManager::subscribe(const std::shared_ptr<IVehicleCallback>& callback,
-                                            const std::vector<SubscribeOptions>& options,
-                                            bool isContinuousProperty) {
+void ContSubConfigs::refreshMaxSampleRate() {
+    float maxSampleRate = 0.;
+    // This is not called frequently so a brute-focre is okay. More efficient way exists but this
+    // is simpler.
+    for (const auto& [_, sampleRate] : mSampleRates) {
+        if (sampleRate > maxSampleRate) {
+            maxSampleRate = sampleRate;
+        }
+    }
+    mMaxSampleRate = maxSampleRate;
+}
+
+void ContSubConfigs::addClient(const ClientIdType& clientId, float sampleRate) {
+    mSampleRates[clientId] = sampleRate;
+    refreshMaxSampleRate();
+}
+
+void ContSubConfigs::removeClient(const ClientIdType& clientId) {
+    mSampleRates.erase(clientId);
+    refreshMaxSampleRate();
+}
+
+float ContSubConfigs::getMaxSampleRate() {
+    return mMaxSampleRate;
+}
+
+VhalResult<void> SubscriptionManager::updateSampleRateLocked(const ClientIdType& clientId,
+                                                             const PropIdAreaId& propIdAreaId,
+                                                             float sampleRate) {
+    // Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases.
+    ContSubConfigs infoCopy = mContSubConfigsByPropIdArea[propIdAreaId];
+    infoCopy.addClient(clientId, sampleRate);
+    if (infoCopy.getMaxSampleRate() ==
+        mContSubConfigsByPropIdArea[propIdAreaId].getMaxSampleRate()) {
+        mContSubConfigsByPropIdArea[propIdAreaId] = infoCopy;
+        return {};
+    }
+    float newRate = infoCopy.getMaxSampleRate();
+    int32_t propId = propIdAreaId.propId;
+    int32_t areaId = propIdAreaId.areaId;
+    if (auto status = mVehicleHardware->updateSampleRate(propId, areaId, newRate);
+        status != StatusCode::OK) {
+        return StatusError(status) << StringPrintf("failed to update sample rate for prop: %" PRId32
+                                                   ", area"
+                                                   ": %" PRId32 ", sample rate: %f",
+                                                   propId, areaId, newRate);
+    }
+    mContSubConfigsByPropIdArea[propIdAreaId] = infoCopy;
+    return {};
+}
+
+VhalResult<void> SubscriptionManager::removeSampleRateLocked(const ClientIdType& clientId,
+                                                             const PropIdAreaId& propIdAreaId) {
+    // Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases.
+    ContSubConfigs infoCopy = mContSubConfigsByPropIdArea[propIdAreaId];
+    infoCopy.removeClient(clientId);
+    if (infoCopy.getMaxSampleRate() ==
+        mContSubConfigsByPropIdArea[propIdAreaId].getMaxSampleRate()) {
+        mContSubConfigsByPropIdArea[propIdAreaId] = infoCopy;
+        return {};
+    }
+    float newRate = infoCopy.getMaxSampleRate();
+    int32_t propId = propIdAreaId.propId;
+    int32_t areaId = propIdAreaId.areaId;
+    if (auto status = mVehicleHardware->updateSampleRate(propId, areaId, newRate);
+        status != StatusCode::OK) {
+        return StatusError(status) << StringPrintf("failed to update sample rate for prop: %" PRId32
+                                                   ", area"
+                                                   ": %" PRId32 ", sample rate: %f",
+                                                   propId, areaId, newRate);
+    }
+    mContSubConfigsByPropIdArea[propIdAreaId] = infoCopy;
+    return {};
+}
+
+VhalResult<void> SubscriptionManager::subscribe(const std::shared_ptr<IVehicleCallback>& callback,
+                                                const std::vector<SubscribeOptions>& options,
+                                                bool isContinuousProperty) {
     std::scoped_lock<std::mutex> lockGuard(mLock);
 
     std::vector<int64_t> intervals;
@@ -88,109 +154,108 @@
         if (isContinuousProperty) {
             auto intervalResult = getInterval(sampleRate);
             if (!intervalResult.ok()) {
-                return intervalResult.error();
+                return StatusError(StatusCode::INVALID_ARG) << intervalResult.error().message();
             }
-            intervals.push_back(intervalResult.value());
         }
 
         if (option.areaIds.empty()) {
             ALOGE("area IDs to subscribe must not be empty");
-            return Error() << "area IDs to subscribe must not be empty";
+            return StatusError(StatusCode::INVALID_ARG)
+                   << "area IDs to subscribe must not be empty";
         }
     }
 
-    size_t intervalIndex = 0;
     ClientIdType clientId = callback->asBinder().get();
+
     for (const auto& option : options) {
         int32_t propId = option.propId;
         const std::vector<int32_t>& areaIds = option.areaIds;
-        int64_t interval = 0;
-        if (isContinuousProperty) {
-            interval = intervals[intervalIndex];
-            intervalIndex++;
-        }
         for (int32_t areaId : areaIds) {
             PropIdAreaId propIdAreaId = {
                     .propId = propId,
                     .areaId = areaId,
             };
             if (isContinuousProperty) {
-                VehiclePropValue propValueRequest{
-                        .prop = propId,
-                        .areaId = areaId,
-                };
-                mSubscriptionsByClient[clientId][propIdAreaId] =
-                        std::make_unique<RecurrentSubscription>(
-                                mTimer,
-                                [this, callback, propValueRequest] {
-                                    mGetValue(callback, propValueRequest);
-                                },
-                                interval);
-            } else {
-                mSubscriptionsByClient[clientId][propIdAreaId] =
-                        std::make_unique<OnChangeSubscription>();
+                if (auto result = updateSampleRateLocked(clientId, propIdAreaId, option.sampleRate);
+                    !result.ok()) {
+                    return result;
+                }
             }
+
+            mSubscribedPropsByClient[clientId].insert(propIdAreaId);
             mClientsByPropIdArea[propIdAreaId][clientId] = callback;
         }
     }
     return {};
 }
 
-Result<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId,
-                                              const std::vector<int32_t>& propIds) {
+VhalResult<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId,
+                                                  const std::vector<int32_t>& propIds) {
     std::scoped_lock<std::mutex> lockGuard(mLock);
 
-    if (mSubscriptionsByClient.find(clientId) == mSubscriptionsByClient.end()) {
-        return Error() << "No property was subscribed for the callback";
+    if (mSubscribedPropsByClient.find(clientId) == mSubscribedPropsByClient.end()) {
+        return StatusError(StatusCode::INVALID_ARG)
+               << "No property was subscribed for the callback";
     }
     std::unordered_set<int32_t> subscribedPropIds;
-    for (auto const& [propIdAreaId, _] : mSubscriptionsByClient[clientId]) {
+    for (auto const& propIdAreaId : mSubscribedPropsByClient[clientId]) {
         subscribedPropIds.insert(propIdAreaId.propId);
     }
 
     for (int32_t propId : propIds) {
         if (subscribedPropIds.find(propId) == subscribedPropIds.end()) {
-            return Error() << "property ID: " << propId << " is not subscribed";
+            return StatusError(StatusCode::INVALID_ARG)
+                   << "property ID: " << propId << " is not subscribed";
         }
     }
 
-    auto& subscriptions = mSubscriptionsByClient[clientId];
-    auto it = subscriptions.begin();
-    while (it != subscriptions.end()) {
-        int32_t propId = it->first.propId;
+    auto& propIdAreaIds = mSubscribedPropsByClient[clientId];
+    auto it = propIdAreaIds.begin();
+    while (it != propIdAreaIds.end()) {
+        int32_t propId = it->propId;
         if (std::find(propIds.begin(), propIds.end(), propId) != propIds.end()) {
-            auto& clients = mClientsByPropIdArea[it->first];
+            if (auto result = removeSampleRateLocked(clientId, *it); !result.ok()) {
+                return result;
+            }
+
+            auto& clients = mClientsByPropIdArea[*it];
             clients.erase(clientId);
             if (clients.empty()) {
-                mClientsByPropIdArea.erase(it->first);
+                mClientsByPropIdArea.erase(*it);
+                mContSubConfigsByPropIdArea.erase(*it);
             }
-            it = subscriptions.erase(it);
+            it = propIdAreaIds.erase(it);
         } else {
             it++;
         }
     }
-    if (subscriptions.empty()) {
-        mSubscriptionsByClient.erase(clientId);
+    if (propIdAreaIds.empty()) {
+        mSubscribedPropsByClient.erase(clientId);
     }
     return {};
 }
 
-Result<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId) {
+VhalResult<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId) {
     std::scoped_lock<std::mutex> lockGuard(mLock);
 
-    if (mSubscriptionsByClient.find(clientId) == mSubscriptionsByClient.end()) {
-        return Error() << "No property was subscribed for this client";
+    if (mSubscribedPropsByClient.find(clientId) == mSubscribedPropsByClient.end()) {
+        return StatusError(StatusCode::INVALID_ARG) << "No property was subscribed for this client";
     }
 
-    auto& subscriptions = mSubscriptionsByClient[clientId];
-    for (auto const& [propIdAreaId, _] : subscriptions) {
+    auto& subscriptions = mSubscribedPropsByClient[clientId];
+    for (auto const& propIdAreaId : subscriptions) {
+        if (auto result = removeSampleRateLocked(clientId, propIdAreaId); !result.ok()) {
+            return result;
+        }
+
         auto& clients = mClientsByPropIdArea[propIdAreaId];
         clients.erase(clientId);
         if (clients.empty()) {
             mClientsByPropIdArea.erase(propIdAreaId);
+            mContSubConfigsByPropIdArea.erase(propIdAreaId);
         }
     }
-    mSubscriptionsByClient.erase(clientId);
+    mSubscribedPropsByClient.erase(clientId);
     return {};
 }
 
@@ -208,10 +273,8 @@
         if (mClientsByPropIdArea.find(propIdAreaId) == mClientsByPropIdArea.end()) {
             continue;
         }
-        for (const auto& [clientId, client] : mClientsByPropIdArea[propIdAreaId]) {
-            if (!mSubscriptionsByClient[clientId][propIdAreaId]->isOnChange()) {
-                continue;
-            }
+
+        for (const auto& [_, client] : mClientsByPropIdArea[propIdAreaId]) {
             clients[client].push_back(&value);
         }
     }
@@ -220,25 +283,7 @@
 
 bool SubscriptionManager::isEmpty() {
     std::scoped_lock<std::mutex> lockGuard(mLock);
-    return mSubscriptionsByClient.empty() && mClientsByPropIdArea.empty();
-}
-
-SubscriptionManager::RecurrentSubscription::RecurrentSubscription(
-        std::shared_ptr<RecurrentTimer> timer, std::function<void()>&& action, int64_t interval)
-    : mAction(std::make_shared<std::function<void()>>(action)), mTimer(timer) {
-    mTimer->registerTimerCallback(interval, mAction);
-}
-
-SubscriptionManager::RecurrentSubscription::~RecurrentSubscription() {
-    mTimer->unregisterTimerCallback(mAction);
-}
-
-bool SubscriptionManager::RecurrentSubscription::isOnChange() {
-    return false;
-}
-
-bool SubscriptionManager::OnChangeSubscription::isOnChange() {
-    return true;
+    return mSubscribedPropsByClient.empty() && mClientsByPropIdArea.empty();
 }
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
index 49f5b7e..f48b906 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
@@ -1308,25 +1308,7 @@
 TEST_F(DefaultVehicleHalTest, testSubscribeGlobalContinuous) {
     VehiclePropValue testValue{
             .prop = GLOBAL_CONTINUOUS_PROP,
-            .value.int32Values = {0},
     };
-    // Set responses for all the hardware getValues requests.
-    getHardware()->setGetValueResponder(
-            [](std::shared_ptr<const IVehicleHardware::GetValuesCallback> callback,
-               const std::vector<GetValueRequest>& requests) {
-                std::vector<GetValueResult> results;
-                for (auto& request : requests) {
-                    VehiclePropValue prop = request.prop;
-                    prop.value.int32Values = {0};
-                    results.push_back({
-                            .requestId = request.requestId,
-                            .status = StatusCode::OK,
-                            .prop = prop,
-                    });
-                }
-                (*callback)(results);
-                return StatusCode::OK;
-            });
 
     std::vector<SubscribeOptions> options = {
             {
@@ -1353,28 +1335,6 @@
 }
 
 TEST_F(DefaultVehicleHalTest, testSubscribeGlobalContinuousRateOutOfRange) {
-    VehiclePropValue testValue{
-            .prop = GLOBAL_CONTINUOUS_PROP,
-            .value.int32Values = {0},
-    };
-    // Set responses for all the hardware getValues requests.
-    getHardware()->setGetValueResponder(
-            [](std::shared_ptr<const IVehicleHardware::GetValuesCallback> callback,
-               const std::vector<GetValueRequest>& requests) {
-                std::vector<GetValueResult> results;
-                for (auto& request : requests) {
-                    VehiclePropValue prop = request.prop;
-                    prop.value.int32Values = {0};
-                    results.push_back({
-                            .requestId = request.requestId,
-                            .status = StatusCode::OK,
-                            .prop = prop,
-                    });
-                }
-                (*callback)(results);
-                return StatusCode::OK;
-            });
-
     // The maxSampleRate is 100, so the sample rate should be the default max 100.
     std::vector<SubscribeOptions> options = {
             {
@@ -1398,24 +1358,6 @@
 }
 
 TEST_F(DefaultVehicleHalTest, testSubscribeAreaContinuous) {
-    // Set responses for all the hardware getValues requests.
-    getHardware()->setGetValueResponder(
-            [](std::shared_ptr<const IVehicleHardware::GetValuesCallback> callback,
-               const std::vector<GetValueRequest>& requests) {
-                std::vector<GetValueResult> results;
-                for (auto& request : requests) {
-                    VehiclePropValue prop = request.prop;
-                    prop.value.int32Values = {0};
-                    results.push_back({
-                            .requestId = request.requestId,
-                            .status = StatusCode::OK,
-                            .prop = prop,
-                    });
-                }
-                (*callback)(results);
-                return StatusCode::OK;
-            });
-
     std::vector<SubscribeOptions> options = {
             {
                     .propId = AREA_CONTINUOUS_PROP,
@@ -1436,6 +1378,8 @@
     // Sleep for 1s, which should generate ~20 events.
     std::this_thread::sleep_for(std::chrono::seconds(1));
 
+    getClient()->unsubscribe(getCallbackClient(), std::vector<int32_t>({AREA_CONTINUOUS_PROP}));
+
     std::vector<VehiclePropValue> events;
     while (true) {
         auto maybeResults = getCallback()->nextOnPropertyEventResults();
@@ -1509,28 +1453,6 @@
 }
 
 TEST_F(DefaultVehicleHalTest, testUnsubscribeContinuous) {
-    VehiclePropValue testValue{
-            .prop = GLOBAL_CONTINUOUS_PROP,
-            .value.int32Values = {0},
-    };
-    // Set responses for all the hardware getValues requests.
-    getHardware()->setGetValueResponder(
-            [](std::shared_ptr<const IVehicleHardware::GetValuesCallback> callback,
-               const std::vector<GetValueRequest>& requests) {
-                std::vector<GetValueResult> results;
-                for (auto& request : requests) {
-                    VehiclePropValue prop = request.prop;
-                    prop.value.int32Values = {0};
-                    results.push_back({
-                            .requestId = request.requestId,
-                            .status = StatusCode::OK,
-                            .prop = prop,
-                    });
-                }
-                (*callback)(results);
-                return StatusCode::OK;
-            });
-
     std::vector<SubscribeOptions> options = {
             {
                     .propId = GLOBAL_CONTINUOUS_PROP,
@@ -1621,12 +1543,6 @@
 }
 
 TEST_F(DefaultVehicleHalTest, testOnBinderDiedUnlinked) {
-    // First subscribe to a continuous property so that we register a death recipient for our
-    // client.
-    VehiclePropValue testValue{
-            .prop = GLOBAL_CONTINUOUS_PROP,
-            .value.int32Values = {0},
-    };
     // Set responses for all the hardware getValues requests.
     getHardware()->setGetValueResponder(
             [](std::shared_ptr<const IVehicleHardware::GetValuesCallback> callback,
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
index 66aef7c..4df4e1a 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
@@ -32,9 +32,14 @@
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
 
+MockVehicleHardware::MockVehicleHardware() {
+    mRecurrentTimer = std::make_unique<RecurrentTimer>();
+}
+
 MockVehicleHardware::~MockVehicleHardware() {
     std::unique_lock<std::mutex> lk(mLock);
     mCv.wait(lk, [this] { return mThreadCount == 0; });
+    mRecurrentTimer.reset();
 }
 
 std::vector<VehiclePropConfig> MockVehicleHardware::getAllPropertyConfigs() const {
@@ -83,6 +88,42 @@
     return StatusCode::OK;
 }
 
+StatusCode MockVehicleHardware::updateSampleRate(int32_t propId, int32_t areaId, float sampleRate) {
+    std::shared_ptr<std::function<void()>> action;
+
+    {
+        std::scoped_lock<std::mutex> lockGuard(mLock);
+        if (mRecurrentActions[propId][areaId] != nullptr) {
+            // Remove the previous action register for this [propId, areaId].
+            mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propId][areaId]);
+        }
+        if (sampleRate == 0) {
+            return StatusCode::OK;
+        }
+
+        // We are sure 'propertyChangeCallback' would be alive because we would unregister timer
+        // before destroying 'this' which owns mPropertyChangeCallback.
+        const PropertyChangeCallback* propertyChangeCallback = mPropertyChangeCallback.get();
+        action = std::make_shared<std::function<void()>>([propertyChangeCallback, propId, areaId] {
+            std::vector<VehiclePropValue> values = {
+                    {
+                            .prop = propId,
+                            .areaId = areaId,
+                    },
+            };
+            (*propertyChangeCallback)(values);
+        });
+        // Store the action in a map so that we could remove the action later.
+        mRecurrentActions[propId][areaId] = action;
+    }
+
+    // In mock implementation, we generate a new property change event for this property at sample
+    // rate.
+    int64_t interval = static_cast<int64_t>(1'000'000'000. / sampleRate);
+    mRecurrentTimer->registerTimerCallback(interval, action);
+    return StatusCode::OK;
+}
+
 void MockVehicleHardware::registerOnPropertyChangeEvent(
         std::unique_ptr<const PropertyChangeCallback> callback) {
     std::scoped_lock<std::mutex> lockGuard(mLock);
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
index cb8b6a0..743841c 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
@@ -18,6 +18,7 @@
 #define android_hardware_automotive_vehicle_aidl_impl_vhal_test_MockVehicleHardware_H_
 
 #include <IVehicleHardware.h>
+#include <RecurrentTimer.h>
 #include <VehicleHalTypes.h>
 
 #include <android-base/thread_annotations.h>
@@ -38,6 +39,8 @@
 
 class MockVehicleHardware final : public IVehicleHardware {
   public:
+    MockVehicleHardware();
+
     ~MockVehicleHardware();
 
     std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
@@ -55,6 +58,8 @@
     void registerOnPropertyChangeEvent(
             std::unique_ptr<const PropertyChangeCallback> callback) override;
     void registerOnPropertySetErrorEvent(std::unique_ptr<const PropertySetErrorCallback>) override;
+    aidl::android::hardware::automotive::vehicle::StatusCode updateSampleRate(
+            int32_t propId, int32_t areaId, float sampleRate) override;
 
     // Test functions.
     void setPropertyConfigs(
@@ -117,6 +122,11 @@
             std::list<std::vector<ResultType>>* storedResponses) const REQUIRES(mLock);
 
     DumpResult mDumpResult;
+
+    // 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()>>>>
+            mRecurrentActions GUARDED_BY(mLock);
 };
 
 }  // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
index 2a468f6..3f59363 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
@@ -16,6 +16,7 @@
 
 #include "SubscriptionManager.h"
 
+#include <MockVehicleHardware.h>
 #include <VehicleHalTypes.h>
 
 #include <aidl/android/hardware/automotive/vehicle/BnVehicleCallback.h>
@@ -86,19 +87,21 @@
 class SubscriptionManagerTest : public testing::Test {
   public:
     void SetUp() override {
-        mManager = std::make_unique<SubscriptionManager>(
-                [](const std::shared_ptr<IVehicleCallback>& callback,
-                   const VehiclePropValue& value) {
-                    callback->onPropertyEvent(
-                            VehiclePropValues{
-                                    .payloads = {value},
-                            },
-                            0);
-                });
+        mHardware = std::make_shared<MockVehicleHardware>();
+        mManager = std::make_unique<SubscriptionManager>(mHardware.get());
         mCallback = ndk::SharedRefBase::make<PropertyCallback>();
         // Keep the local binder alive.
         mBinder = mCallback->asBinder();
         mCallbackClient = IVehicleCallback::fromBinder(mBinder);
+        std::shared_ptr<IVehicleCallback> callbackClient = mCallbackClient;
+        mHardware->registerOnPropertyChangeEvent(
+                std::make_unique<IVehicleHardware::PropertyChangeCallback>(
+                        [callbackClient](std::vector<VehiclePropValue> updatedValues) {
+                            VehiclePropValues values = {
+                                    .payloads = std::move(updatedValues),
+                            };
+                            callbackClient->onPropertyEvent(values, 0);
+                        }));
     }
 
     SubscriptionManager* getManager() { return mManager.get(); }
@@ -115,6 +118,7 @@
     std::unique_ptr<SubscriptionManager> mManager;
     std::shared_ptr<PropertyCallback> mCallback;
     std::shared_ptr<IVehicleCallback> mCallbackClient;
+    std::shared_ptr<MockVehicleHardware> mHardware;
     SpAIBinder mBinder;
 };
 
diff --git a/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp b/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
index c33f3e9..c431d85 100644
--- a/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
+++ b/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
@@ -30,6 +30,7 @@
 #include <hidl/ServiceManagement.h>
 #include <inttypes.h>
 #include <utils/Log.h>
+#include <utils/SystemClock.h>
 
 #include <chrono>
 #include <mutex>
@@ -67,6 +68,7 @@
   private:
     std::mutex mLock;
     std::unordered_map<int32_t, size_t> mEventsCount GUARDED_BY(mLock);
+    std::unordered_map<int32_t, std::vector<int64_t>> mEventTimestamps GUARDED_BY(mLock);
     std::condition_variable mEventCond;
 
   public:
@@ -74,7 +76,9 @@
         {
             std::lock_guard<std::mutex> lockGuard(mLock);
             for (auto& value : values) {
-                mEventsCount[value->getPropId()] += 1;
+                int32_t propId = value->getPropId();
+                mEventsCount[propId] += 1;
+                mEventTimestamps[propId].push_back(value->getTimestamp());
             }
         }
         mEventCond.notify_one();
@@ -94,6 +98,13 @@
         });
     }
 
+    std::vector<int64_t> getEventTimestamps(int32_t propId) {
+        {
+            std::lock_guard<std::mutex> lockGuard(mLock);
+            return mEventTimestamps[propId];
+        }
+    }
+
     void reset() {
         std::lock_guard<std::mutex> lockGuard(mLock);
         mEventsCount.clear();
@@ -285,19 +296,59 @@
 
     int32_t propId = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
 
-    std::vector<SubscribeOptions> options = {
-            SubscribeOptions{.propId = propId, .sampleRate = 10.0}};
+    auto propConfigsResult = mVhalClient->getPropConfigs({propId});
+
+    ASSERT_TRUE(propConfigsResult.ok()) << "Failed to get property config for PERF_VEHICLE_SPEED: "
+                                        << "error: " << propConfigsResult.error().message();
+    ASSERT_EQ(propConfigsResult.value().size(), 1u)
+            << "Expect to return 1 config for PERF_VEHICLE_SPEED";
+    auto& propConfig = propConfigsResult.value()[0];
+    float minSampleRate = propConfig->getMinSampleRate();
+    float maxSampleRate = propConfig->getMaxSampleRate();
+
+    if (minSampleRate < 1) {
+        GTEST_SKIP() << "Sample rate for vehicle speed < 1 times/sec, skip test since it would "
+                        "take too long";
+    }
 
     auto client = mVhalClient->getSubscriptionClient(mCallback);
     ASSERT_NE(client, nullptr) << "Failed to get subscription client";
 
-    auto result = client->subscribe(options);
+    auto result = client->subscribe({{.propId = propId, .sampleRate = minSampleRate}});
 
     ASSERT_TRUE(result.ok()) << StringPrintf("Failed to subscribe to property: %" PRId32
                                              ", error: %s",
                                              propId, result.error().message().c_str());
-    ASSERT_TRUE(mCallback->waitForExpectedEvents(propId, 10, std::chrono::seconds(10)))
-            << "Didn't get enough events for subscription";
+
+    if (mVhalClient->isAidlVhal()) {
+        // Skip checking timestamp for HIDL because the behavior for sample rate and timestamp is
+        // only specified clearly for AIDL.
+
+        // Timeout is 2 seconds, which gives a 1 second buffer.
+        ASSERT_TRUE(mCallback->waitForExpectedEvents(propId, std::floor(minSampleRate),
+                                                     std::chrono::seconds(2)))
+                << "Didn't get enough events for subscribing to minSampleRate";
+    }
+
+    result = client->subscribe({{.propId = propId, .sampleRate = maxSampleRate}});
+
+    ASSERT_TRUE(result.ok()) << StringPrintf("Failed to subscribe to property: %" PRId32
+                                             ", error: %s",
+                                             propId, result.error().message().c_str());
+
+    if (mVhalClient->isAidlVhal()) {
+        ASSERT_TRUE(mCallback->waitForExpectedEvents(propId, std::floor(maxSampleRate),
+                                                     std::chrono::seconds(2)))
+                << "Didn't get enough events for subscribing to maxSampleRate";
+
+        std::unordered_set<int64_t> timestamps;
+        // Event event should have a different timestamp.
+        for (const int64_t& eventTimestamp : mCallback->getEventTimestamps(propId)) {
+            ASSERT_TRUE(timestamps.find(eventTimestamp) == timestamps.end())
+                    << "two events for the same property must not have the same timestamp";
+            timestamps.insert(eventTimestamp);
+        }
+    }
 
     result = client->unsubscribe({propId});
     ASSERT_TRUE(result.ok()) << StringPrintf("Failed to unsubscribe to property: %" PRId32
@@ -325,6 +376,49 @@
                                               kInvalidProp);
 }
 
+// Test the timestamp returned in GetValues results is the timestamp when the value is retrieved.
+TEST_P(VtsHalAutomotiveVehicleTargetTest, testGetValuesTimestampAIDL) {
+    if (!mVhalClient->isAidlVhal()) {
+        GTEST_SKIP() << "Skip checking timestamp for HIDL because the behavior is only specified "
+                        "for AIDL";
+    }
+
+    int32_t propId = toInt(VehicleProperty::PARKING_BRAKE_ON);
+    auto prop = mVhalClient->createHalPropValue(propId);
+
+    auto result = mVhalClient->getValueSync(*prop);
+
+    ASSERT_TRUE(result.ok()) << StringPrintf("Failed to get value for property: %" PRId32
+                                             ", error: %s",
+                                             propId, result.error().message().c_str());
+    ASSERT_NE(result.value(), nullptr) << "Result value must not be null";
+    ASSERT_EQ(result.value()->getInt32Values().size(), 1u) << "Result must contain 1 int value";
+
+    bool parkBrakeOnValue1 = (result.value()->getInt32Values()[0] == 1);
+    int64_t timestampValue1 = result.value()->getTimestamp();
+
+    result = mVhalClient->getValueSync(*prop);
+
+    ASSERT_TRUE(result.ok()) << StringPrintf("Failed to get value for property: %" PRId32
+                                             ", error: %s",
+                                             propId, result.error().message().c_str());
+    ASSERT_NE(result.value(), nullptr) << "Result value must not be null";
+    ASSERT_EQ(result.value()->getInt32Values().size(), 1u) << "Result must contain 1 int value";
+
+    bool parkBarkeOnValue2 = (result.value()->getInt32Values()[0] == 1);
+    int64_t timestampValue2 = result.value()->getTimestamp();
+
+    if (parkBarkeOnValue2 == parkBrakeOnValue1) {
+        ASSERT_EQ(timestampValue2, timestampValue1)
+                << "getValue result must contain a timestamp updated when the value was updated, if"
+                   "the value does not change, expect the same timestamp";
+    } else {
+        ASSERT_GT(timestampValue2, timestampValue1)
+                << "getValue result must contain a timestamp updated when the value was updated, if"
+                   "the value changes, expect the newer value has a larger timestamp";
+    }
+}
+
 std::vector<ServiceDescriptor> getDescriptors() {
     std::vector<ServiceDescriptor> descriptors;
     for (std::string name : getAidlHalInstanceNames(IVehicle::descriptor)) {
diff --git a/bluetooth/1.0/default/test/fuzzer/Android.bp b/bluetooth/1.0/default/test/fuzzer/Android.bp
index 81f328e..691136f 100644
--- a/bluetooth/1.0/default/test/fuzzer/Android.bp
+++ b/bluetooth/1.0/default/test/fuzzer/Android.bp
@@ -46,7 +46,6 @@
         "android.hardware.bluetooth-async",
         "android.hardware.bluetooth-hci",
         "libcutils",
-        "libutils",
     ],
     shared_libs: [
         "android.hardware.bluetooth@1.0",
@@ -54,6 +53,7 @@
         "libhidlbase",
         "libbt-vendor-fuzz",
         "liblog",
+        "libutils",
     ],
     fuzz_config: {
         cc: [
diff --git a/bluetooth/audio/aidl/default/BluetoothAudioProvider.cpp b/bluetooth/audio/aidl/default/BluetoothAudioProvider.cpp
index 0dd8148..2a88959 100644
--- a/bluetooth/audio/aidl/default/BluetoothAudioProvider.cpp
+++ b/bluetooth/audio/aidl/default/BluetoothAudioProvider.cpp
@@ -45,6 +45,7 @@
   latency_modes_ = latencyModes;
   audio_config_ = std::make_unique<AudioConfiguration>(audio_config);
   stack_iface_ = host_if;
+  is_binder_died = false;
 
   AIBinder_linkToDeath(stack_iface_->asBinder().get(), death_recipient_.get(),
                        this);
@@ -59,8 +60,10 @@
   if (stack_iface_ != nullptr) {
     BluetoothAudioSessionReport::OnSessionEnded(session_type_);
 
-    AIBinder_unlinkToDeath(stack_iface_->asBinder().get(),
-                           death_recipient_.get(), this);
+    if (!is_binder_died) {
+      AIBinder_unlinkToDeath(stack_iface_->asBinder().get(),
+                             death_recipient_.get(), this);
+    }
   } else {
     LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
               << " has NO session";
@@ -147,6 +150,7 @@
     LOG(ERROR) << __func__ << ": Null AudioProvider HAL died";
     return;
   }
+  provider->is_binder_died = true;
   provider->endSession();
 }
 
diff --git a/bluetooth/audio/aidl/default/BluetoothAudioProvider.h b/bluetooth/audio/aidl/default/BluetoothAudioProvider.h
index a9f830a..dbfff7d 100644
--- a/bluetooth/audio/aidl/default/BluetoothAudioProvider.h
+++ b/bluetooth/audio/aidl/default/BluetoothAudioProvider.h
@@ -62,6 +62,7 @@
   std::unique_ptr<AudioConfiguration> audio_config_ = nullptr;
   SessionType session_type_;
   std::vector<LatencyMode> latency_modes_;
+  bool is_binder_died = false;
 };
 
 }  // namespace audio
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 ae17c51..1d81f7b 100644
--- a/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientWriter.h
+++ b/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientWriter.h
@@ -80,8 +80,9 @@
         getDisplayCommand(display).colorTransformMatrix.emplace(std::move(matVec));
     }
 
-    void setDisplayBrightness(int64_t display, float brightness) {
-        getDisplayCommand(display).brightness.emplace(DisplayBrightness{.brightness = brightness});
+    void setDisplayBrightness(int64_t display, float brightness, float brightnessNits) {
+        getDisplayCommand(display).brightness.emplace(
+                DisplayBrightness{.brightness = brightness, .brightnessNits = brightnessNits});
     }
 
     void setClientTarget(int64_t display, uint32_t slot, const native_handle_t* target,
diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
index 147a9ee..c081199 100644
--- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
+++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
@@ -974,7 +974,7 @@
     // Preconditions to successfully run are knowing the max brightness and successfully applying
     // the max brightness
     ASSERT_GT(maxBrightnessNits, 0.f);
-    mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 1.f);
+    mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 1.f, maxBrightnessNits);
     execute();
     ASSERT_TRUE(mReader.takeErrors().empty());
 
diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
index 2d08ac6..2cae5a2 100644
--- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
+++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
@@ -1374,7 +1374,7 @@
     bool brightnessSupport = std::find(capabilities.begin(), capabilities.end(),
                                        DisplayCapability::BRIGHTNESS) != capabilities.end();
     if (!brightnessSupport) {
-        mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 0.5f);
+        mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 0.5f, -1.f);
         execute();
         const auto errors = mReader.takeErrors();
         EXPECT_EQ(1, errors.size());
@@ -1383,23 +1383,23 @@
         return;
     }
 
-    mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 0.0f);
+    mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 0.0f, -1.f);
     execute();
     EXPECT_TRUE(mReader.takeErrors().empty());
 
-    mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 0.5f);
+    mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 0.5f, -1.f);
     execute();
     EXPECT_TRUE(mReader.takeErrors().empty());
 
-    mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 1.0f);
+    mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 1.0f, -1.f);
     execute();
     EXPECT_TRUE(mReader.takeErrors().empty());
 
-    mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ -1.0f);
+    mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ -1.0f, -1.f);
     execute();
     EXPECT_TRUE(mReader.takeErrors().empty());
 
-    mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 2.0f);
+    mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 2.0f, -1.f);
     execute();
     {
         const auto errors = mReader.takeErrors();
@@ -1407,7 +1407,7 @@
         EXPECT_EQ(IComposerClient::EX_BAD_PARAMETER, errors[0].errorCode);
     }
 
-    mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ -2.0f);
+    mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ -2.0f, -1.f);
     execute();
     {
         const auto errors = mReader.takeErrors();
diff --git a/graphics/mapper/3.0/utils/vts/MapperVts.cpp b/graphics/mapper/3.0/utils/vts/MapperVts.cpp
index de886a9..c470a4a 100644
--- a/graphics/mapper/3.0/utils/vts/MapperVts.cpp
+++ b/graphics/mapper/3.0/utils/vts/MapperVts.cpp
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
+#include <android-base/properties.h>
 #include <mapper-vts/3.0/MapperVts.h>
+#include "gtest/gtest.h"
 
 namespace android {
 namespace hardware {
@@ -94,23 +96,31 @@
     std::vector<const native_handle_t*> bufferHandles;
     bufferHandles.reserve(count);
     mAllocator->allocate(
-        descriptor, count,
-        [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) {
-            ASSERT_EQ(Error::NONE, tmpError) << "failed to allocate buffers";
-            ASSERT_EQ(count, tmpBuffers.size()) << "invalid buffer array";
-
-            for (uint32_t i = 0; i < count; i++) {
-                if (import) {
-                    ASSERT_NO_FATAL_FAILURE(bufferHandles.push_back(importBuffer(tmpBuffers[i])));
-                } else {
-                    ASSERT_NO_FATAL_FAILURE(bufferHandles.push_back(cloneBuffer(tmpBuffers[i])));
+            descriptor, count,
+            [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) {
+                if (tmpError != Error::NONE) {
+                    if (base::GetIntProperty("ro.vendor.build.version.sdk", 0, 0, INT_MAX) < 33) {
+                        GTEST_SKIP() << "Old vendor grallocs may not support P010";
+                    } else {
+                        GTEST_FAIL() << "failed to allocate buffers";
+                    }
                 }
-            }
+                ASSERT_EQ(count, tmpBuffers.size()) << "invalid buffer array";
 
-            if (outStride) {
-                *outStride = tmpStride;
-            }
-        });
+                for (uint32_t i = 0; i < count; i++) {
+                    if (import) {
+                        ASSERT_NO_FATAL_FAILURE(
+                                bufferHandles.push_back(importBuffer(tmpBuffers[i])));
+                    } else {
+                        ASSERT_NO_FATAL_FAILURE(
+                                bufferHandles.push_back(cloneBuffer(tmpBuffers[i])));
+                    }
+                }
+
+                if (outStride) {
+                    *outStride = tmpStride;
+                }
+            });
 
     if (::testing::Test::HasFatalFailure()) {
         bufferHandles.clear();
@@ -127,7 +137,7 @@
     }
 
     auto buffers = allocate(descriptor, 1, import, outStride);
-    if (::testing::Test::HasFatalFailure()) {
+    if (::testing::Test::HasFatalFailure() || ::testing::Test::IsSkipped()) {
         return nullptr;
     }
 
diff --git a/graphics/mapper/3.0/vts/functional/VtsHalGraphicsMapperV3_0TargetTest.cpp b/graphics/mapper/3.0/vts/functional/VtsHalGraphicsMapperV3_0TargetTest.cpp
index 6c90af4..3b1bfab 100644
--- a/graphics/mapper/3.0/vts/functional/VtsHalGraphicsMapperV3_0TargetTest.cpp
+++ b/graphics/mapper/3.0/vts/functional/VtsHalGraphicsMapperV3_0TargetTest.cpp
@@ -337,6 +337,10 @@
     uint32_t stride;
     ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(info, true, &stride));
 
+    if (::testing::Test::IsSkipped()) {
+        GTEST_SKIP();
+    }
+
     ASSERT_NE(nullptr, bufferHandle);
 
     const IMapper::Rect region{0, 0, static_cast<int32_t>(info.width),
diff --git a/graphics/mapper/4.0/utils/vts/MapperVts.cpp b/graphics/mapper/4.0/utils/vts/MapperVts.cpp
index 901f0e3..4a6f68d 100644
--- a/graphics/mapper/4.0/utils/vts/MapperVts.cpp
+++ b/graphics/mapper/4.0/utils/vts/MapperVts.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <android-base/properties.h>
 #include <gralloctypes/Gralloc4.h>
 #include <mapper-vts/4.0/MapperVts.h>
 
@@ -95,7 +96,14 @@
                                  return;
                              }
 
-                             ASSERT_EQ(Error::NONE, tmpError) << "failed to allocate buffers";
+                             if (tmpError != Error::NONE) {
+                                 if (base::GetIntProperty("ro.vendor.build.version.sdk", 0, 0,
+                                                          INT_MAX) < 33) {
+                                     GTEST_SKIP() << "Old vendor grallocs may not support P010";
+                                 } else {
+                                     GTEST_FAIL() << "failed to allocate buffers";
+                                 }
+                             }
                              ASSERT_EQ(count, tmpBuffers.size()) << "invalid buffer array";
 
                              for (uint32_t i = 0; i < count; i++) {
@@ -133,11 +141,7 @@
     }
 
     auto buffers = allocate(descriptor, 1, import, tolerance, outStride);
-    if (::testing::Test::HasFatalFailure()) {
-        return nullptr;
-    }
-
-    if (buffers.size() != 1) {
+    if (::testing::Test::HasFatalFailure() || ::testing::Test::IsSkipped() || buffers.size() != 1) {
         return nullptr;
     }
     return buffers[0];
diff --git a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
index 463b565..8f440e4 100644
--- a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
+++ b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
@@ -999,10 +999,13 @@
     auto info = mDummyDescriptorInfo;
     info.format = PixelFormat::YCBCR_P010;
 
-    const native_handle_t* bufferHandle;
     uint32_t stride;
-    ASSERT_NO_FATAL_FAILURE(
-            bufferHandle = mGralloc->allocate(info, true, Tolerance::kToleranceStrict, &stride));
+    const native_handle_t* bufferHandle =
+            mGralloc->allocate(info, true, Tolerance::kToleranceStrict, &stride);
+
+    if (::testing::Test::IsSkipped()) {
+        GTEST_SKIP();
+    }
 
     const IMapper::Rect region{0, 0, static_cast<int32_t>(info.width),
                                static_cast<int32_t>(info.height)};
diff --git a/health/aidl/OWNERS b/health/aidl/OWNERS
index cd06415..fcad499 100644
--- a/health/aidl/OWNERS
+++ b/health/aidl/OWNERS
@@ -1,2 +1,4 @@
 # Bug component: 30545
 elsk@google.com
+smoreland@google.com
+stayfan@google.com
diff --git a/keymaster/4.0/support/fuzzer/Android.bp b/keymaster/4.0/support/fuzzer/Android.bp
index 3a3f4d5..8bc681a 100644
--- a/keymaster/4.0/support/fuzzer/Android.bp
+++ b/keymaster/4.0/support/fuzzer/Android.bp
@@ -30,12 +30,12 @@
         "libbase",
         "liblog",
         "libkeymaster4support",
-        "libutils",
     ],
     shared_libs: [
         "android.hardware.keymaster@4.0",
         "libcrypto",
         "libhidlbase",
+        "libutils",
     ],
     fuzz_config: {
         cc: [
diff --git a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
index 22aa0f9..bf56860 100644
--- a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
+++ b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
@@ -2866,8 +2866,8 @@
 
         EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
         string plaintext;
-        ErrorCode error = Finish(message, &plaintext);
-        if (error == ErrorCode::INVALID_INPUT_LENGTH) {
+        ErrorCode error = Finish(ciphertext, &plaintext);
+        if (error == ErrorCode::INVALID_ARGUMENT) {
             // This is the expected error, we can exit the test now.
             return;
         } else {
diff --git a/media/omx/1.0/vts/functional/store/VtsHalMediaOmxV1_0TargetStoreTest.cpp b/media/omx/1.0/vts/functional/store/VtsHalMediaOmxV1_0TargetStoreTest.cpp
index d9a6363..5fa13e7 100644
--- a/media/omx/1.0/vts/functional/store/VtsHalMediaOmxV1_0TargetStoreTest.cpp
+++ b/media/omx/1.0/vts/functional/store/VtsHalMediaOmxV1_0TargetStoreTest.cpp
@@ -24,6 +24,7 @@
 #include <android-base/strings.h>
 #include <android/api-level.h>
 
+#include <VtsCoreUtil.h>
 #include <android/hardware/media/omx/1.0/IOmx.h>
 #include <android/hardware/media/omx/1.0/IOmxNode.h>
 #include <android/hardware/media/omx/1.0/IOmxObserver.h>
@@ -377,6 +378,10 @@
     return android::base::GetIntProperty("ro.product.first_api_level", __ANDROID_API_T__);
 }
 
+static bool isTV() {
+    return testing::deviceSupportsFeature("android.software.leanback");
+}
+
 // list components and roles.
 TEST_P(StoreHidlTest, OmxCodecAllowedTest) {
     hidl_vec<IOmx::ComponentInfo> componentInfos = getComponentInfoList(omx);
@@ -384,9 +389,16 @@
         for (std::string role : info.mRoles) {
             if (role.find("video_decoder") != std::string::npos ||
                 role.find("video_encoder") != std::string::npos) {
-                ASSERT_LT(getFirstApiLevel(), __ANDROID_API_S__)
-                        << " Component: " << info.mName.c_str() << " Role: " << role.c_str()
-                        << " not allowed for devices launching with Android S and above";
+                // Codec2 is not mandatory on Android TV devices that launched with Android S
+                if (isTV()) {
+                    ASSERT_LT(getFirstApiLevel(), __ANDROID_API_T__)
+                            << " Component: " << info.mName.c_str() << " Role: " << role.c_str()
+                            << " not allowed for devices launching with Android T and above";
+                } else {
+                    ASSERT_LT(getFirstApiLevel(), __ANDROID_API_S__)
+                            << " Component: " << info.mName.c_str() << " Role: " << role.c_str()
+                            << " not allowed for devices launching with Android S and above";
+                }
             }
             if (role.find("audio_decoder") != std::string::npos ||
                 role.find("audio_encoder") != std::string::npos) {
diff --git a/radio/aidl/vts/radio_config_test.cpp b/radio/aidl/vts/radio_config_test.cpp
index 5e1c811..258b172 100644
--- a/radio/aidl/vts/radio_config_test.cpp
+++ b/radio/aidl/vts/radio_config_test.cpp
@@ -59,6 +59,7 @@
     serial = GetRandomSerialNumber();
     ndk::ScopedAStatus res = radio_config->getHalDeviceCapabilities(serial);
     ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
     ALOGI("getHalDeviceCapabilities, rspInfo.error = %s\n",
           toString(radioRsp_config->rspInfo.error).c_str());
 }
@@ -70,6 +71,7 @@
     serial = GetRandomSerialNumber();
     ndk::ScopedAStatus res = radio_config->getSimSlotsStatus(serial);
     ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
     ALOGI("getSimSlotsStatus, rspInfo.error = %s\n",
           toString(radioRsp_config->rspInfo.error).c_str());
 }
@@ -166,31 +168,60 @@
  * Test IRadioConfig.setSimSlotsMapping() for the response returned.
  */
 TEST_P(RadioConfigTest, setSimSlotsMapping) {
-    serial = GetRandomSerialNumber();
-    SlotPortMapping slotPortMapping;
-    slotPortMapping.physicalSlotId = 0;
-    slotPortMapping.portId = 0;
-    std::vector<SlotPortMapping> slotPortMappingList = {slotPortMapping};
-    if (isDsDsEnabled()) {
-        slotPortMapping.physicalSlotId = 1;
-        slotPortMappingList.push_back(slotPortMapping);
-    } else if (isTsTsEnabled()) {
-        slotPortMapping.physicalSlotId = 1;
-        slotPortMappingList.push_back(slotPortMapping);
-        slotPortMapping.physicalSlotId = 2;
-        slotPortMappingList.push_back(slotPortMapping);
-    }
-    ndk::ScopedAStatus res = radio_config->setSimSlotsMapping(serial, slotPortMappingList);
-    ASSERT_OK(res);
-    EXPECT_EQ(std::cv_status::no_timeout, wait());
-    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_config->rspInfo.type);
-    EXPECT_EQ(serial, radioRsp_config->rspInfo.serial);
-    ALOGI("setSimSlotsMapping, rspInfo.error = %s\n",
-          toString(radioRsp_config->rspInfo.error).c_str());
-    ASSERT_TRUE(CheckAnyOfErrors(radioRsp_config->rspInfo.error, {RadioError::NONE}));
+    // get slot status and set SIM slots mapping based on the result.
+    updateSimSlotStatus();
+    if (radioRsp_config->rspInfo.error == RadioError::NONE) {
+        SlotPortMapping slotPortMapping;
+        // put invalid value at first and adjust by slotStatusResponse.
+        slotPortMapping.physicalSlotId = -1;
+        slotPortMapping.portId = -1;
+        std::vector<SlotPortMapping> slotPortMappingList = {slotPortMapping};
+        if (isDsDsEnabled()) {
+            slotPortMappingList.push_back(slotPortMapping);
+        } else if (isTsTsEnabled()) {
+            slotPortMappingList.push_back(slotPortMapping);
+            slotPortMappingList.push_back(slotPortMapping);
+        }
+        for (size_t i = 0; i < radioRsp_config->simSlotStatus.size(); i++) {
+            ASSERT_TRUE(radioRsp_config->simSlotStatus[i].portInfo.size() > 0);
+            for (size_t j = 0; j < radioRsp_config->simSlotStatus[i].portInfo.size(); j++) {
+                if (radioRsp_config->simSlotStatus[i].portInfo[j].portActive) {
+                    int32_t logicalSlotId =
+                            radioRsp_config->simSlotStatus[i].portInfo[j].logicalSlotId;
+                    // logicalSlotId should be 0 or positive numbers if the port
+                    // is active.
+                    EXPECT_GE(logicalSlotId, 0);
+                    // logicalSlotId should be less than the maximum number of
+                    // supported SIM slots.
+                    EXPECT_LT(logicalSlotId, slotPortMappingList.size());
+                    if (logicalSlotId >= 0 && logicalSlotId < slotPortMappingList.size()) {
+                        slotPortMappingList[logicalSlotId].physicalSlotId = i;
+                        slotPortMappingList[logicalSlotId].portId = j;
+                    }
+                }
+            }
+        }
 
-    // Give some time for modem to fully switch SIM configuration
-    sleep(MODEM_SET_SIM_SLOT_MAPPING_DELAY_IN_SECONDS);
+        // set SIM slots mapping
+        for (size_t i = 0; i < slotPortMappingList.size(); i++) {
+            // physicalSlotId and portId should be 0 or positive numbers for the
+            // input of setSimSlotsMapping.
+            EXPECT_GE(slotPortMappingList[i].physicalSlotId, 0);
+            EXPECT_GE(slotPortMappingList[i].portId, 0);
+        }
+        serial = GetRandomSerialNumber();
+        ndk::ScopedAStatus res = radio_config->setSimSlotsMapping(serial, slotPortMappingList);
+        ASSERT_OK(res);
+        EXPECT_EQ(std::cv_status::no_timeout, wait());
+        EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_config->rspInfo.type);
+        EXPECT_EQ(serial, radioRsp_config->rspInfo.serial);
+        ALOGI("setSimSlotsMapping, rspInfo.error = %s\n",
+              toString(radioRsp_config->rspInfo.error).c_str());
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_config->rspInfo.error, {RadioError::NONE}));
+
+        // Give some time for modem to fully switch SIM configuration
+        sleep(MODEM_SET_SIM_SLOT_MAPPING_DELAY_IN_SECONDS);
+    }
 }
 
 /*
@@ -207,12 +238,24 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_config->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_config->rspInfo.serial);
     if (radioRsp_config->rspInfo.error == RadioError::NONE) {
+        uint8_t simCount = 0;
         // check if cardState is present, portInfo size should be more than 0
         for (const SimSlotStatus& slotStatusResponse : radioRsp_config->simSlotStatus) {
             if (slotStatusResponse.cardState == CardStatus::STATE_PRESENT) {
                 ASSERT_TRUE(slotStatusResponse.portInfo.size() > 0);
-                ASSERT_TRUE(slotStatusResponse.portInfo[0].portActive);
+                for (const SimPortInfo& simPortInfo : slotStatusResponse.portInfo) {
+                    if (simPortInfo.portActive) {
+                        simCount++;
+                    }
+                }
             }
         }
+        if (isSsSsEnabled()) {
+            EXPECT_EQ(1, simCount);
+        } else if (isDsDsEnabled()) {
+            EXPECT_EQ(2, simCount);
+        } else if (isTsTsEnabled()) {
+            EXPECT_EQ(3, simCount);
+        }
     }
 }
diff --git a/radio/aidl/vts/radio_modem_response.cpp b/radio/aidl/vts/radio_modem_response.cpp
index d2715a8..20b44c5 100644
--- a/radio/aidl/vts/radio_modem_response.cpp
+++ b/radio/aidl/vts/radio_modem_response.cpp
@@ -24,6 +24,7 @@
 
 ndk::ScopedAStatus RadioModemResponse::enableModemResponse(const RadioResponseInfo& info) {
     rspInfo = info;
+    enableModemResponseToggle = !enableModemResponseToggle;
     parent_modem.notify(info.serial);
     return ndk::ScopedAStatus::ok();
 }
diff --git a/radio/aidl/vts/radio_modem_utils.h b/radio/aidl/vts/radio_modem_utils.h
index 8779e0c..49e1891 100644
--- a/radio/aidl/vts/radio_modem_utils.h
+++ b/radio/aidl/vts/radio_modem_utils.h
@@ -37,7 +37,7 @@
 
     RadioResponseInfo rspInfo;
     bool isModemEnabled;
-    bool enableModemResponseToggle;
+    bool enableModemResponseToggle = false;
 
     virtual ndk::ScopedAStatus acknowledgeRequest(int32_t serial) override;
 
diff --git a/radio/aidl/vts/radio_network_test.cpp b/radio/aidl/vts/radio_network_test.cpp
index e1d508d..0bae374 100644
--- a/radio/aidl/vts/radio_network_test.cpp
+++ b/radio/aidl/vts/radio_network_test.cpp
@@ -592,6 +592,89 @@
 }
 
 /*
+ * Test IRadioNetwork.setSignalStrengthReportingCriteria() for multi-RANs per request
+ */
+TEST_P(RadioNetworkTest, setSignalStrengthReportingCriteria_multiRansPerRequest) {
+    SignalThresholdInfo signalThresholdInfoGeran;
+    signalThresholdInfoGeran.signalMeasurement = SignalThresholdInfo::SIGNAL_MEASUREMENT_TYPE_RSSI;
+    signalThresholdInfoGeran.hysteresisMs = 5000;
+    signalThresholdInfoGeran.hysteresisDb = 2;
+    signalThresholdInfoGeran.thresholds = {-109, -103, -97, -89};
+    signalThresholdInfoGeran.isEnabled = true;
+    signalThresholdInfoGeran.ran = AccessNetwork::GERAN;
+
+    SignalThresholdInfo signalThresholdInfoUtran;
+    signalThresholdInfoUtran.signalMeasurement = SignalThresholdInfo::SIGNAL_MEASUREMENT_TYPE_RSCP;
+    signalThresholdInfoUtran.hysteresisMs = 5000;
+    signalThresholdInfoUtran.hysteresisDb = 2;
+    signalThresholdInfoUtran.thresholds = {-110, -97, -73, -49, -25};
+    signalThresholdInfoUtran.isEnabled = true;
+    signalThresholdInfoUtran.ran = AccessNetwork::UTRAN;
+
+    SignalThresholdInfo signalThresholdInfoEutran;
+    signalThresholdInfoEutran.signalMeasurement = SignalThresholdInfo::SIGNAL_MEASUREMENT_TYPE_RSRP;
+    signalThresholdInfoEutran.hysteresisMs = 5000;
+    signalThresholdInfoEutran.hysteresisDb = 2;
+    signalThresholdInfoEutran.thresholds = {-128, -108, -88, -68};
+    signalThresholdInfoEutran.isEnabled = true;
+    signalThresholdInfoEutran.ran = AccessNetwork::EUTRAN;
+
+    SignalThresholdInfo signalThresholdInfoCdma2000;
+    signalThresholdInfoCdma2000.signalMeasurement =
+            SignalThresholdInfo::SIGNAL_MEASUREMENT_TYPE_RSSI;
+    signalThresholdInfoCdma2000.hysteresisMs = 5000;
+    signalThresholdInfoCdma2000.hysteresisDb = 2;
+    signalThresholdInfoCdma2000.thresholds = {-105, -90, -75, -65};
+    signalThresholdInfoCdma2000.isEnabled = true;
+    signalThresholdInfoCdma2000.ran = AccessNetwork::CDMA2000;
+
+    SignalThresholdInfo signalThresholdInfoNgran;
+    signalThresholdInfoNgran.signalMeasurement =
+            SignalThresholdInfo::SIGNAL_MEASUREMENT_TYPE_SSRSRP;
+    signalThresholdInfoNgran.hysteresisMs = 5000;
+    signalThresholdInfoNgran.hysteresisDb = 0;
+    signalThresholdInfoNgran.thresholds = {-105, -90, -75, -65};
+    signalThresholdInfoNgran.isEnabled = true;
+    signalThresholdInfoNgran.ran = AccessNetwork::NGRAN;
+
+    const static std::vector<SignalThresholdInfo> candidateSignalThresholdInfos = {
+            signalThresholdInfoGeran, signalThresholdInfoUtran, signalThresholdInfoEutran,
+            signalThresholdInfoCdma2000, signalThresholdInfoNgran};
+
+    std::vector<SignalThresholdInfo> supportedSignalThresholdInfos;
+    for (size_t i = 0; i < candidateSignalThresholdInfos.size(); i++) {
+        serial = GetRandomSerialNumber();
+        ndk::ScopedAStatus res = radio_network->setSignalStrengthReportingCriteria(
+                serial, {candidateSignalThresholdInfos[i]});
+        ASSERT_OK(res);
+        EXPECT_EQ(std::cv_status::no_timeout, wait());
+        if (radioRsp_network->rspInfo.error == RadioError::NONE) {
+            supportedSignalThresholdInfos.push_back(signalThresholdInfoGeran);
+        } else {
+            // Refer to IRadioNetworkResponse#setSignalStrengthReportingCriteriaResponse
+            ASSERT_TRUE(CheckAnyOfErrors(
+                    radioRsp_network->rspInfo.error,
+                    {RadioError::INVALID_ARGUMENTS, RadioError::RADIO_NOT_AVAILABLE}));
+        }
+    }
+
+    ASSERT_FALSE(supportedSignalThresholdInfos.empty());
+
+    serial = GetRandomSerialNumber();
+    ndk::ScopedAStatus res = radio_network->setSignalStrengthReportingCriteria(
+            serial, supportedSignalThresholdInfos);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_network->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_network->rspInfo.serial);
+
+    ALOGI("setSignalStrengthReportingCriteria_multiRansPerRequest, rspInfo.error = %s\n",
+          toString(radioRsp_network->rspInfo.error).c_str());
+
+    ASSERT_TRUE(CheckAnyOfErrors(radioRsp_network->rspInfo.error, {RadioError::NONE}));
+}
+
+/*
  * Test IRadioNetwork.setLinkCapacityReportingCriteria() invalid hysteresisDlKbps
  */
 TEST_P(RadioNetworkTest, setLinkCapacityReportingCriteria_invalidHysteresisDlKbps) {
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
index ca89555..c30c183 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
@@ -242,7 +242,8 @@
      *   not a multiple of the AES block size, finish() must return
      *   ErrorCode::INVALID_INPUT_LENGTH.  If padding is PaddingMode::PKCS7, pad the data per the
      *   PKCS#7 specification, including adding an additional padding block if the data is a
-     *   multiple of the block length.
+     *   multiple of the block length.  If padding is PaddingMode::PKCS7 and decryption does not
+     *   result in valid padding, return ErrorCode::INVALID_ARGUMENT.
      *
      * o BlockMode::GCM.  During encryption, after processing all plaintext, compute the tag
      *   (Tag::MAC_LENGTH bytes) and append it to the returned ciphertext.  During decryption,
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index e73f46c..cbe4512 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -5481,18 +5481,45 @@
 
         EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
         string plaintext;
-        ErrorCode error = Finish(message, &plaintext);
-        if (error == ErrorCode::INVALID_INPUT_LENGTH) {
+        ErrorCode error = Finish(ciphertext, &plaintext);
+        if (error == ErrorCode::INVALID_ARGUMENT) {
             // This is the expected error, we can exit the test now.
             return;
         } else {
             // Very small chance we got valid decryption, so try again.
-            ASSERT_EQ(error, ErrorCode::OK);
+            ASSERT_EQ(error, ErrorCode::OK)
+                    << "Expected INVALID_ARGUMENT or (rarely) OK, got " << error;
         }
     }
     FAIL() << "Corrupt ciphertext should have failed to decrypt by now.";
 }
 
+/*
+ * EncryptionOperationsTest.AesEcbPkcs7CiphertextTooShort
+ *
+ * Verifies that AES decryption fails in the correct way when the padding is corrupted.
+ */
+TEST_P(EncryptionOperationsTest, AesEcbPkcs7CiphertextTooShort) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+                                                 .Padding(PaddingMode::PKCS7)));
+
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+
+    string message = "a";
+    string ciphertext = EncryptMessage(message, params);
+    EXPECT_EQ(16U, ciphertext.size());
+    EXPECT_NE(ciphertext, message);
+
+    // Shorten the ciphertext.
+    ciphertext.resize(ciphertext.size() - 1);
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+    string plaintext;
+    EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(ciphertext, &plaintext));
+}
+
 vector<uint8_t> CopyIv(const AuthorizationSet& set) {
     auto iv = set.GetTagValue(TAG_NONCE);
     EXPECT_TRUE(iv);
diff --git a/sensors/aidl/android/hardware/sensors/ISensors.aidl b/sensors/aidl/android/hardware/sensors/ISensors.aidl
index 2ac1884..2c68489 100644
--- a/sensors/aidl/android/hardware/sensors/ISensors.aidl
+++ b/sensors/aidl/android/hardware/sensors/ISensors.aidl
@@ -229,8 +229,7 @@
      *
      * @param mem shared memory info data structure.
      * @param out channelHandle The registered channel handle.
-     * @return The direct channel handle, which is positive if successfully registered, and -1
-     *         otherwise.
+     * @return The direct channel handle, which is positive if successfully registered.
      * @return Status::ok on success
      *         EX_ILLEGAL_ARGUMENT if the shared memory information is not consistent.
      *         EX_UNSUPPORTED_OPERATION if this functionality is unsupported.
@@ -245,7 +244,7 @@
      * @see OperationMode
      * @param mode The operation mode.
      * @return Status::ok on success
-     *         EX_UNSUPPORTED_OPERATION if requested mode is not supported.
+     *         EX_UNSUPPORTED_OPERATION or EX_ILLEGAL_ARGUMENT if requested mode is not supported.
      *         EX_SECURITY if the operation is not allowed.
      */
     void setOperationMode(in OperationMode mode);
diff --git a/sensors/aidl/default/multihal/HalProxyAidl.cpp b/sensors/aidl/default/multihal/HalProxyAidl.cpp
index 64805e6..e6bcdad 100644
--- a/sensors/aidl/default/multihal/HalProxyAidl.cpp
+++ b/sensors/aidl/default/multihal/HalProxyAidl.cpp
@@ -29,6 +29,7 @@
 using ::aidl::android::hardware::sensors::ISensors;
 using ::aidl::android::hardware::sensors::ISensorsCallback;
 using ::android::hardware::sensors::V2_1::implementation::convertToOldEvent;
+using ::ndk::ScopedAStatus;
 
 namespace aidl {
 namespace android {
@@ -36,19 +37,22 @@
 namespace sensors {
 namespace implementation {
 
-static binder_status_t resultToBinderStatus(::android::hardware::sensors::V1_0::Result result) {
-    switch (result) {
-        case ::android::hardware::sensors::V1_0::Result::OK:
-            return STATUS_OK;
-        case ::android::hardware::sensors::V1_0::Result::PERMISSION_DENIED:
-            return STATUS_PERMISSION_DENIED;
-        case ::android::hardware::sensors::V1_0::Result::NO_MEMORY:
-            return STATUS_NO_MEMORY;
-        case ::android::hardware::sensors::V1_0::Result::BAD_VALUE:
-            return STATUS_BAD_VALUE;
-        case ::android::hardware::sensors::V1_0::Result::INVALID_OPERATION:
-            return STATUS_INVALID_OPERATION;
-    }
+static ScopedAStatus
+resultToAStatus(::android::hardware::sensors::V1_0::Result result) {
+  switch (result) {
+  case ::android::hardware::sensors::V1_0::Result::OK:
+    return ScopedAStatus::ok();
+  case ::android::hardware::sensors::V1_0::Result::PERMISSION_DENIED:
+    return ScopedAStatus::fromExceptionCode(EX_SECURITY);
+  case ::android::hardware::sensors::V1_0::Result::NO_MEMORY:
+    return ScopedAStatus::fromServiceSpecificError(ISensors::ERROR_NO_MEMORY);
+  case ::android::hardware::sensors::V1_0::Result::BAD_VALUE:
+    return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+  case ::android::hardware::sensors::V1_0::Result::INVALID_OPERATION:
+    return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+  default:
+    return ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
+  }
 }
 
 static ::android::hardware::sensors::V1_0::RateLevel convertRateLevel(
@@ -62,6 +66,8 @@
             return ::android::hardware::sensors::V1_0::RateLevel::FAST;
         case ISensors::RateLevel::VERY_FAST:
             return ::android::hardware::sensors::V1_0::RateLevel::VERY_FAST;
+        default:
+          assert(false);
     }
 }
 
@@ -72,6 +78,8 @@
             return ::android::hardware::sensors::V1_0::OperationMode::NORMAL;
         case ISensors::OperationMode::DATA_INJECTION:
             return ::android::hardware::sensors::V1_0::OperationMode::DATA_INJECTION;
+        default:
+          assert(false);
     }
 }
 
@@ -82,6 +90,8 @@
             return ::android::hardware::sensors::V1_0::SharedMemType::ASHMEM;
         case ISensors::SharedMemInfo::SharedMemType::GRALLOC:
             return ::android::hardware::sensors::V1_0::SharedMemType::GRALLOC;
+        default:
+          assert(false);
     }
 }
 
@@ -90,6 +100,8 @@
     switch (sharedMemFormat) {
         case ISensors::SharedMemInfo::SharedMemFormat::SENSORS_EVENT:
             return ::android::hardware::sensors::V1_0::SharedMemFormat::SENSORS_EVENT;
+        default:
+          assert(false);
     }
 }
 
@@ -104,106 +116,125 @@
     return v1SharedMemInfo;
 }
 
-::ndk::ScopedAStatus HalProxyAidl::activate(int32_t in_sensorHandle, bool in_enabled) {
-    return ndk::ScopedAStatus::fromStatus(
-            resultToBinderStatus(HalProxy::activate(in_sensorHandle, in_enabled)));
+ScopedAStatus HalProxyAidl::activate(int32_t in_sensorHandle, bool in_enabled) {
+  return resultToAStatus(HalProxy::activate(in_sensorHandle, in_enabled));
 }
 
-::ndk::ScopedAStatus HalProxyAidl::batch(int32_t in_sensorHandle, int64_t in_samplingPeriodNs,
-                                         int64_t in_maxReportLatencyNs) {
-    return ndk::ScopedAStatus::fromStatus(resultToBinderStatus(
-            HalProxy::batch(in_sensorHandle, in_samplingPeriodNs, in_maxReportLatencyNs)));
+ScopedAStatus HalProxyAidl::batch(int32_t in_sensorHandle,
+                                  int64_t in_samplingPeriodNs,
+                                  int64_t in_maxReportLatencyNs) {
+  return resultToAStatus(HalProxy::batch(in_sensorHandle, in_samplingPeriodNs,
+                                         in_maxReportLatencyNs));
 }
 
-::ndk::ScopedAStatus HalProxyAidl::configDirectReport(int32_t in_sensorHandle,
-                                                      int32_t in_channelHandle,
-                                                      ISensors::RateLevel in_rate,
-                                                      int32_t* _aidl_return) {
-    binder_status_t binderStatus;
-    HalProxy::configDirectReport(
-            in_sensorHandle, in_channelHandle, convertRateLevel(in_rate),
-            [&binderStatus, _aidl_return](::android::hardware::sensors::V1_0::Result result,
-                                          int32_t reportToken) {
-                binderStatus = resultToBinderStatus(result);
-                *_aidl_return = reportToken;
-            });
-    return ndk::ScopedAStatus::fromStatus(binderStatus);
+ScopedAStatus HalProxyAidl::configDirectReport(int32_t in_sensorHandle,
+                                               int32_t in_channelHandle,
+                                               ISensors::RateLevel in_rate,
+                                               int32_t *_aidl_return) {
+  ScopedAStatus status =
+      ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+  HalProxy::configDirectReport(
+      in_sensorHandle, in_channelHandle, convertRateLevel(in_rate),
+      [&status, _aidl_return](::android::hardware::sensors::V1_0::Result result,
+                              int32_t reportToken) {
+        status = resultToAStatus(result);
+        *_aidl_return = reportToken;
+      });
+
+  return status;
 }
 
-::ndk::ScopedAStatus HalProxyAidl::flush(int32_t in_sensorHandle) {
-    return ndk::ScopedAStatus::fromStatus(resultToBinderStatus(HalProxy::flush(in_sensorHandle)));
+ScopedAStatus HalProxyAidl::flush(int32_t in_sensorHandle) {
+  return resultToAStatus(HalProxy::flush(in_sensorHandle));
 }
 
-::ndk::ScopedAStatus HalProxyAidl::getSensorsList(
-        std::vector<::aidl::android::hardware::sensors::SensorInfo>* _aidl_return) {
-    for (const auto& sensor : HalProxy::getSensors()) {
-        _aidl_return->push_back(convertSensorInfo(sensor.second));
-    }
-    return ndk::ScopedAStatus::ok();
+ScopedAStatus HalProxyAidl::getSensorsList(
+    std::vector<::aidl::android::hardware::sensors::SensorInfo> *_aidl_return) {
+  for (const auto &sensor : HalProxy::getSensors()) {
+    _aidl_return->push_back(convertSensorInfo(sensor.second));
+  }
+  return ScopedAStatus::ok();
 }
 
-::ndk::ScopedAStatus HalProxyAidl::initialize(
-        const MQDescriptor<::aidl::android::hardware::sensors::Event, SynchronizedReadWrite>&
-                in_eventQueueDescriptor,
-        const MQDescriptor<int32_t, SynchronizedReadWrite>& in_wakeLockDescriptor,
-        const std::shared_ptr<ISensorsCallback>& in_sensorsCallback) {
-    ::android::sp<::android::hardware::sensors::V2_1::implementation::ISensorsCallbackWrapperBase>
-            dynamicCallback = new ISensorsCallbackWrapperAidl(in_sensorsCallback);
+ScopedAStatus HalProxyAidl::initialize(
+    const MQDescriptor<::aidl::android::hardware::sensors::Event,
+                       SynchronizedReadWrite> &in_eventQueueDescriptor,
+    const MQDescriptor<int32_t, SynchronizedReadWrite> &in_wakeLockDescriptor,
+    const std::shared_ptr<ISensorsCallback> &in_sensorsCallback) {
+  ::android::sp<::android::hardware::sensors::V2_1::implementation::
+                    ISensorsCallbackWrapperBase>
+      dynamicCallback = new ISensorsCallbackWrapperAidl(in_sensorsCallback);
 
-    auto aidlEventQueue =
-            std::make_unique<::android::AidlMessageQueue<::aidl::android::hardware::sensors::Event,
-                                                         SynchronizedReadWrite>>(
-                    in_eventQueueDescriptor, true /* resetPointers */);
-    std::unique_ptr<
-            ::android::hardware::sensors::V2_1::implementation::EventMessageQueueWrapperBase>
-            eventQueue = std::make_unique<EventMessageQueueWrapperAidl>(aidlEventQueue);
+  auto aidlEventQueue = std::make_unique<::android::AidlMessageQueue<
+      ::aidl::android::hardware::sensors::Event, SynchronizedReadWrite>>(
+      in_eventQueueDescriptor, true /* resetPointers */);
+  std::unique_ptr<::android::hardware::sensors::V2_1::implementation::
+                      EventMessageQueueWrapperBase>
+      eventQueue =
+          std::make_unique<EventMessageQueueWrapperAidl>(aidlEventQueue);
 
-    auto aidlWakeLockQueue =
-            std::make_unique<::android::AidlMessageQueue<int32_t, SynchronizedReadWrite>>(
-                    in_wakeLockDescriptor, true /* resetPointers */);
-    std::unique_ptr<
-            ::android::hardware::sensors::V2_1::implementation::WakeLockMessageQueueWrapperBase>
-            wakeLockQueue = std::make_unique<WakeLockMessageQueueWrapperAidl>(aidlWakeLockQueue);
+  auto aidlWakeLockQueue = std::make_unique<
+      ::android::AidlMessageQueue<int32_t, SynchronizedReadWrite>>(
+      in_wakeLockDescriptor, true /* resetPointers */);
+  std::unique_ptr<::android::hardware::sensors::V2_1::implementation::
+                      WakeLockMessageQueueWrapperBase>
+      wakeLockQueue =
+          std::make_unique<WakeLockMessageQueueWrapperAidl>(aidlWakeLockQueue);
 
-    return ndk::ScopedAStatus::fromStatus(
-            resultToBinderStatus(initializeCommon(eventQueue, wakeLockQueue, dynamicCallback)));
+  return resultToAStatus(
+      initializeCommon(eventQueue, wakeLockQueue, dynamicCallback));
 }
 
-::ndk::ScopedAStatus HalProxyAidl::injectSensorData(
-        const ::aidl::android::hardware::sensors::Event& in_event) {
-    ::android::hardware::sensors::V2_1::Event hidlEvent;
-    convertToHidlEvent(in_event, &hidlEvent);
-    return ndk::ScopedAStatus::fromStatus(
-            resultToBinderStatus(HalProxy::injectSensorData(convertToOldEvent(hidlEvent))));
+ScopedAStatus HalProxyAidl::injectSensorData(
+    const ::aidl::android::hardware::sensors::Event &in_event) {
+  ::android::hardware::sensors::V2_1::Event hidlEvent;
+  convertToHidlEvent(in_event, &hidlEvent);
+  return resultToAStatus(
+      HalProxy::injectSensorData(convertToOldEvent(hidlEvent)));
 }
 
-::ndk::ScopedAStatus HalProxyAidl::registerDirectChannel(const ISensors::SharedMemInfo& in_mem,
-                                                         int32_t* _aidl_return) {
-    binder_status_t binderStatus;
-    ::android::hardware::sensors::V1_0::SharedMemInfo sharedMemInfo = convertSharedMemInfo(in_mem);
+ScopedAStatus
+HalProxyAidl::registerDirectChannel(const ISensors::SharedMemInfo &in_mem,
+                                    int32_t *_aidl_return) {
+  ScopedAStatus status =
+      ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+  ::android::hardware::sensors::V1_0::SharedMemInfo sharedMemInfo =
+      convertSharedMemInfo(in_mem);
 
-    HalProxy::registerDirectChannel(
-            sharedMemInfo,
-            [&binderStatus, _aidl_return](::android::hardware::sensors::V1_0::Result result,
-                                          int32_t reportToken) {
-                binderStatus = resultToBinderStatus(result);
-                *_aidl_return = reportToken;
-            });
+  HalProxy::registerDirectChannel(
+      sharedMemInfo,
+      [&status, _aidl_return](::android::hardware::sensors::V1_0::Result result,
+                              int32_t reportToken) {
+        status = resultToAStatus(result);
+        *_aidl_return = reportToken;
+      });
 
-    native_handle_delete(
-            const_cast<native_handle_t*>(sharedMemInfo.memoryHandle.getNativeHandle()));
-    return ndk::ScopedAStatus::fromStatus(binderStatus);
+  native_handle_delete(const_cast<native_handle_t *>(
+      sharedMemInfo.memoryHandle.getNativeHandle()));
+
+  return status;
 }
 
-::ndk::ScopedAStatus HalProxyAidl::setOperationMode(
-        ::aidl::android::hardware::sensors::ISensors::OperationMode in_mode) {
-    return ndk::ScopedAStatus::fromStatus(
-            resultToBinderStatus(HalProxy::setOperationMode(convertOperationMode(in_mode))));
+ScopedAStatus HalProxyAidl::setOperationMode(
+    ::aidl::android::hardware::sensors::ISensors::OperationMode in_mode) {
+  return resultToAStatus(
+      HalProxy::setOperationMode(convertOperationMode(in_mode)));
 }
 
-::ndk::ScopedAStatus HalProxyAidl::unregisterDirectChannel(int32_t in_channelHandle) {
-    return ndk::ScopedAStatus::fromStatus(
-            resultToBinderStatus(HalProxy::unregisterDirectChannel(in_channelHandle)));
+ScopedAStatus HalProxyAidl::unregisterDirectChannel(int32_t in_channelHandle) {
+  return resultToAStatus(HalProxy::unregisterDirectChannel(in_channelHandle));
+}
+
+binder_status_t HalProxyAidl::dump(int fd, const char ** /* args */,
+                                   uint32_t /* numArgs */) {
+  native_handle_t *nativeHandle =
+      native_handle_create(1 /* numFds */, 0 /* numInts */);
+  nativeHandle->data[0] = fd;
+
+  HalProxy::debug(nativeHandle, {} /* args */);
+
+  native_handle_delete(nativeHandle);
+  return STATUS_OK;
 }
 
 }  // namespace implementation
diff --git a/sensors/aidl/default/multihal/include/HalProxyAidl.h b/sensors/aidl/default/multihal/include/HalProxyAidl.h
index 7401726..5c81715 100644
--- a/sensors/aidl/default/multihal/include/HalProxyAidl.h
+++ b/sensors/aidl/default/multihal/include/HalProxyAidl.h
@@ -55,6 +55,8 @@
     ::ndk::ScopedAStatus setOperationMode(
             ::aidl::android::hardware::sensors::ISensors::OperationMode in_mode) override;
     ::ndk::ScopedAStatus unregisterDirectChannel(int32_t in_channelHandle) override;
+
+    binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
 };
 
 }  // namespace implementation
diff --git a/sensors/aidl/vts/VtsAidlHalSensorsTargetTest.cpp b/sensors/aidl/vts/VtsAidlHalSensorsTargetTest.cpp
index 83d0dc9..d536e29 100644
--- a/sensors/aidl/vts/VtsAidlHalSensorsTargetTest.cpp
+++ b/sensors/aidl/vts/VtsAidlHalSensorsTargetTest.cpp
@@ -599,10 +599,12 @@
         ASSERT_TRUE(getSensors()->setOperationMode(ISensors::OperationMode::DATA_INJECTION).isOk());
         ASSERT_TRUE(getSensors()->setOperationMode(ISensors::OperationMode::NORMAL).isOk());
     } else {
-        ASSERT_EQ(getSensors()
-                          ->setOperationMode(ISensors::OperationMode::DATA_INJECTION)
-                          .getExceptionCode(),
-                  EX_UNSUPPORTED_OPERATION);
+      int errorCode =
+          getSensors()
+              ->setOperationMode(ISensors::OperationMode::DATA_INJECTION)
+              .getExceptionCode();
+      ASSERT_TRUE((errorCode == EX_UNSUPPORTED_OPERATION) ||
+                  (errorCode == EX_ILLEGAL_ARGUMENT));
     }
 }
 
@@ -938,10 +940,10 @@
     if (isDirectReportRateSupported(sensor, rateLevel)) {
         ASSERT_TRUE(status.isOk());
         if (rateLevel != ISensors::RateLevel::STOP) {
-            ASSERT_GT(*reportToken, 0);
-        } else {
-            ASSERT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+          ASSERT_GT(*reportToken, 0);
         }
+    } else {
+      ASSERT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT);
     }
 }
 
@@ -982,11 +984,15 @@
     ::ndk::ScopedAStatus status = registerDirectChannel(mem->getSharedMemInfo(), &channelHandle);
     if (supportsSharedMemType) {
         ASSERT_TRUE(status.isOk());
-        ASSERT_EQ(channelHandle, 0);
+        ASSERT_GT(channelHandle, 0);
+
+        // Verify that the memory has been zeroed
+        for (size_t i = 0; i < mem->getSize(); i++) {
+          ASSERT_EQ(buffer[i], 0x00);
+        }
     } else {
         int32_t error = supportsAnyDirectChannel ? EX_ILLEGAL_ARGUMENT : EX_UNSUPPORTED_OPERATION;
         ASSERT_EQ(status.getExceptionCode(), error);
-        ASSERT_EQ(channelHandle, -1);
     }
     *directChannelHandle = channelHandle;
 }
@@ -1038,7 +1044,7 @@
         // Verify that a sensor handle of -1 is only acceptable when using RateLevel::STOP
         ndk::ScopedAStatus status = configDirectReport(-1 /* sensorHandle */, directChannelHandle,
                                                        ISensors::RateLevel::NORMAL, &reportToken);
-        ASSERT_EQ(status.getServiceSpecificError(), android::BAD_VALUE);
+        ASSERT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT);
 
         status = configDirectReport(-1 /* sensorHandle */, directChannelHandle,
                                     ISensors::RateLevel::STOP, &reportToken);
diff --git a/uwb/aidl/Android.bp b/uwb/aidl/Android.bp
index 2cc1e6a..36cde9b 100755
--- a/uwb/aidl/Android.bp
+++ b/uwb/aidl/Android.bp
@@ -14,6 +14,7 @@
     vendor_available: true,
     srcs: ["android/hardware/uwb/*.aidl"],
     stability: "vintf",
+    host_supported: true,
     backend: {
         java: {
             sdk_version: "module_Tiramisu",
diff --git a/vibrator/aidl/default/Android.bp b/vibrator/aidl/default/Android.bp
index acdbdcd..c4140be 100644
--- a/vibrator/aidl/default/Android.bp
+++ b/vibrator/aidl/default/Android.bp
@@ -63,7 +63,6 @@
         "libbinder_random_parcel",
         "libcutils",
         "liblog",
-        "libutils",
         "libvibratorexampleimpl",
     ],
     target: {
@@ -71,12 +70,14 @@
             shared_libs: [
                 "libbinder_ndk",
                 "libbinder",
+                "libutils",
             ],
         },
         host: {
             static_libs: [
                 "libbinder_ndk",
                 "libbinder",
+                "libutils",
             ],
         },
         darwin: {
diff --git a/wifi/1.6/default/wifi_legacy_hal.cpp b/wifi/1.6/default/wifi_legacy_hal.cpp
index 8a75fd8..bb8cf59 100644
--- a/wifi/1.6/default/wifi_legacy_hal.cpp
+++ b/wifi/1.6/default/wifi_legacy_hal.cpp
@@ -1549,13 +1549,14 @@
 
 std::pair<wifi_error, wifi_radio_combination_matrix*>
 WifiLegacyHal::getSupportedRadioCombinationsMatrix() {
-    std::array<char, kMaxSupportedRadioCombinationsMatrixLength> buffer;
-    buffer.fill(0);
+    char* buffer = new char[kMaxSupportedRadioCombinationsMatrixLength];
+    std::fill(buffer, buffer + kMaxSupportedRadioCombinationsMatrixLength, 0);
     uint32_t size = 0;
     wifi_radio_combination_matrix* radio_combination_matrix_ptr =
-            reinterpret_cast<wifi_radio_combination_matrix*>(buffer.data());
+            reinterpret_cast<wifi_radio_combination_matrix*>(buffer);
     wifi_error status = global_func_table_.wifi_get_supported_radio_combinations_matrix(
-            global_handle_, buffer.size(), &size, radio_combination_matrix_ptr);
+            global_handle_, kMaxSupportedRadioCombinationsMatrixLength, &size,
+            radio_combination_matrix_ptr);
     CHECK(size >= 0 && size <= kMaxSupportedRadioCombinationsMatrixLength);
     return {status, radio_combination_matrix_ptr};
 }