Merge changes from topic "default_vhal_emulated_user"

* changes:
  Move emulatedUserHal to default VHAL.
  Add fake OBD2 frame logic to default VHAL.
  Support vendor override in default VHAL.
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index 2e8ca66..2c3422e 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -92,10 +92,12 @@
         "impl/vhal_v2_0/LinearFakeValueGenerator.cpp",
         "impl/vhal_v2_0/JsonFakeValueGenerator.cpp",
         "impl/vhal_v2_0/GeneratorHub.cpp",
+        "impl/vhal_v2_0/FakeObd2Frame.cpp",
     ],
     local_include_dirs: ["common/include/vhal_v2_0"],
     export_include_dirs: ["impl"],
     whole_static_libs: [
+        "android.hardware.automotive.vehicle@2.0-emulated-user-hal-lib",
         "android.hardware.automotive.vehicle@2.0-manager-lib",
     ],
     shared_libs: [
@@ -141,10 +143,10 @@
         "impl/vhal_v2_0/JsonFakeValueGenerator.cpp",
         "impl/vhal_v2_0/LinearFakeValueGenerator.cpp",
         "impl/vhal_v2_0/DefaultVehicleHalServer.cpp",
+        "impl/vhal_v2_0/FakeObd2Frame.cpp",
     ],
     whole_static_libs: [
         "android.hardware.automotive.vehicle@2.0-server-common-lib",
-        "android.hardware.automotive.vehicle@2.0-user-hal-helper-lib",
     ],
     static_libs: [
         "android.hardware.automotive.vehicle@2.0-libproto-native",
@@ -213,7 +215,10 @@
         "android.hardware.automotive.vehicle@2.0-default-impl-lib",
         "android.hardware.automotive.vehicle@2.0-libproto-native",
     ],
-    data: [":vhal_test_json"],
+    data: [
+        ":vhal_test_json",
+        ":vhal_test_override_json",
+    ],
     test_suites: ["general-tests"],
 }
 
diff --git a/automotive/vehicle/2.0/default/VehicleService.cpp b/automotive/vehicle/2.0/default/VehicleService.cpp
index ba2a606..fae6e43 100644
--- a/automotive/vehicle/2.0/default/VehicleService.cpp
+++ b/automotive/vehicle/2.0/default/VehicleService.cpp
@@ -20,7 +20,6 @@
 
 #include <iostream>
 
-#include <EmulatedUserHal.h>
 #include <EmulatedVehicleConnector.h>
 #include <EmulatedVehicleHal.h>
 #include <vhal_v2_0/VehicleHalManager.h>
@@ -32,8 +31,7 @@
 int main(int /* argc */, char* /* argv */ []) {
     auto store = std::make_unique<VehiclePropertyStore>();
     auto connector = std::make_unique<impl::EmulatedVehicleConnector>();
-    auto userHal = connector->getEmulatedUserHal();
-    auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get(), connector.get(), userHal);
+    auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get(), connector.get());
     auto emulator = connector->getEmulator();
     auto service = std::make_unique<VehicleHalManager>(hal.get());
     connector->setValuePool(hal->getValuePool());
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp
index 1b37cbd..25a698b 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp
@@ -17,10 +17,14 @@
 
 #include <android-base/chrono_utils.h>
 #include <assert.h>
+#include <stdio.h>
 #include <utils/Log.h>
 #include <utils/SystemClock.h>
 #include <vhal_v2_0/RecurrentTimer.h>
+#include <unordered_set>
 
+#include "FakeObd2Frame.h"
+#include "PropertyUtils.h"
 #include "VehicleUtils.h"
 
 #include "DefaultVehicleHal.h"
@@ -52,8 +56,30 @@
     }
     return nullptr;
 }
+
+VehicleHal::VehiclePropValuePtr addTimestamp(VehicleHal::VehiclePropValuePtr v) {
+    if (v.get()) {
+        v->timestamp = elapsedRealtimeNano();
+    }
+    return v;
+}
+
+bool isDebugProperty(int propId) {
+    return (propId == kGenerateFakeDataControllingProperty ||
+            propId == kSetIntPropertyFromVehicleForTest ||
+            propId == kSetFloatPropertyFromVehicleForTest ||
+            propId == kSetBooleanPropertyFromVehicleForTest);
+}
 }  // namespace
 
+VehicleHal::VehiclePropValuePtr DefaultVehicleHal::createVhalHeartBeatProp() {
+    VehicleHal::VehiclePropValuePtr v = getValuePool()->obtainInt64(uptimeMillis());
+    v->prop = static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT);
+    v->areaId = 0;
+    v->status = VehiclePropertyStatus::AVAILABLE;
+    return v;
+}
+
 DefaultVehicleHal::DefaultVehicleHal(VehiclePropertyStore* propStore, VehicleHalClient* client)
     : mPropStore(propStore), mRecurrentTimer(getTimerAction()), mVehicleClient(client) {
     initStaticConfig();
@@ -63,12 +89,51 @@
             });
 }
 
+VehicleHal::VehiclePropValuePtr DefaultVehicleHal::getUserHalProp(
+        const VehiclePropValue& requestedPropValue, StatusCode* outStatus) {
+    auto propId = requestedPropValue.prop;
+    ALOGI("get(): getting value for prop %d from User HAL", propId);
+    const auto& ret = mEmulatedUserHal.onGetProperty(requestedPropValue);
+    VehicleHal::VehiclePropValuePtr v = nullptr;
+    if (!ret.ok()) {
+        ALOGE("get(): User HAL returned error: %s", ret.error().message().c_str());
+        *outStatus = StatusCode(ret.error().code());
+    } else {
+        auto value = ret.value().get();
+        if (value != nullptr) {
+            ALOGI("get(): User HAL returned value: %s", toString(*value).c_str());
+            v = getValuePool()->obtain(*value);
+            *outStatus = StatusCode::OK;
+        } else {
+            ALOGE("get(): User HAL returned null value");
+            *outStatus = StatusCode::INTERNAL_ERROR;
+        }
+    }
+    return addTimestamp(std::move(v));
+}
+
 VehicleHal::VehiclePropValuePtr DefaultVehicleHal::get(const VehiclePropValue& requestedPropValue,
                                                        StatusCode* outStatus) {
     auto propId = requestedPropValue.prop;
     ALOGV("get(%d)", propId);
 
+    if (mEmulatedUserHal.isSupported(propId)) {
+        return getUserHalProp(requestedPropValue, outStatus);
+    }
+
     VehiclePropValuePtr v = nullptr;
+    if (propId == OBD2_FREEZE_FRAME) {
+        v = getValuePool()->obtainComplex();
+        *outStatus = fillObd2FreezeFrame(mPropStore, requestedPropValue, v.get());
+        return addTimestamp(std::move(v));
+    }
+
+    if (propId == OBD2_FREEZE_FRAME_INFO) {
+        v = getValuePool()->obtainComplex();
+        *outStatus = fillObd2DtcInfo(mPropStore, v.get());
+        return addTimestamp(std::move(v));
+    }
+
     auto internalPropValue = mPropStore->readValueOrNull(requestedPropValue);
     if (internalPropValue != nullptr) {
         v = getValuePool()->obtain(*internalPropValue);
@@ -81,10 +146,7 @@
     } else {
         *outStatus = StatusCode::TRY_AGAIN;
     }
-    if (v.get()) {
-        v->timestamp = elapsedRealtimeNano();
-    }
-    return v;
+    return addTimestamp(std::move(v));
 }
 
 std::vector<VehiclePropConfig> DefaultVehicleHal::listProperties() {
@@ -92,6 +154,36 @@
 }
 
 bool DefaultVehicleHal::dump(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
+    int nativeFd = fd->data[0];
+    if (nativeFd < 0) {
+        ALOGW("Invalid fd from HIDL handle: %d", nativeFd);
+        return false;
+    }
+    if (options.size() > 0) {
+        if (options[0] == "--help") {
+            std::string buffer;
+            buffer += "Emulated user hal usage:\n";
+            buffer += mEmulatedUserHal.showDumpHelp();
+            buffer += "\n";
+            buffer += "VHAL server debug usage:\n";
+            buffer += "--debughal: send debug command to VHAL server, see '--debughal --help'\n";
+            buffer += "\n";
+            dprintf(nativeFd, "%s", buffer.c_str());
+            return false;
+        } else if (options[0] == kUserHalDumpOption) {
+            dprintf(nativeFd, "%s", mEmulatedUserHal.dump("").c_str());
+            return false;
+        }
+    } else {
+        // No options, dump the emulated user hal state first and then send command to VHAL server
+        // to dump its state.
+        std::string buffer;
+        buffer += "Emulator user hal state:\n";
+        buffer += mEmulatedUserHal.dump("  ");
+        buffer += "\n";
+        dprintf(nativeFd, "%s", buffer.c_str());
+    }
+
     return mVehicleClient->dump(fd, options);
 }
 
@@ -250,6 +342,23 @@
     return StatusCode::OK;
 }
 
+StatusCode DefaultVehicleHal::setUserHalProp(const VehiclePropValue& propValue) {
+    ALOGI("onSetProperty(): property %d will be handled by UserHal", propValue.prop);
+
+    const auto& ret = mEmulatedUserHal.onSetProperty(propValue);
+    if (!ret.ok()) {
+        ALOGE("onSetProperty(): HAL returned error: %s", ret.error().message().c_str());
+        return StatusCode(ret.error().code());
+    }
+    auto updatedValue = ret.value().get();
+    if (updatedValue != nullptr) {
+        ALOGI("onSetProperty(): updating property returned by HAL: %s",
+              toString(*updatedValue).c_str());
+        onPropertyValue(*updatedValue, true);
+    }
+    return StatusCode::OK;
+}
+
 StatusCode DefaultVehicleHal::set(const VehiclePropValue& propValue) {
     if (propValue.status != VehiclePropertyStatus::AVAILABLE) {
         // Android side cannot set property status - this value is the
@@ -258,6 +367,39 @@
         return StatusCode::INVALID_ARG;
     }
 
+    if (mEmulatedUserHal.isSupported(propValue.prop)) {
+        return setUserHalProp(propValue);
+    }
+
+    std::unordered_set<int32_t> powerProps(std::begin(kHvacPowerProperties),
+                                           std::end(kHvacPowerProperties));
+    if (powerProps.count(propValue.prop)) {
+        auto hvacPowerOn = mPropStore->readValueOrNull(
+                toInt(VehicleProperty::HVAC_POWER_ON),
+                (VehicleAreaSeat::ROW_1_LEFT | VehicleAreaSeat::ROW_1_RIGHT |
+                 VehicleAreaSeat::ROW_2_LEFT | VehicleAreaSeat::ROW_2_CENTER |
+                 VehicleAreaSeat::ROW_2_RIGHT));
+
+        if (hvacPowerOn && hvacPowerOn->value.int32Values.size() == 1 &&
+            hvacPowerOn->value.int32Values[0] == 0) {
+            return StatusCode::NOT_AVAILABLE;
+        }
+    }
+
+    if (propValue.prop == OBD2_FREEZE_FRAME_CLEAR) {
+        return clearObd2FreezeFrames(mPropStore, propValue);
+    }
+    if (propValue.prop == VEHICLE_MAP_SERVICE) {
+        // Placeholder for future implementation of VMS property in the default hal. For
+        // now, just returns OK; otherwise, hal clients crash with property not supported.
+        return StatusCode::OK;
+    }
+    if (isDebugProperty(propValue.prop)) {
+        // These are special debug properties and do not need a config or check.
+        // TODO(shanyu): Remove this after we remove debug properties.
+        return mVehicleClient->setProperty(propValue, /*updateStatus=*/false);
+    }
+
     int32_t property = propValue.prop;
     const VehiclePropConfig* config = mPropStore->getConfigOrNull(property);
     if (config == nullptr) {
@@ -298,7 +440,13 @@
     auto configs = mVehicleClient->getAllPropertyConfig();
 
     for (const auto& cfg : configs) {
-        int32_t numAreas = isGlobalProp(cfg.prop) ? 0 : cfg.areaConfigs.size();
+        if (isDiagnosticProperty(cfg)) {
+            // do not write an initial empty value for the diagnostic properties
+            // as we will initialize those separately.
+            continue;
+        }
+
+        int32_t numAreas = isGlobalProp(cfg.prop) ? 1 : cfg.areaConfigs.size();
 
         for (int i = 0; i < numAreas; i++) {
             int32_t curArea = isGlobalProp(cfg.prop) ? 0 : cfg.areaConfigs[i].areaId;
@@ -315,6 +463,10 @@
     }
 
     mVehicleClient->triggerSendAllValues();
+
+    initObd2LiveFrame(mPropStore, *mPropStore->getConfigOrDie(OBD2_LIVE_FRAME));
+    initObd2FreezeFrame(mPropStore, *mPropStore->getConfigOrDie(OBD2_FREEZE_FRAME));
+
     registerHeartBeatEvent();
 }
 
@@ -344,14 +496,6 @@
     return v;
 }
 
-VehicleHal::VehiclePropValuePtr DefaultVehicleHal::createVhalHeartBeatProp() {
-    VehicleHal::VehiclePropValuePtr v = getValuePool()->obtainInt64(uptimeMillis());
-    v->prop = static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT);
-    v->areaId = 0;
-    v->status = VehiclePropertyStatus::AVAILABLE;
-    return v;
-}
-
 void DefaultVehicleHal::onContinuousPropertyTimer(const std::vector<int32_t>& properties) {
     auto& pool = *getValuePool();
 
@@ -432,7 +576,21 @@
 void DefaultVehicleHal::initStaticConfig() {
     auto configs = mVehicleClient->getAllPropertyConfig();
     for (auto&& cfg : configs) {
-        mPropStore->registerProperty(cfg, nullptr);
+        VehiclePropertyStore::TokenFunction tokenFunction = nullptr;
+
+        switch (cfg.prop) {
+            case OBD2_FREEZE_FRAME: {
+                // We use timestamp as token for OBD2_FREEZE_FRAME
+                tokenFunction = [](const VehiclePropValue& propValue) {
+                    return propValue.timestamp;
+                };
+                break;
+            }
+            default:
+                break;
+        }
+
+        mPropStore->registerProperty(cfg, tokenFunction);
     }
 }
 
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h
index af29143..7cd7ac2 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h
@@ -19,8 +19,9 @@
 
 #include <vhal_v2_0/RecurrentTimer.h>
 #include <vhal_v2_0/VehicleHal.h>
-#include "vhal_v2_0/VehiclePropertyStore.h"
+#include <vhal_v2_0/VehiclePropertyStore.h>
 
+#include "EmulatedUserHal.h"
 #include "VehicleHalClient.h"
 
 namespace android {
@@ -55,10 +56,21 @@
     VehiclePropertyStore* mPropStore;
     RecurrentTimer mRecurrentTimer;
     VehicleHalClient* mVehicleClient;
-    virtual bool isContinuousProperty(int32_t propId) const;
-    virtual void initStaticConfig();
-    virtual void onContinuousPropertyTimer(const std::vector<int32_t>& properties);
+    EmulatedUserHal mEmulatedUserHal;
+
+    // The callback that would be called when a property value is updated. This function could
+    // be extended to handle specific property update event.
     virtual void onPropertyValue(const VehiclePropValue& value, bool updateStatus);
+    // Do an internal health check, vendor should add health check logic in this function.
+    virtual VehicleHal::VehiclePropValuePtr doInternalHealthCheck();
+
+    // The callback that would be called for every event generated by 'subscribe' or heartbeat.
+    // Properties contains a list of properties that need to be handled.
+    void onContinuousPropertyTimer(const std::vector<int32_t>& properties);
+    // Initiate config for all properties, would be called during onCreate().
+    void initStaticConfig();
+    // Whether the property is a continuous property.
+    bool isContinuousProperty(int32_t propId) const;
     // Returns a lambda that could be used in mRecurrentTimer.
     RecurrentTimer::Action getTimerAction();
     // Check whether a propValue is valid according to its type.
@@ -69,8 +81,12 @@
     // Register the heart beat event to be sent every 3s. This is required to inform watch dog that
     // VHAL is alive. Subclasses should always calls this function during onCreate.
     void registerHeartBeatEvent();
-
-    VehicleHal::VehiclePropValuePtr doInternalHealthCheck();
+    // Get a user HAL property.
+    VehiclePropValuePtr getUserHalProp(const VehiclePropValue& requestedPropValue,
+                                       StatusCode* outStatus);
+    // Set a user HAL property.
+    StatusCode setUserHalProp(const VehiclePropValue& propValue);
+    // Create a VHAL heart beat property.
     VehicleHal::VehiclePropValuePtr createVhalHeartBeatProp();
 
   private:
@@ -78,6 +94,8 @@
     // See 'VehiclePropertyType' documentation in 'types.hal' for detail.
     StatusCode checkVendorMixedPropValue(const VehiclePropValue& value,
                                          const VehiclePropConfig* config);
+    // Read the override properties from a config file.
+    void getAllPropertiesOverride();
 };
 
 }  // namespace impl
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.cpp
index f61a93b..0aaa4c1 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.cpp
@@ -17,14 +17,17 @@
 #define LOG_TAG "DefaultVehicleHalServer"
 
 #include <fstream>
+#include <regex>
 
 #include <android-base/format.h>
 #include <android-base/logging.h>
 #include <android-base/parsedouble.h>
 #include <android-base/parseint.h>
+#include <android-base/properties.h>
 #include <utils/SystemClock.h>
 
 #include "DefaultConfig.h"
+#include "FakeObd2Frame.h"
 #include "JsonFakeValueGenerator.h"
 #include "LinearFakeValueGenerator.h"
 
@@ -38,6 +41,10 @@
 
 namespace impl {
 
+namespace {
+const char* VENDOR_OVERRIDE_DIR = "/vendor/etc/vhaloverride/";
+}  // namespace
+
 void DefaultVehicleHalServer::storePropInitialValue(const ConfigDeclaration& config) {
     VehiclePropConfig cfg = config.config;
 
@@ -72,8 +79,13 @@
     for (auto& it : kVehicleProperties) {
         VehiclePropConfig cfg = it.config;
         mServerSidePropStore.registerProperty(cfg);
+        // Skip diagnostic properties since there is special logic to handle those.
+        if (isDiagnosticProperty(cfg)) {
+            continue;
+        }
         storePropInitialValue(it);
     }
+    maybeOverrideProperties(VENDOR_OVERRIDE_DIR);
 }
 
 void DefaultVehicleHalServer::sendAllValuesToClient() {
@@ -559,6 +571,32 @@
     return result;
 }
 
+void DefaultVehicleHalServer::maybeOverrideProperties(const char* overrideDir) {
+    if (android::base::GetBoolProperty("persist.vendor.vhal_init_value_override", false)) {
+        overrideProperties(overrideDir);
+    }
+}
+
+void DefaultVehicleHalServer::overrideProperties(const char* overrideDir) {
+    LOG(INFO) << "loading vendor override properties from " << overrideDir;
+    if (auto dir = opendir(overrideDir)) {
+        std::regex reg_json(".*[.]json", std::regex::icase);
+        while (auto f = readdir(dir)) {
+            if (!regex_match(f->d_name, reg_json)) {
+                continue;
+            }
+            std::string file = overrideDir + std::string(f->d_name);
+            JsonFakeValueGenerator tmpGenerator(file);
+
+            std::vector<VehiclePropValue> propValues = tmpGenerator.getAllEvents();
+            for (const VehiclePropValue& prop : propValues) {
+                mServerSidePropStore.writeValue(prop, true);
+            }
+        }
+        closedir(dir);
+    }
+}
+
 }  // namespace impl
 
 }  // namespace V2_0
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.h
index aa6cf0b..5858325 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.h
@@ -72,6 +72,10 @@
     std::string getHelpInfo();
 
     DumpResult genFakeData(const std::vector<std::string>& options);
+    // If "persist.vendor.vhal_init_value_override" is true, try to override the properties default
+    // values according to JSON files in 'overrideDir'. Would be called in constructor using
+    // VENDOR_OVERRIDE_DIR as overrideDir.
+    void maybeOverrideProperties(const char* overrideDir);
 
   protected:
     GeneratorHub mGeneratorHub{
@@ -79,6 +83,12 @@
 
     VehiclePropValuePool* mValuePool{nullptr};
     VehiclePropertyStore mServerSidePropStore;
+
+  private:
+    // Expose protected methods to unit test.
+    friend class DefaultVhalImplTestHelper;
+    // Override the properties using config files in 'overrideDir'.
+    void overrideProperties(const char* overrideDir);
 };
 
 }  // namespace impl
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeObd2Frame.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeObd2Frame.cpp
new file mode 100644
index 0000000..d95584d
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeObd2Frame.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/hardware/automotive/vehicle/2.0/types.h>
+#include <utils/Log.h>
+#include <vhal_v2_0/Obd2SensorStore.h>
+#include <vhal_v2_0/PropertyUtils.h>
+#include <vhal_v2_0/VehiclePropertyStore.h>
+#include <vhal_v2_0/VehicleUtils.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+namespace {
+
+std::unique_ptr<Obd2SensorStore> fillDefaultObd2Frame(size_t numVendorIntegerSensors,
+                                                      size_t numVendorFloatSensors) {
+    std::unique_ptr<Obd2SensorStore> sensorStore(
+            new Obd2SensorStore(numVendorIntegerSensors, numVendorFloatSensors));
+
+    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::FUEL_SYSTEM_STATUS,
+                                  toInt(Obd2FuelSystemStatus::CLOSED_LOOP));
+    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::MALFUNCTION_INDICATOR_LIGHT_ON, 0);
+    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::IGNITION_MONITORS_SUPPORTED,
+                                  toInt(Obd2IgnitionMonitorKind::SPARK));
+    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::IGNITION_SPECIFIC_MONITORS,
+                                  Obd2CommonIgnitionMonitors::COMPONENTS_AVAILABLE |
+                                          Obd2CommonIgnitionMonitors::MISFIRE_AVAILABLE |
+                                          Obd2SparkIgnitionMonitors::AC_REFRIGERANT_AVAILABLE |
+                                          Obd2SparkIgnitionMonitors::EVAPORATIVE_SYSTEM_AVAILABLE);
+    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::INTAKE_AIR_TEMPERATURE, 35);
+    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::COMMANDED_SECONDARY_AIR_STATUS,
+                                  toInt(Obd2SecondaryAirStatus::FROM_OUTSIDE_OR_OFF));
+    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::NUM_OXYGEN_SENSORS_PRESENT, 1);
+    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::RUNTIME_SINCE_ENGINE_START, 500);
+    sensorStore->setIntegerSensor(
+            DiagnosticIntegerSensorIndex::DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON, 0);
+    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::WARMUPS_SINCE_CODES_CLEARED, 51);
+    sensorStore->setIntegerSensor(
+            DiagnosticIntegerSensorIndex::DISTANCE_TRAVELED_SINCE_CODES_CLEARED, 365);
+    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::ABSOLUTE_BAROMETRIC_PRESSURE, 30);
+    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::CONTROL_MODULE_VOLTAGE, 12);
+    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::AMBIENT_AIR_TEMPERATURE, 18);
+    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::MAX_FUEL_AIR_EQUIVALENCE_RATIO, 1);
+    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::FUEL_TYPE,
+                                  toInt(Obd2FuelType::GASOLINE));
+    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::CALCULATED_ENGINE_LOAD, 0.153);
+    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::SHORT_TERM_FUEL_TRIM_BANK1, -0.16);
+    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::LONG_TERM_FUEL_TRIM_BANK1, -0.16);
+    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::SHORT_TERM_FUEL_TRIM_BANK2, -0.16);
+    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::LONG_TERM_FUEL_TRIM_BANK2, -0.16);
+    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::INTAKE_MANIFOLD_ABSOLUTE_PRESSURE, 7.5);
+    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ENGINE_RPM, 1250.);
+    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::VEHICLE_SPEED, 40.);
+    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::TIMING_ADVANCE, 2.5);
+    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::THROTTLE_POSITION, 19.75);
+    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::OXYGEN_SENSOR1_VOLTAGE, 0.265);
+    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::FUEL_TANK_LEVEL_INPUT, 0.824);
+    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::EVAPORATION_SYSTEM_VAPOR_PRESSURE,
+                                -0.373);
+    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::CATALYST_TEMPERATURE_BANK1_SENSOR1,
+                                190.);
+    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::RELATIVE_THROTTLE_POSITION, 3.);
+    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ABSOLUTE_THROTTLE_POSITION_B, 0.306);
+    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ACCELERATOR_PEDAL_POSITION_D, 0.188);
+    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ACCELERATOR_PEDAL_POSITION_E, 0.094);
+    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::COMMANDED_THROTTLE_ACTUATOR, 0.024);
+
+    return sensorStore;
+}
+
+}  // namespace
+
+void initObd2LiveFrame(VehiclePropertyStore* propStore, const VehiclePropConfig& propConfig) {
+    auto liveObd2Frame = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
+    auto sensorStore = fillDefaultObd2Frame(static_cast<size_t>(propConfig.configArray[0]),
+                                            static_cast<size_t>(propConfig.configArray[1]));
+    sensorStore->fillPropValue("", liveObd2Frame.get());
+    liveObd2Frame->prop = OBD2_LIVE_FRAME;
+
+    propStore->writeValue(*liveObd2Frame, true);
+}
+
+void initObd2FreezeFrame(VehiclePropertyStore* propStore, const VehiclePropConfig& propConfig) {
+    auto sensorStore = fillDefaultObd2Frame(static_cast<size_t>(propConfig.configArray[0]),
+                                            static_cast<size_t>(propConfig.configArray[1]));
+
+    static std::vector<std::string> sampleDtcs = {"P0070", "P0102", "P0123"};
+    for (auto&& dtc : sampleDtcs) {
+        auto freezeFrame = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
+        sensorStore->fillPropValue(dtc, freezeFrame.get());
+        freezeFrame->prop = OBD2_FREEZE_FRAME;
+        ALOGE("freeze frame: %lld", (long long)freezeFrame->timestamp);
+
+        propStore->writeValue(*freezeFrame, true);
+    }
+}
+
+StatusCode fillObd2FreezeFrame(VehiclePropertyStore* propStore,
+                               const VehiclePropValue& requestedPropValue,
+                               VehiclePropValue* outValue) {
+    if (requestedPropValue.value.int64Values.size() != 1) {
+        ALOGE("asked for OBD2_FREEZE_FRAME without valid timestamp");
+        return StatusCode::INVALID_ARG;
+    }
+    if (propStore->readValuesForProperty(OBD2_FREEZE_FRAME).size() == 0) {
+        // Should no freeze frame be available at the given timestamp, a response of NOT_AVAILABLE
+        // must be returned by the implementation
+        return StatusCode::NOT_AVAILABLE;
+    }
+    auto timestamp = requestedPropValue.value.int64Values[0];
+    auto freezeFrame = propStore->readValueOrNull(OBD2_FREEZE_FRAME, 0, timestamp);
+    if (freezeFrame == nullptr) {
+        ALOGE("asked for OBD2_FREEZE_FRAME at invalid timestamp");
+        return StatusCode::INVALID_ARG;
+    }
+    outValue->prop = OBD2_FREEZE_FRAME;
+    outValue->value.int32Values = freezeFrame->value.int32Values;
+    outValue->value.floatValues = freezeFrame->value.floatValues;
+    outValue->value.bytes = freezeFrame->value.bytes;
+    outValue->value.stringValue = freezeFrame->value.stringValue;
+    outValue->timestamp = freezeFrame->timestamp;
+    return StatusCode::OK;
+}
+
+StatusCode fillObd2DtcInfo(VehiclePropertyStore* propStore, VehiclePropValue* outValue) {
+    std::vector<int64_t> timestamps;
+    for (const auto& freezeFrame : propStore->readValuesForProperty(OBD2_FREEZE_FRAME)) {
+        timestamps.push_back(freezeFrame.timestamp);
+    }
+    outValue->value.int64Values = timestamps;
+    outValue->prop = OBD2_FREEZE_FRAME_INFO;
+    return StatusCode::OK;
+}
+
+StatusCode clearObd2FreezeFrames(VehiclePropertyStore* propStore,
+                                 const VehiclePropValue& propValue) {
+    if (propValue.value.int64Values.size() == 0) {
+        propStore->removeValuesForProperty(OBD2_FREEZE_FRAME);
+        return StatusCode::OK;
+    } else {
+        for (int64_t timestamp : propValue.value.int64Values) {
+            auto freezeFrame = propStore->readValueOrNull(OBD2_FREEZE_FRAME, 0, timestamp);
+            if (freezeFrame == nullptr) {
+                ALOGE("asked for OBD2_FREEZE_FRAME at invalid timestamp");
+                return StatusCode::INVALID_ARG;
+            }
+            propStore->removeValue(*freezeFrame);
+        }
+    }
+    return StatusCode::OK;
+}
+
+bool isDiagnosticProperty(const VehiclePropConfig& propConfig) {
+    return (propConfig.prop == OBD2_LIVE_FRAME || propConfig.prop == OBD2_FREEZE_FRAME ||
+            propConfig.prop == OBD2_FREEZE_FRAME_CLEAR ||
+            propConfig.prop == OBD2_FREEZE_FRAME_INFO);
+}
+
+}  // namespace impl
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeObd2Frame.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeObd2Frame.h
new file mode 100644
index 0000000..704964c
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeObd2Frame.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef android_hardware_automotive_vehicle_V2_0_impl_FakeObd2Frame_H_
+#define android_hardware_automotive_vehicle_V2_0_impl_FakeObd2Frame_H_
+
+#include <android/hardware/automotive/vehicle/2.0/types.h>
+#include <vhal_v2_0/VehiclePropertyStore.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+void initObd2LiveFrame(VehiclePropertyStore* propStore, const VehiclePropConfig& propConfig);
+void initObd2FreezeFrame(VehiclePropertyStore* propStore, const VehiclePropConfig& propConfig);
+StatusCode fillObd2FreezeFrame(VehiclePropertyStore* propStore,
+                               const VehiclePropValue& requestedPropValue,
+                               VehiclePropValue* outValue);
+StatusCode fillObd2DtcInfo(VehiclePropertyStore* propStore, VehiclePropValue* outValue);
+StatusCode clearObd2FreezeFrames(VehiclePropertyStore* propStore,
+                                 const VehiclePropValue& propValue);
+bool isDiagnosticProperty(const VehiclePropConfig& propConfig);
+
+}  // namespace impl
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_V2_0_impl_FakeObd2Frame_H_
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/Android.bp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/Android.bp
index 5762223..a7d5440 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/Android.bp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/Android.bp
@@ -16,3 +16,8 @@
     name: "vhal_test_json",
     srcs: ["prop.json"],
 }
+
+filegroup {
+    name: "vhal_test_override_json",
+    srcs: ["override/*"],
+}
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
index 891d8d6..2268df8 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
@@ -27,6 +27,32 @@
 #include <vhal_v2_0/VehicleObjectPool.h>
 #include <vhal_v2_0/VehiclePropertyStore.h>
 
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+namespace impl {
+
+class DefaultVhalImplTestHelper {
+  public:
+    DefaultVhalImplTestHelper(DefaultVehicleHalServer* server) { mServer = server; }
+
+    void overrideProperties(const char* overrideDir) {
+        mServer->overrideProperties(overrideDir);
+    }
+
+  private:
+    DefaultVehicleHalServer* mServer;
+};
+
+}  // namespace impl
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
 namespace {
 
 using ::android::hardware::hidl_handle;
@@ -44,9 +70,15 @@
 using ::android::hardware::automotive::vehicle::V2_0::VehiclePropValuePool;
 using ::android::hardware::automotive::vehicle::V2_0::impl::DefaultVehicleConnector;
 using ::android::hardware::automotive::vehicle::V2_0::impl::DefaultVehicleHal;
+using ::android::hardware::automotive::vehicle::V2_0::impl::DefaultVhalImplTestHelper;
 using ::android::hardware::automotive::vehicle::V2_0::impl::HVAC_ALL;
 using ::android::hardware::automotive::vehicle::V2_0::impl::HVAC_LEFT;
+using ::android::hardware::automotive::vehicle::V2_0::impl::HVAC_RIGHT;
 using ::android::hardware::automotive::vehicle::V2_0::impl::kMixedTypePropertyForTest;
+using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_FREEZE_FRAME;
+using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_FREEZE_FRAME_CLEAR;
+using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_FREEZE_FRAME_INFO;
+using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_LIVE_FRAME;
 
 using ::testing::HasSubstr;
 
@@ -72,12 +104,24 @@
         mConnector.reset(new DefaultVehicleConnector);
         mConnector->setValuePool(&mValueObjectPool);
         mHal.reset(new DefaultVehicleHal(mPropStore.get(), mConnector.get()));
+        initHal();
+    }
+
+    void initHal() {
         mHal->init(&mValueObjectPool,
                    std::bind(&DefaultVhalImplTest::onHalEvent, this, std::placeholders::_1),
                    std::bind(&DefaultVhalImplTest::onHalPropertySetError, this,
                              std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
     }
 
+  protected:
+    std::unique_ptr<DefaultVehicleHal> mHal;
+    std::unique_ptr<DefaultVehicleConnector> mConnector;
+    std::unique_ptr<VehiclePropertyStore> mPropStore;
+    VehiclePropValuePool mValueObjectPool;
+    android::ConcurrentQueue<VehiclePropValuePtr> mEventQueue;
+    android::ConcurrentQueue<VehiclePropValuePtr> mHeartBeatQueue;
+
   private:
     void onHalEvent(VehiclePropValuePtr v) {
         if (v->prop != toInt(VehicleProperty::VHAL_HEARTBEAT)) {
@@ -90,14 +134,6 @@
 
     void onHalPropertySetError(StatusCode /*errorCode*/, int32_t /*property*/, int32_t /*areaId*/) {
     }
-
-  protected:
-    std::unique_ptr<DefaultVehicleHal> mHal;
-    std::unique_ptr<DefaultVehicleConnector> mConnector;
-    std::unique_ptr<VehiclePropertyStore> mPropStore;
-    VehiclePropValuePool mValueObjectPool;
-    android::ConcurrentQueue<VehiclePropValuePtr> mEventQueue;
-    android::ConcurrentQueue<VehiclePropValuePtr> mHeartBeatQueue;
 };
 
 TEST_F(DefaultVhalImplTest, testListProperties) {
@@ -721,12 +757,12 @@
     EXPECT_EQ((size_t)1, events[0]->value.int32Values.size());
     EXPECT_EQ(8, events[0]->value.int32Values[0]);
     EXPECT_EQ((size_t)1, events[3]->value.int32Values.size());
-    EXPECT_EQ(4, events[3]->value.int32Values[0]);
+    EXPECT_EQ(10, events[3]->value.int32Values[0]);
     // Second set of the same events.
     EXPECT_EQ((size_t)1, events[4]->value.int32Values.size());
     EXPECT_EQ(8, events[4]->value.int32Values[0]);
     EXPECT_EQ((size_t)1, events[7]->value.int32Values.size());
-    EXPECT_EQ(4, events[7]->value.int32Values[0]);
+    EXPECT_EQ(10, events[7]->value.int32Values[0]);
 }
 
 TEST_F(DefaultVhalImplTest, testDebugGenFakeDataKeyPress) {
@@ -768,4 +804,463 @@
     ASSERT_EQ(toInt(VehicleProperty::VHAL_HEARTBEAT), events[0]->prop);
 }
 
+TEST_F(DefaultVhalImplTest, testVendorOverrideProperties) {
+    // Destroy the existing VHAL first to prevent it using destroyed connector or propstore.
+    mHal.reset();
+    // Create a new Default VHAL and reinitialize it to load the override properties.
+    std::string overrideDir = android::base::GetExecutableDirectory() + "/override/";
+    mPropStore.reset(new VehiclePropertyStore);
+    mConnector.reset(new DefaultVehicleConnector);
+    mConnector->setValuePool(&mValueObjectPool);
+    mHal.reset(new DefaultVehicleHal(mPropStore.get(), mConnector.get()));
+    // Set vendor override directory.
+    DefaultVhalImplTestHelper helper(mConnector.get());
+    helper.overrideProperties(overrideDir.c_str());
+
+    initHal();
+
+    VehiclePropValue value;
+    StatusCode status;
+    // This is the same as the prop in 'gear_selection.json'.
+    value.prop = toInt(VehicleProperty::GEAR_SELECTION);
+
+    auto gotValue = mHal->get(value, &status);
+
+    ASSERT_EQ(StatusCode::OK, status);
+    ASSERT_EQ((size_t)1, gotValue->value.int32Values.size());
+    ASSERT_EQ(8, gotValue->value.int32Values[0]);
+
+    // If we set the value, it should update despite the override.
+    value.prop = toInt(VehicleProperty::GEAR_SELECTION);
+    value.value.int32Values.resize(1);
+    value.value.int32Values[0] = 5;
+
+    status = mHal->set(value);
+    ASSERT_EQ(StatusCode::OK, status);
+
+    gotValue = mHal->get(value, &status);
+    ASSERT_EQ(StatusCode::OK, status);
+    ASSERT_EQ((size_t)1, gotValue->value.int32Values.size());
+    ASSERT_EQ(5, gotValue->value.int32Values[0]);
+}
+
+TEST_F(DefaultVhalImplTest, testVendorOverridePropertiesMultipleAreas) {
+    // Destroy the existing VHAL first to prevent it using destroyed connector or propstore.
+    mHal.reset();
+    // Create a new Default VHAL and reinitialize it to load the override properties.
+    std::string overrideDir = android::base::GetExecutableDirectory() + "/override/";
+    mPropStore.reset(new VehiclePropertyStore);
+    mConnector.reset(new DefaultVehicleConnector);
+    mConnector->setValuePool(&mValueObjectPool);
+    mHal.reset(new DefaultVehicleHal(mPropStore.get(), mConnector.get()));
+    // Set vendor override directory.
+    DefaultVhalImplTestHelper helper(mConnector.get());
+    helper.overrideProperties(overrideDir.c_str());
+
+    initHal();
+
+    VehiclePropValue value;
+    StatusCode status;
+    // This is the same as the prop in 'hvac_temperature_set.json'.
+    value.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_SET);
+    value.areaId = HVAC_LEFT;
+
+    auto gotValue = mHal->get(value, &status);
+
+    ASSERT_EQ(StatusCode::OK, status);
+    ASSERT_EQ((size_t)1, gotValue->value.floatValues.size());
+    ASSERT_EQ(30.0f, gotValue->value.floatValues[0]);
+
+    // HVAC_RIGHT should not be affected and return the default value.
+    value.areaId = HVAC_RIGHT;
+
+    gotValue = mHal->get(value, &status);
+
+    ASSERT_EQ(StatusCode::OK, status);
+    ASSERT_EQ((size_t)1, gotValue->value.floatValues.size());
+    ASSERT_EQ(20.0f, gotValue->value.floatValues[0]);
+}
+
+TEST_F(DefaultVhalImplTest, testVendorOverridePropertiesDirDoesNotExist) {
+    // Destroy the existing VHAL first to prevent it using destroyed connector or propstore.
+    mHal.reset();
+    // Create a new Default VHAL and reinitialize it to load the override properties.
+    mPropStore.reset(new VehiclePropertyStore);
+    mConnector.reset(new DefaultVehicleConnector);
+    mConnector->setValuePool(&mValueObjectPool);
+    mHal.reset(new DefaultVehicleHal(mPropStore.get(), mConnector.get()));
+    // Set vendor override directory to a non-existing dir
+    DefaultVhalImplTestHelper helper(mConnector.get());
+    helper.overrideProperties("123");
+    initHal();
+
+    VehiclePropValue value;
+    StatusCode status;
+    value.prop = toInt(VehicleProperty::GEAR_SELECTION);
+
+    auto gotValue = mHal->get(value, &status);
+
+    // We should get the default value.
+    ASSERT_EQ(StatusCode::OK, status);
+    ASSERT_EQ((size_t)1, gotValue->value.int32Values.size());
+    ASSERT_EQ(4, gotValue->value.int32Values[0]);
+}
+
+TEST_F(DefaultVhalImplTest, testGetObd2FreezeFrameNoTimestamp) {
+    VehiclePropValue value;
+    value.prop = OBD2_FREEZE_FRAME;
+    StatusCode status;
+
+    auto gotValue = mHal->get(value, &status);
+
+    ASSERT_EQ(StatusCode::INVALID_ARG, status);
+}
+
+TEST_F(DefaultVhalImplTest, testGetObd2FreezeFrameInvalidTimestamp) {
+    VehiclePropValue value;
+    value.prop = OBD2_FREEZE_FRAME;
+    value.value.int64Values.resize(1);
+    value.value.int64Values[0] = 0;
+    StatusCode status;
+
+    auto gotValue = mHal->get(value, &status);
+
+    ASSERT_EQ(StatusCode::INVALID_ARG, status);
+}
+
+TEST_F(DefaultVhalImplTest, testGetObd2FreezeFrameInfoGetObd2FreezeFrame) {
+    VehiclePropValue value;
+    value.prop = OBD2_FREEZE_FRAME_INFO;
+    StatusCode status;
+
+    auto gotValue = mHal->get(value, &status);
+
+    ASSERT_EQ(StatusCode::OK, status);
+    ASSERT_EQ((size_t)3, gotValue->value.int64Values.size());
+
+    std::vector<std::string> dtcs;
+    std::vector<std::string> sampleDtcs = {"P0070", "P0102", "P0123"};
+    for (int64_t timestamp : gotValue->value.int64Values) {
+        VehiclePropValue freezeFrameRequest;
+        freezeFrameRequest.prop = OBD2_FREEZE_FRAME;
+        freezeFrameRequest.value.int64Values.resize(1);
+        freezeFrameRequest.value.int64Values[0] = timestamp;
+
+        auto freezeFrameValue = mHal->get(freezeFrameRequest, &status);
+
+        ASSERT_EQ(StatusCode::OK, status);
+        // Obd2IntegerSensorIndex.LAST_SYSTEM_INDEX + 1
+        EXPECT_EQ((size_t)32, freezeFrameValue->value.int32Values.size());
+        // Obd2FloatSensorIndex.LAST_SYSTEM_INDEX + 1
+        EXPECT_EQ((size_t)71, freezeFrameValue->value.floatValues.size());
+        // (intValues.size() + floatValues.size()) / 8
+        EXPECT_EQ((size_t)13, freezeFrameValue->value.bytes.size());
+
+        dtcs.push_back(freezeFrameValue->value.stringValue);
+    }
+
+    for (std::string expectDtc : sampleDtcs) {
+        EXPECT_NE(std::find(dtcs.begin(), dtcs.end(), expectDtc), dtcs.end());
+    }
+}
+
+TEST_F(DefaultVhalImplTest, testGetObd2LiveFrame) {
+    VehiclePropValue value;
+    value.prop = OBD2_LIVE_FRAME;
+    StatusCode status;
+
+    auto gotValue = mHal->get(value, &status);
+
+    ASSERT_EQ(StatusCode::OK, status);
+    // Obd2IntegerSensorIndex.LAST_SYSTEM_INDEX + 1
+    EXPECT_EQ((size_t)32, gotValue->value.int32Values.size());
+    // Obd2FloatSensorIndex.LAST_SYSTEM_INDEX + 1
+    EXPECT_EQ((size_t)71, gotValue->value.floatValues.size());
+    // (intValues.size() + floatValues.size()) / 8
+    EXPECT_EQ((size_t)13, gotValue->value.bytes.size());
+}
+
+TEST_F(DefaultVhalImplTest, testClearObd2FreezeFrameAll) {
+    VehiclePropValue value;
+    value.prop = OBD2_FREEZE_FRAME_CLEAR;
+    // No int64Values is to clear all frames.
+
+    auto status = mHal->set(value);
+
+    EXPECT_EQ(StatusCode::OK, status);
+
+    VehiclePropValue freezeFrameRequest;
+    freezeFrameRequest.prop = OBD2_FREEZE_FRAME;
+    freezeFrameRequest.value.int64Values.resize(1);
+
+    auto gotValue = mHal->get(freezeFrameRequest, &status);
+
+    EXPECT_EQ(StatusCode::NOT_AVAILABLE, status);
+
+    VehiclePropValue freezeFrameInfoRequest;
+    freezeFrameInfoRequest.prop = OBD2_FREEZE_FRAME_INFO;
+
+    gotValue = mHal->get(freezeFrameInfoRequest, &status);
+
+    EXPECT_EQ(StatusCode::OK, status);
+    EXPECT_EQ((size_t)0, gotValue->value.int64Values.size());
+}
+
+TEST_F(DefaultVhalImplTest, testClearObd2FreezeFrameOneFrame) {
+    // Get existing freeze frame info first.
+    VehiclePropValue frameInfoRequest;
+    frameInfoRequest.prop = OBD2_FREEZE_FRAME_INFO;
+    StatusCode status;
+    auto gotValue = mHal->get(frameInfoRequest, &status);
+    ASSERT_EQ(StatusCode::OK, status);
+    ASSERT_EQ((size_t)3, gotValue->value.int64Values.size());
+
+    VehiclePropValue clearRequest;
+    int64_t timestamp = gotValue->value.int64Values[0];
+    clearRequest.prop = OBD2_FREEZE_FRAME_CLEAR;
+    clearRequest.value.int64Values.resize(1);
+    clearRequest.value.int64Values[0] = timestamp;
+
+    // Try to clear the first frame.
+    status = mHal->set(clearRequest);
+
+    // Get freeze frame info again.
+    gotValue = mHal->get(frameInfoRequest, &status);
+
+    ASSERT_EQ(StatusCode::OK, status);
+    // Now we should only have 2 frames.
+    ASSERT_EQ((size_t)2, gotValue->value.int64Values.size());
+
+    // Try to get the deleted frame, should fail.
+    VehiclePropValue frameRequest;
+    frameRequest.prop = OBD2_FREEZE_FRAME;
+    frameRequest.value.int64Values.resize(1);
+    frameRequest.value.int64Values[0] = timestamp;
+
+    gotValue = mHal->get(frameRequest, &status);
+
+    ASSERT_EQ(StatusCode::INVALID_ARG, status);
+
+    // Clear the same frame again should fail.
+    status = mHal->set(clearRequest);
+
+    ASSERT_EQ(StatusCode::INVALID_ARG, status);
+}
+
+TEST_F(DefaultVhalImplTest, testGetUserPropertySetOnly) {
+    VehiclePropValue value;
+    value.prop = toInt(VehicleProperty::INITIAL_USER_INFO);
+    StatusCode status;
+
+    mHal->get(value, &status);
+
+    ASSERT_EQ(StatusCode::INVALID_ARG, status);
+
+    value.prop = toInt(VehicleProperty::SWITCH_USER);
+
+    mHal->get(value, &status);
+
+    ASSERT_EQ(StatusCode::INVALID_ARG, status);
+
+    value.prop = toInt(VehicleProperty::CREATE_USER);
+
+    mHal->get(value, &status);
+
+    ASSERT_EQ(StatusCode::INVALID_ARG, status);
+
+    value.prop = toInt(VehicleProperty::REMOVE_USER);
+
+    mHal->get(value, &status);
+
+    ASSERT_EQ(StatusCode::INVALID_ARG, status);
+}
+
+TEST_F(DefaultVhalImplTest, testGetUserIdAssoc) {
+    VehiclePropValue value;
+    value.prop = toInt(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION);
+    StatusCode status;
+
+    mHal->get(value, &status);
+
+    // Default returns NOT_AVAILABLE.
+    ASSERT_EQ(StatusCode::NOT_AVAILABLE, status);
+
+    // This is the same example as used in User HAL Emulation doc.
+    VehiclePropValue setValue = {
+            .prop = toInt(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION),
+            .areaId = 1,
+            .value.int32Values = {666, 1, 1, 2},
+    };
+
+    status = mHal->set(setValue);
+
+    ASSERT_EQ(StatusCode::OK, status);
+
+    auto gotValue = mHal->get(value, &status);
+
+    ASSERT_EQ(StatusCode::OK, status);
+    ASSERT_EQ((size_t)4, gotValue->value.int32Values.size());
+    EXPECT_EQ(1, gotValue->areaId);
+    EXPECT_EQ(666, gotValue->value.int32Values[0]);
+    EXPECT_EQ(1, gotValue->value.int32Values[1]);
+    EXPECT_EQ(1, gotValue->value.int32Values[2]);
+    EXPECT_EQ(2, gotValue->value.int32Values[3]);
+    EXPECT_EQ(toInt(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION), gotValue->prop);
+}
+
+TEST_F(DefaultVhalImplTest, testSwitchUser) {
+    // This is the same example as used in User HAL Emulation doc.
+    VehiclePropValue setValue = {
+            .prop = toInt(VehicleProperty::SWITCH_USER),
+            .areaId = 1,
+            .value.int32Values = {666, 3, 2},
+    };
+
+    auto status = mHal->set(setValue);
+
+    ASSERT_EQ(StatusCode::OK, status);
+
+    // Simulate a request from Android side.
+    setValue = {
+            .prop = toInt(VehicleProperty::SWITCH_USER),
+            .areaId = 0,
+            .value.int32Values = {666, 3},
+    };
+    // Clear existing events.
+    mEventQueue.flush();
+
+    status = mHal->set(setValue);
+
+    ASSERT_EQ(StatusCode::OK, status);
+
+    // Should generate an event for user hal response.
+    auto events = mEventQueue.flush();
+    ASSERT_EQ((size_t)1, events.size());
+    EXPECT_EQ(1, events[0]->areaId);
+    EXPECT_EQ(toInt(VehicleProperty::SWITCH_USER), events[0]->prop);
+    ASSERT_EQ((size_t)3, events[0]->value.int32Values.size());
+    EXPECT_EQ(666, events[0]->value.int32Values[0]);
+    EXPECT_EQ(3, events[0]->value.int32Values[1]);
+    EXPECT_EQ(2, events[0]->value.int32Values[2]);
+
+    // Try to get switch_user again, should return default value.
+    status = mHal->set(setValue);
+    ASSERT_EQ(StatusCode::OK, status);
+
+    events = mEventQueue.flush();
+    ASSERT_EQ((size_t)1, events.size());
+    EXPECT_EQ(0, events[0]->areaId);
+    EXPECT_EQ(toInt(VehicleProperty::SWITCH_USER), events[0]->prop);
+    ASSERT_EQ((size_t)3, events[0]->value.int32Values.size());
+    // Request ID
+    EXPECT_EQ(666, events[0]->value.int32Values[0]);
+    // VEHICLE_RESPONSE
+    EXPECT_EQ(3, events[0]->value.int32Values[1]);
+    // SUCCESS
+    EXPECT_EQ(1, events[0]->value.int32Values[2]);
+}
+
+TEST_F(DefaultVhalImplTest, testCreateUser) {
+    // This is the same example as used in User HAL Emulation doc.
+    VehiclePropValue setValue = {
+            .prop = toInt(VehicleProperty::CREATE_USER),
+            .areaId = 1,
+            .value.int32Values = {666, 2},
+    };
+
+    auto status = mHal->set(setValue);
+
+    ASSERT_EQ(StatusCode::OK, status);
+
+    // Simulate a request from Android side.
+    setValue = {
+            .prop = toInt(VehicleProperty::CREATE_USER),
+            .areaId = 0,
+            .value.int32Values = {666},
+    };
+    // Clear existing events.
+    mEventQueue.flush();
+
+    status = mHal->set(setValue);
+
+    ASSERT_EQ(StatusCode::OK, status);
+
+    // Should generate an event for user hal response.
+    auto events = mEventQueue.flush();
+    ASSERT_EQ((size_t)1, events.size());
+    EXPECT_EQ(1, events[0]->areaId);
+    EXPECT_EQ(toInt(VehicleProperty::CREATE_USER), events[0]->prop);
+    ASSERT_EQ((size_t)2, events[0]->value.int32Values.size());
+    EXPECT_EQ(666, events[0]->value.int32Values[0]);
+    EXPECT_EQ(2, events[0]->value.int32Values[1]);
+
+    // Try to get create_user again, should return default value.
+    status = mHal->set(setValue);
+    ASSERT_EQ(StatusCode::OK, status);
+
+    events = mEventQueue.flush();
+    ASSERT_EQ((size_t)1, events.size());
+    EXPECT_EQ(0, events[0]->areaId);
+    EXPECT_EQ(toInt(VehicleProperty::CREATE_USER), events[0]->prop);
+    ASSERT_EQ((size_t)2, events[0]->value.int32Values.size());
+    // Request ID
+    EXPECT_EQ(666, events[0]->value.int32Values[0]);
+    // SUCCESS
+    EXPECT_EQ(1, events[0]->value.int32Values[1]);
+}
+
+TEST_F(DefaultVhalImplTest, testInitialUserInfo) {
+    // This is the same example as used in User HAL Emulation doc.
+    VehiclePropValue setValue = {
+            .prop = toInt(VehicleProperty::INITIAL_USER_INFO),
+            .areaId = 1,
+            .value.int32Values = {666, 1, 11},
+    };
+
+    auto status = mHal->set(setValue);
+
+    ASSERT_EQ(StatusCode::OK, status);
+
+    // Simulate a request from Android side.
+    setValue = {
+            .prop = toInt(VehicleProperty::INITIAL_USER_INFO),
+            .areaId = 0,
+            .value.int32Values = {3},
+    };
+    // Clear existing events.
+    mEventQueue.flush();
+
+    status = mHal->set(setValue);
+
+    ASSERT_EQ(StatusCode::OK, status);
+
+    // Should generate an event for user hal response.
+    auto events = mEventQueue.flush();
+    ASSERT_EQ((size_t)1, events.size());
+    EXPECT_EQ(1, events[0]->areaId);
+    EXPECT_EQ(toInt(VehicleProperty::INITIAL_USER_INFO), events[0]->prop);
+    ASSERT_EQ((size_t)3, events[0]->value.int32Values.size());
+    EXPECT_EQ(3, events[0]->value.int32Values[0]);
+    EXPECT_EQ(1, events[0]->value.int32Values[1]);
+    EXPECT_EQ(11, events[0]->value.int32Values[2]);
+
+    // Try to get create_user again, should return default value.
+    status = mHal->set(setValue);
+    ASSERT_EQ(StatusCode::OK, status);
+
+    events = mEventQueue.flush();
+    ASSERT_EQ((size_t)1, events.size());
+    EXPECT_EQ(0, events[0]->areaId);
+    EXPECT_EQ(toInt(VehicleProperty::INITIAL_USER_INFO), events[0]->prop);
+    ASSERT_EQ((size_t)4, events[0]->value.int32Values.size());
+    // Request ID
+    EXPECT_EQ(3, events[0]->value.int32Values[0]);
+    // ACTION: DEFAULT
+    EXPECT_EQ(0, events[0]->value.int32Values[1]);
+    // User id: 0
+    EXPECT_EQ(0, events[0]->value.int32Values[2]);
+    // Flags: 0
+    EXPECT_EQ(0, events[0]->value.int32Values[3]);
+}
+
 }  // namespace
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/override/gear_selection.json b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/override/gear_selection.json
new file mode 100644
index 0000000..59666b8
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/override/gear_selection.json
@@ -0,0 +1,9 @@
+[
+  {
+    "timestamp": 1000000,
+    "areaId": 0,
+    "value": 8,
+    // GEAR_SELECTION
+    "prop": 289408000
+  }
+]
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/override/hvac_temperature_set.json b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/override/hvac_temperature_set.json
new file mode 100644
index 0000000..93a97ed
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/override/hvac_temperature_set.json
@@ -0,0 +1,10 @@
+[
+  {
+    "timestamp": 1000000,
+    // HVAC_LEFT
+    "areaId": 49,
+    "value": 30,
+    // HVAC_TEMPERATURE_SET
+    "prop": 358614275
+  }
+]
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/prop.json b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/prop.json
index 2e77a44..b881109 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/prop.json
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/prop.json
@@ -20,7 +20,7 @@
   {
     "timestamp": 4000000,
     "areaId": 0,
-    "value": 4,
+    "value": 10,
     "prop": 289408000
   }
 ]
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/Android.bp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/Android.bp
new file mode 100644
index 0000000..0058d33
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/Android.bp
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Library used to emulate User HAL behavior through lshal debug requests.
+cc_library {
+    name: "android.hardware.automotive.vehicle@2.0-emulated-user-hal-lib",
+    vendor: true,
+    defaults: ["vhal_v2_0_target_defaults"],
+    srcs: ["EmulatedUserHal.cpp"],
+    shared_libs: [
+        "libbase",
+        "libutils",
+        "libcutils",
+    ],
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+    whole_static_libs: [
+        "android.hardware.automotive.vehicle@2.0-user-hal-helper-lib",
+    ],
+}
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/EmulatedUserHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/EmulatedUserHal.cpp
new file mode 100644
index 0000000..55dc01a
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/EmulatedUserHal.cpp
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "EmulatedUserHal"
+
+#include <cutils/log.h>
+#include <utils/SystemClock.h>
+
+#include "UserHalHelper.h"
+
+#include "EmulatedUserHal.h"
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+namespace {
+
+using android::base::Error;
+using android::base::Result;
+
+constexpr int32_t INITIAL_USER_INFO = static_cast<int32_t>(VehicleProperty::INITIAL_USER_INFO);
+constexpr int32_t SWITCH_USER = static_cast<int32_t>(VehicleProperty::SWITCH_USER);
+constexpr int32_t CREATE_USER = static_cast<int32_t>(VehicleProperty::CREATE_USER);
+constexpr int32_t REMOVE_USER = static_cast<int32_t>(VehicleProperty::REMOVE_USER);
+constexpr int32_t USER_IDENTIFICATION_ASSOCIATION =
+        static_cast<int32_t>(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION);
+
+Result<int32_t> getRequestId(const VehiclePropValue& value) {
+    if (value.value.int32Values.size() < 1) {
+        return Error(static_cast<int>(StatusCode::INVALID_ARG))
+               << "no int32values on " << toString(value);
+    }
+    return value.value.int32Values[0];
+}
+
+Result<SwitchUserMessageType> getSwitchUserMessageType(const VehiclePropValue& value) {
+    if (value.value.int32Values.size() < 2) {
+        return Error(static_cast<int>(StatusCode::INVALID_ARG))
+               << "missing switch user message type " << toString(value);
+    }
+    return user_hal_helper::verifyAndCast<SwitchUserMessageType>(value.value.int32Values[1]);
+}
+
+}  // namespace
+
+bool EmulatedUserHal::isSupported(int32_t prop) {
+    switch (prop) {
+        case INITIAL_USER_INFO:
+        case SWITCH_USER:
+        case CREATE_USER:
+        case REMOVE_USER:
+        case USER_IDENTIFICATION_ASSOCIATION:
+            return true;
+        default:
+            return false;
+    }
+}
+
+Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onSetProperty(
+        const VehiclePropValue& value) {
+    ALOGV("onSetProperty(): %s", toString(value).c_str());
+
+    switch (value.prop) {
+        case INITIAL_USER_INFO:
+            return onSetInitialUserInfoResponse(value);
+        case SWITCH_USER:
+            return onSetSwitchUserResponse(value);
+        case CREATE_USER:
+            return onSetCreateUserResponse(value);
+        case REMOVE_USER:
+            ALOGI("REMOVE_USER is FYI only, nothing to do...");
+            return {};
+        case USER_IDENTIFICATION_ASSOCIATION:
+            return onSetUserIdentificationAssociation(value);
+        default:
+            return Error(static_cast<int>(StatusCode::INVALID_ARG))
+                   << "Unsupported property: " << toString(value);
+    }
+}
+
+Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onGetProperty(
+        const VehiclePropValue& value) {
+    ALOGV("onGetProperty(%s)", toString(value).c_str());
+    switch (value.prop) {
+        case INITIAL_USER_INFO:
+        case SWITCH_USER:
+        case CREATE_USER:
+        case REMOVE_USER:
+            ALOGE("onGetProperty(): %d is only supported on SET", value.prop);
+            return Error(static_cast<int>(StatusCode::INVALID_ARG)) << "only supported on SET";
+        case USER_IDENTIFICATION_ASSOCIATION:
+            return onGetUserIdentificationAssociation(value);
+        default:
+            ALOGE("onGetProperty(): %d is not supported", value.prop);
+            return Error(static_cast<int>(StatusCode::INVALID_ARG)) << "not supported by User HAL";
+    }
+}
+
+Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onGetUserIdentificationAssociation(
+        const VehiclePropValue& value) {
+    if (mSetUserIdentificationAssociationResponseFromCmd == nullptr) {
+        return defaultUserIdentificationAssociation(value);
+    }
+    ALOGI("get(USER_IDENTIFICATION_ASSOCIATION): returning %s",
+          toString(*mSetUserIdentificationAssociationResponseFromCmd).c_str());
+    auto newValue = std::unique_ptr<VehiclePropValue>(
+            new VehiclePropValue(*mSetUserIdentificationAssociationResponseFromCmd));
+    auto requestId = getRequestId(value);
+    if (requestId.ok()) {
+        // Must use the same requestId
+        newValue->value.int32Values[0] = *requestId;
+    } else {
+        ALOGE("get(USER_IDENTIFICATION_ASSOCIATION): no requestId on %s", toString(value).c_str());
+    }
+    return newValue;
+}
+
+Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onSetInitialUserInfoResponse(
+        const VehiclePropValue& value) {
+    auto requestId = getRequestId(value);
+    if (!requestId.ok()) {
+        ALOGE("Failed to get requestId on set(INITIAL_USER_INFO): %s",
+              requestId.error().message().c_str());
+        return requestId.error();
+    }
+
+    if (value.areaId != 0) {
+        ALOGD("set(INITIAL_USER_INFO) called from lshal; storing it: %s", toString(value).c_str());
+        mInitialUserResponseFromCmd.reset(new VehiclePropValue(value));
+        return {};
+    }
+
+    ALOGD("set(INITIAL_USER_INFO) called from Android: %s", toString(value).c_str());
+    if (mInitialUserResponseFromCmd != nullptr) {
+        ALOGI("replying INITIAL_USER_INFO with lshal value:  %s",
+              toString(*mInitialUserResponseFromCmd).c_str());
+        return sendUserHalResponse(std::move(mInitialUserResponseFromCmd), *requestId);
+    }
+
+    // Returns default response
+    auto updatedValue = user_hal_helper::toVehiclePropValue(InitialUserInfoResponse{
+            .requestId = *requestId,
+            .action = InitialUserInfoResponseAction::DEFAULT,
+    });
+    ALOGI("no lshal response; replying with InitialUserInfoResponseAction::DEFAULT: %s",
+          toString(*updatedValue).c_str());
+    return updatedValue;
+}
+
+Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onSetSwitchUserResponse(
+        const VehiclePropValue& value) {
+    auto requestId = getRequestId(value);
+    if (!requestId.ok()) {
+        ALOGE("Failed to get requestId on set(SWITCH_USER): %s",
+              requestId.error().message().c_str());
+        return requestId.error();
+    }
+
+    auto messageType = getSwitchUserMessageType(value);
+    if (!messageType.ok()) {
+        ALOGE("Failed to get messageType on set(SWITCH_USER): %s",
+              messageType.error().message().c_str());
+        return messageType.error();
+    }
+
+    if (value.areaId != 0) {
+        if (*messageType == SwitchUserMessageType::VEHICLE_REQUEST) {
+            // User HAL can also request a user switch, so we need to check it first
+            ALOGD("set(SWITCH_USER) called from lshal to emulate a vehicle request: %s",
+                  toString(value).c_str());
+            return std::unique_ptr<VehiclePropValue>(new VehiclePropValue(value));
+        }
+        // Otherwise, we store it
+        ALOGD("set(SWITCH_USER) called from lshal; storing it: %s", toString(value).c_str());
+        mSwitchUserResponseFromCmd.reset(new VehiclePropValue(value));
+        return {};
+    }
+    ALOGD("set(SWITCH_USER) called from Android: %s", toString(value).c_str());
+
+    if (mSwitchUserResponseFromCmd != nullptr) {
+        ALOGI("replying SWITCH_USER with lshal value:  %s",
+              toString(*mSwitchUserResponseFromCmd).c_str());
+        return sendUserHalResponse(std::move(mSwitchUserResponseFromCmd), *requestId);
+    }
+
+    if (*messageType == SwitchUserMessageType::LEGACY_ANDROID_SWITCH ||
+        *messageType == SwitchUserMessageType::ANDROID_POST_SWITCH) {
+        ALOGI("request is %s; ignoring it", toString(*messageType).c_str());
+        return {};
+    }
+
+    // Returns default response
+    auto updatedValue = user_hal_helper::toVehiclePropValue(SwitchUserResponse{
+            .requestId = *requestId,
+            .messageType = SwitchUserMessageType::VEHICLE_RESPONSE,
+            .status = SwitchUserStatus::SUCCESS,
+    });
+    ALOGI("no lshal response; replying with VEHICLE_RESPONSE / SUCCESS: %s",
+          toString(*updatedValue).c_str());
+    return updatedValue;
+}
+
+Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onSetCreateUserResponse(
+        const VehiclePropValue& value) {
+    auto requestId = getRequestId(value);
+    if (!requestId.ok()) {
+        ALOGE("Failed to get requestId on set(CREATE_USER): %s",
+              requestId.error().message().c_str());
+        return requestId.error();
+    }
+
+    if (value.areaId != 0) {
+        ALOGD("set(CREATE_USER) called from lshal; storing it: %s", toString(value).c_str());
+        mCreateUserResponseFromCmd.reset(new VehiclePropValue(value));
+        return {};
+    }
+    ALOGD("set(CREATE_USER) called from Android: %s", toString(value).c_str());
+
+    if (mCreateUserResponseFromCmd != nullptr) {
+        ALOGI("replying CREATE_USER with lshal value:  %s",
+              toString(*mCreateUserResponseFromCmd).c_str());
+        return sendUserHalResponse(std::move(mCreateUserResponseFromCmd), *requestId);
+    }
+
+    // Returns default response
+    auto updatedValue = user_hal_helper::toVehiclePropValue(CreateUserResponse{
+            .requestId = *requestId,
+            .status = CreateUserStatus::SUCCESS,
+    });
+    ALOGI("no lshal response; replying with SUCCESS: %s", toString(*updatedValue).c_str());
+    return updatedValue;
+}
+
+Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onSetUserIdentificationAssociation(
+        const VehiclePropValue& value) {
+    auto requestId = getRequestId(value);
+    if (!requestId.ok()) {
+        ALOGE("Failed to get requestId on set(USER_IDENTIFICATION_ASSOCIATION): %s",
+              requestId.error().message().c_str());
+        return requestId.error();
+    }
+
+    if (value.areaId != 0) {
+        ALOGD("set(USER_IDENTIFICATION_ASSOCIATION) called from lshal; storing it: %s",
+              toString(value).c_str());
+        mSetUserIdentificationAssociationResponseFromCmd.reset(new VehiclePropValue(value));
+        return {};
+    }
+    ALOGD("set(USER_IDENTIFICATION_ASSOCIATION) called from Android: %s", toString(value).c_str());
+
+    if (mSetUserIdentificationAssociationResponseFromCmd != nullptr) {
+        ALOGI("replying USER_IDENTIFICATION_ASSOCIATION with lshal value:  %s",
+              toString(*mSetUserIdentificationAssociationResponseFromCmd).c_str());
+        // Not moving response so it can be used on GET requests
+        auto copy = std::unique_ptr<VehiclePropValue>(
+                new VehiclePropValue(*mSetUserIdentificationAssociationResponseFromCmd));
+        return sendUserHalResponse(std::move(copy), *requestId);
+    }
+    // Returns default response
+    return defaultUserIdentificationAssociation(value);
+}
+
+Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::defaultUserIdentificationAssociation(
+        const VehiclePropValue& request) {
+    // TODO(b/159498909): return a response with NOT_ASSOCIATED_ANY_USER for all requested types
+    ALOGE("no lshal response for %s; replying with NOT_AVAILABLE", toString(request).c_str());
+    return Error(static_cast<int>(StatusCode::NOT_AVAILABLE)) << "not set by lshal";
+}
+
+Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::sendUserHalResponse(
+        std::unique_ptr<VehiclePropValue> response, int32_t requestId) {
+    switch (response->areaId) {
+        case 1:
+            ALOGD("returning response with right request id");
+            response->value.int32Values[0] = requestId;
+            break;
+        case 2:
+            ALOGD("returning response with wrong request id");
+            response->value.int32Values[0] = -requestId;
+            break;
+        case 3:
+            ALOGD("not generating a property change event because of lshal prop: %s",
+                  toString(*response).c_str());
+            return Error(static_cast<int>(StatusCode::NOT_AVAILABLE))
+                   << "not generating a property change event because of lshal prop: "
+                   << toString(*response);
+        default:
+            ALOGE("invalid action on lshal response: %s", toString(*response).c_str());
+            return Error(static_cast<int>(StatusCode::INTERNAL_ERROR))
+                   << "invalid action on lshal response: " << toString(*response);
+    }
+
+    ALOGD("updating property to: %s", toString(*response).c_str());
+    return response;
+}
+
+std::string EmulatedUserHal::showDumpHelp() {
+    return fmt::format("{}: dumps state used for user management\n", kUserHalDumpOption);
+}
+
+std::string EmulatedUserHal::dump(std::string indent) {
+    std::string info;
+    if (mInitialUserResponseFromCmd != nullptr) {
+        info += fmt::format("{}InitialUserInfo response: {}\n", indent.c_str(),
+                            toString(*mInitialUserResponseFromCmd).c_str());
+    } else {
+        info += fmt::format("{}No InitialUserInfo response\n", indent.c_str());
+    }
+    if (mSwitchUserResponseFromCmd != nullptr) {
+        info += fmt::format("{}SwitchUser response: {}\n", indent.c_str(),
+                            toString(*mSwitchUserResponseFromCmd).c_str());
+    } else {
+        info += fmt::format("{}No SwitchUser response\n", indent.c_str());
+    }
+    if (mCreateUserResponseFromCmd != nullptr) {
+        info += fmt::format("{}CreateUser response: {}\n", indent.c_str(),
+                            toString(*mCreateUserResponseFromCmd).c_str());
+    } else {
+        info += fmt::format("{}No CreateUser response\n", indent.c_str());
+    }
+    if (mSetUserIdentificationAssociationResponseFromCmd != nullptr) {
+        info += fmt::format("{}SetUserIdentificationAssociation response: {}\n", indent.c_str(),
+                            toString(*mSetUserIdentificationAssociationResponseFromCmd).c_str());
+    } else {
+        info += fmt::format("{}No SetUserIdentificationAssociation response\n", indent.c_str());
+    }
+    return info;
+}
+
+}  // namespace impl
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/include/EmulatedUserHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/include/EmulatedUserHal.h
new file mode 100644
index 0000000..46f6d0f
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/include/EmulatedUserHal.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef android_hardware_automotive_vehicle_V2_0_impl_EmulatedUserHal_H_
+#define android_hardware_automotive_vehicle_V2_0_impl_EmulatedUserHal_H_
+
+#include <android-base/format.h>
+#include <android-base/result.h>
+
+#include <android/hardware/automotive/vehicle/2.0/types.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+constexpr char kUserHalDumpOption[] = "--user-hal";
+
+/**
+ * Class used to emulate a real User HAL behavior through lshal debug requests.
+ */
+class EmulatedUserHal {
+  public:
+    EmulatedUserHal() {}
+
+    ~EmulatedUserHal() = default;
+
+    /**
+     * Checks if the emulator can handle the property.
+     */
+    bool isSupported(int32_t prop);
+
+    /**
+     * Lets the emulator set the property.
+     *
+     * @return updated property and StatusCode
+     */
+    android::base::Result<std::unique_ptr<VehiclePropValue>> onSetProperty(
+            const VehiclePropValue& value);
+
+    /**
+     * Gets the property value from the emulator.
+     *
+     * @return property value and StatusCode
+     */
+    android::base::Result<std::unique_ptr<VehiclePropValue>> onGetProperty(
+            const VehiclePropValue& value);
+
+    /**
+     * Shows the User HAL emulation help.
+     */
+    std::string showDumpHelp();
+
+    /**
+     * Dump its contents.
+     */
+    std::string dump(std::string indent);
+
+  private:
+    /**
+     * INITIAL_USER_INFO is called by Android when it starts, and it's expecting a property change
+     * indicating what the initial user should be.
+     *
+     * During normal circumstances, the emulator will reply right away, passing a response if
+     * InitialUserInfoResponseAction::DEFAULT (so Android could use its own logic to decide which
+     * user to boot).
+     *
+     * But during development / testing, the behavior can be changed using lshal dump, which must
+     * use the areaId to indicate what should happen next.
+     *
+     * So, the behavior of set(INITIAL_USER_INFO) is:
+     *
+     * - if it has an areaId, store the property into mInitialUserResponseFromCmd (as it was called
+     * by lshal).
+     * - else if mInitialUserResponseFromCmd is not set, return a response with the same request id
+     * and InitialUserInfoResponseAction::DEFAULT
+     * - else the behavior is defined by the areaId on mInitialUserResponseFromCmd:
+     * - if it's 1, reply with mInitialUserResponseFromCmd and the right request id
+     * - if it's 2, reply with mInitialUserResponseFromCmd but a wrong request id (so Android can
+     * test this error scenario)
+     * - if it's 3, then don't send a property change (so Android can emulate a timeout)
+     *
+     */
+    android::base::Result<std::unique_ptr<VehiclePropValue>> onSetInitialUserInfoResponse(
+            const VehiclePropValue& value);
+
+    /**
+     * Used to emulate SWITCH_USER - see onSetInitialUserInfoResponse() for usage.
+     */
+    android::base::Result<std::unique_ptr<VehiclePropValue>> onSetSwitchUserResponse(
+            const VehiclePropValue& value);
+
+    /**
+     * Used to emulate CREATE_USER - see onSetInitialUserInfoResponse() for usage.
+     */
+    android::base::Result<std::unique_ptr<VehiclePropValue>> onSetCreateUserResponse(
+            const VehiclePropValue& value);
+
+    /**
+     * Used to emulate set USER_IDENTIFICATION_ASSOCIATION - see onSetInitialUserInfoResponse() for
+     * usage.
+     */
+    android::base::Result<std::unique_ptr<VehiclePropValue>> onSetUserIdentificationAssociation(
+            const VehiclePropValue& value);
+
+    /**
+     * Used to emulate get USER_IDENTIFICATION_ASSOCIATION - see onSetInitialUserInfoResponse() for
+     * usage.
+     */
+    android::base::Result<std::unique_ptr<VehiclePropValue>> onGetUserIdentificationAssociation(
+            const VehiclePropValue& value);
+
+    /**
+     * Creates a default USER_IDENTIFICATION_ASSOCIATION when it was not set by lshal.
+     */
+    android::base::Result<std::unique_ptr<VehiclePropValue>> defaultUserIdentificationAssociation(
+            const VehiclePropValue& request);
+
+    android::base::Result<std::unique_ptr<VehiclePropValue>> sendUserHalResponse(
+            std::unique_ptr<VehiclePropValue> response, int32_t requestId);
+
+    std::unique_ptr<VehiclePropValue> mInitialUserResponseFromCmd;
+    std::unique_ptr<VehiclePropValue> mSwitchUserResponseFromCmd;
+    std::unique_ptr<VehiclePropValue> mCreateUserResponseFromCmd;
+    std::unique_ptr<VehiclePropValue> mSetUserIdentificationAssociationResponseFromCmd;
+};
+
+}  // namespace impl
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_automotive_vehicle_V2_0_impl_EmulatedUserHal_H_