Add special logic handle setting some props.

Add special logic to handle setting OBD2 properties and
VEHICLE_MAP_SERVICE.

Test: atest FakeVehicleHardwareTest
Bug: 201830716
Change-Id: I9b136efc452944e6c393c12feca14942919993c7
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp b/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp
index 210f316..f59a6ed 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp
@@ -34,6 +34,7 @@
     static_libs: [
         "VehicleHalUtils",
         "FakeVehicleHalValueGenerators",
+        "FakeObd2Frame",
     ],
     shared_libs: [
         "libjsoncpp",
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 b2d813f..e1cd8bd 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
@@ -18,9 +18,11 @@
 #define android_hardware_automotive_vehicle_aidl_impl_fake_impl_hardware_include_FakeVehicleHardware_H_
 
 #include <DefaultConfig.h>
+#include <FakeObd2Frame.h>
 #include <IVehicleHardware.h>
 #include <VehicleHalTypes.h>
 #include <VehiclePropertyStore.h>
+#include <android-base/result.h>
 #include <android-base/thread_annotations.h>
 
 #include <map>
@@ -85,14 +87,15 @@
     // Expose private methods to unit test.
     friend class FakeVehicleHardwareTestHelper;
 
-    std::unique_ptr<VehiclePropertyStore> mServerSidePropStore;
     // mValuePool is also used in mServerSidePropStore.
-    std::shared_ptr<VehiclePropValuePool> mValuePool;
+    const std::shared_ptr<VehiclePropValuePool> mValuePool;
+    const std::shared_ptr<VehiclePropertyStore> mServerSidePropStore;
+    const std::unique_ptr<obd2frame::FakeObd2Frame> mFakeObd2Frame;
     std::mutex mCallbackLock;
     OnPropertyChangeCallback mOnPropertyChangeCallback GUARDED_BY(mCallbackLock);
     OnPropertySetErrorCallback mOnPropertySetErrorCallback GUARDED_BY(mCallbackLock);
 
-    void init(std::shared_ptr<VehiclePropValuePool> valuePool);
+    void init();
     // Stores the initial value to property store.
     void storePropInitialValue(const defaultconfig::ConfigDeclaration& config);
     // The callback that would be called when a vehicle property value change happens.
@@ -107,6 +110,9 @@
     ::aidl::android::hardware::automotive::vehicle::StatusCode maybeSetSpecialValue(
             const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value,
             bool* isSpecialValue);
+    ::android::base::Result<VehiclePropValuePool::RecyclableType> maybeGetSpecialValue(
+            const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value,
+            bool* isSpecialValue) const;
     ::aidl::android::hardware::automotive::vehicle::StatusCode setApPowerStateReport(
             const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value);
     VehiclePropValuePool::RecyclableType createApPowerStateReq(
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 94791da..15db767 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -17,7 +17,9 @@
 #include "FakeVehicleHardware.h"
 
 #include <DefaultConfig.h>
+#include <FakeObd2Frame.h>
 #include <JsonFakeValueGenerator.h>
+#include <PropertyUtils.h>
 #include <VehicleHalTypes.h>
 #include <VehicleUtils.h>
 #include <android-base/properties.h>
@@ -53,9 +55,27 @@
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
 
+using ::android::base::Result;
+
 const char* VENDOR_OVERRIDE_DIR = "/vendor/etc/automotive/vhaloverride/";
 const char* OVERRIDE_PROPERTY = "persist.vendor.vhal_init_value_override";
 
+template <class T>
+StatusCode getErrorCode(const Result<T>& result) {
+    if (result.ok()) {
+        return StatusCode::OK;
+    }
+    return static_cast<StatusCode>(result.error().code());
+}
+
+template <class T>
+std::string getErrorMsg(const Result<T>& result) {
+    if (result.ok()) {
+        return "";
+    }
+    return result.error().message();
+}
+
 }  // namespace
 
 void FakeVehicleHardware::storePropInitialValue(const defaultconfig::ConfigDeclaration& config) {
@@ -93,32 +113,51 @@
         auto result =
                 mServerSidePropStore->writeValue(mValuePool->obtain(prop), /*updateStatus=*/true);
         if (!result.ok()) {
-            ALOGE("failed to write default config value, error: %s",
-                  result.error().message().c_str());
+            ALOGE("failed to write default config value, error: %s, status: %d",
+                  getErrorMsg(result).c_str(), getErrorCode(result));
         }
     }
 }
 
-FakeVehicleHardware::FakeVehicleHardware() {
-    mValuePool = std::make_shared<VehiclePropValuePool>();
-    init(mValuePool);
+FakeVehicleHardware::FakeVehicleHardware()
+    : mValuePool(new VehiclePropValuePool),
+      mServerSidePropStore(new VehiclePropertyStore(mValuePool)),
+      mFakeObd2Frame(new obd2frame::FakeObd2Frame(mServerSidePropStore)) {
+    init();
 }
 
 FakeVehicleHardware::FakeVehicleHardware(std::unique_ptr<VehiclePropValuePool> valuePool)
-    : mValuePool(std::move(valuePool)) {
-    init(mValuePool);
+    : mValuePool(std::move(valuePool)),
+      mServerSidePropStore(new VehiclePropertyStore(mValuePool)),
+      mFakeObd2Frame(new obd2frame::FakeObd2Frame(mServerSidePropStore)) {
+    init();
 }
 
-void FakeVehicleHardware::init(std::shared_ptr<VehiclePropValuePool> valuePool) {
-    mServerSidePropStore.reset(new VehiclePropertyStore(valuePool));
+void FakeVehicleHardware::init() {
     for (auto& it : defaultconfig::getDefaultConfigs()) {
         VehiclePropConfig cfg = it.config;
-        mServerSidePropStore->registerProperty(cfg);
+        VehiclePropertyStore::TokenFunction tokenFunction = nullptr;
+
+        if (cfg.prop == OBD2_FREEZE_FRAME) {
+            tokenFunction = [](const VehiclePropValue& propValue) { return propValue.timestamp; };
+        }
+
+        mServerSidePropStore->registerProperty(cfg, tokenFunction);
+        if (obd2frame::FakeObd2Frame::isDiagnosticProperty(cfg)) {
+            // Ignore storing default value for diagnostic property. They have special get/set
+            // logic.
+            continue;
+        }
         storePropInitialValue(it);
     }
 
     maybeOverrideProperties(VENDOR_OVERRIDE_DIR);
 
+    // OBD2_LIVE_FRAME and OBD2_FREEZE_FRAME must be configured in default configs.
+    mFakeObd2Frame->initObd2LiveFrame(*mServerSidePropStore->getConfig(OBD2_LIVE_FRAME).value());
+    mFakeObd2Frame->initObd2FreezeFrame(
+            *mServerSidePropStore->getConfig(OBD2_FREEZE_FRAME).value());
+
     mServerSidePropStore->setOnValueChangeCallback(
             [this](const VehiclePropValue& value) { return onValueChangeCallback(value); });
 }
@@ -146,9 +185,10 @@
 
     if (auto writeResult = mServerSidePropStore->writeValue(std::move(updatedValue));
         !writeResult.ok()) {
-        ALOGE("failed to write value into property store, error: %s",
-              writeResult.error().message().c_str());
-        return StatusCode::INVALID_ARG;
+        StatusCode errorCode = getErrorCode(writeResult);
+        ALOGE("failed to write value into property store, error: %s, code: %d",
+              getErrorMsg(writeResult).c_str(), errorCode);
+        return errorCode;
     }
 
     VehiclePropValuePool::RecyclableType prop;
@@ -168,9 +208,10 @@
             if (auto writeResult =
                         mServerSidePropStore->writeValue(std::move(prop), /*updateStatus=*/true);
                 !writeResult.ok()) {
-                ALOGE("failed to write AP_POWER_STATE_REQ into property store, error: %s",
-                      writeResult.error().message().c_str());
-                return StatusCode::INTERNAL_ERROR;
+                StatusCode errorCode = getErrorCode(writeResult);
+                ALOGE("failed to write AP_POWER_STATE_REQ into property store, error: %s, code: %d",
+                      getErrorMsg(writeResult).c_str(), errorCode);
+                return errorCode;
             }
             break;
         case toInt(VehicleApPowerStateReport::DEEP_SLEEP_ENTRY):
@@ -185,9 +226,10 @@
             if (auto writeResult =
                         mServerSidePropStore->writeValue(std::move(prop), /*updateStatus=*/true);
                 !writeResult.ok()) {
-                ALOGE("failed to write AP_POWER_STATE_REQ into property store, error: %s",
-                      writeResult.error().message().c_str());
-                return StatusCode::INTERNAL_ERROR;
+                StatusCode errorCode = getErrorCode(writeResult);
+                ALOGE("failed to write AP_POWER_STATE_REQ into property store, error: %s, code: %d",
+                      getErrorMsg(writeResult).c_str(), errorCode);
+                return errorCode;
             }
             break;
         default:
@@ -197,6 +239,35 @@
     return StatusCode::OK;
 }
 
+Result<VehiclePropValuePool::RecyclableType> FakeVehicleHardware::maybeGetSpecialValue(
+        const VehiclePropValue& value, bool* isSpecialValue) const {
+    *isSpecialValue = false;
+    int32_t propId = value.prop;
+    Result<VehiclePropValuePool::RecyclableType> result;
+
+    switch (propId) {
+        case OBD2_FREEZE_FRAME:
+            *isSpecialValue = true;
+            result = mFakeObd2Frame->getObd2FreezeFrame(value);
+            if (result.ok()) {
+                result.value()->timestamp = elapsedRealtimeNano();
+            }
+            return result;
+        case OBD2_FREEZE_FRAME_INFO:
+            *isSpecialValue = true;
+            result = mFakeObd2Frame->getObd2DtcInfo();
+            if (result.ok()) {
+                result.value()->timestamp = elapsedRealtimeNano();
+            }
+            return result;
+        default:
+            // Do nothing.
+            break;
+    }
+
+    return nullptr;
+}
+
 StatusCode FakeVehicleHardware::maybeSetSpecialValue(const VehiclePropValue& value,
                                                      bool* isSpecialValue) {
     *isSpecialValue = false;
@@ -206,6 +277,14 @@
         case toInt(VehicleProperty::AP_POWER_STATE_REPORT):
             *isSpecialValue = true;
             return setApPowerStateReport(value);
+        case toInt(VehicleProperty::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.
+            *isSpecialValue = true;
+            return StatusCode::OK;
+        case OBD2_FREEZE_FRAME_CLEAR:
+            *isSpecialValue = true;
+            return mFakeObd2Frame->clearObd2FreezeFrames(value);
 
 #ifdef ENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING
         case toInt(VehicleProperty::CLUSTER_REPORT_STATE):
@@ -230,9 +309,10 @@
             updatedValue->areaId = value.areaId;
             if (auto writeResult = mServerSidePropStore->writeValue(std::move(updatedValue));
                 !writeResult.ok()) {
-                ALOGE("failed to write value into property store, error: %s",
-                      writeResult.error().message().c_str());
-                return StatusCode::INVALID_ARG;
+                StatusCode errorCode = getErrorCode(writeResult);
+                ALOGE("failed to write value into property store, error: %s, code: %d",
+                      getErrorMsg(writeResult).c_str(), errorCode);
+                return errorCode;
             }
             return StatusCode::OK;
 #endif  // ENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING
@@ -278,9 +358,10 @@
 
         auto writeResult = mServerSidePropStore->writeValue(std::move(updatedValue));
         if (!writeResult.ok()) {
+            StatusCode errorCode = getErrorCode(writeResult);
             ALOGE("failed to write value into property store, error: %s, code: %d",
-                  writeResult.error().message().c_str(), writeResult.error().code());
-            setValueResult.status = StatusCode::INVALID_ARG;
+                  getErrorMsg(writeResult).c_str(), errorCode);
+            setValueResult.status = errorCode;
         }
         results.push_back(std::move(setValueResult));
     }
@@ -296,22 +377,38 @@
                                           const std::vector<GetValueRequest>& requests) const {
     std::vector<GetValueResult> results;
     for (auto& request : requests) {
-        const VehiclePropValue* value = &request.prop;
-        ALOGD("getValues(%d)", value->prop);
+        const VehiclePropValue& value = request.prop;
+        ALOGD("getValues(%d)", value.prop);
 
-        auto readResult = mServerSidePropStore->readValue(*value);
         GetValueResult getValueResult;
         getValueResult.requestId = request.requestId;
-        if (!readResult.ok()) {
-            auto error = readResult.error();
-            if (error.code() == toInt(StatusCode::NOT_AVAILABLE)) {
-                ALOGW("%s", "value has not been set yet");
-                getValueResult.status = StatusCode::NOT_AVAILABLE;
+        bool isSpecialValue = false;
+
+        auto result = maybeGetSpecialValue(value, &isSpecialValue);
+        if (isSpecialValue) {
+            if (!result.ok()) {
+                StatusCode errorCode = getErrorCode(result);
+                ALOGE("failed to get special value: %d, error: %s, code: %d", value.prop,
+                      getErrorMsg(result).c_str(), errorCode);
+                getValueResult.status = errorCode;
             } else {
-                ALOGE("failed to get value, error: %s, code: %d", error.message().c_str(),
-                      error.code());
-                getValueResult.status = StatusCode::INVALID_ARG;
+                getValueResult.status = StatusCode::OK;
+                getValueResult.prop = *result.value();
             }
+            results.push_back(std::move(getValueResult));
+            continue;
+        }
+
+        auto readResult = mServerSidePropStore->readValue(value);
+        if (!readResult.ok()) {
+            StatusCode errorCode = getErrorCode(readResult);
+            if (errorCode == StatusCode::NOT_AVAILABLE) {
+                ALOGW("%s", "value has not been set yet");
+            } else {
+                ALOGE("failed to get value, error: %s, code: %d", getErrorMsg(readResult).c_str(),
+                      errorCode);
+            }
+            getValueResult.status = errorCode;
         } else {
             getValueResult.status = StatusCode::OK;
             getValueResult.prop = *readResult.value();
@@ -378,8 +475,8 @@
                 if (auto result = mServerSidePropStore->writeValue(std::move(propToStore),
                                                                    /*updateStatus=*/true);
                     !result.ok()) {
-                    ALOGW("failed to write vendor override properties: %d, error: %s", prop.prop,
-                          result.error().message().c_str());
+                    ALOGW("failed to write vendor override properties: %d, error: %s, code: %d",
+                          prop.prop, getErrorMsg(result).c_str(), getErrorCode(result));
                 }
             }
         }
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp
index 3399317..b40c906 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp
@@ -32,6 +32,7 @@
         "VehicleHalUtils",
         "FakeVehicleHardware",
         "FakeVehicleHalValueGenerators",
+        "FakeObd2Frame",
         "libgtest",
         "libgmock",
     ],
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 523214c..6f59ce7 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
@@ -17,6 +17,8 @@
 #include <FakeVehicleHardware.h>
 
 #include <DefaultConfig.h>
+#include <FakeObd2Frame.h>
+#include <PropertyUtils.h>
 #include <TestPropertyUtils.h>
 
 #include <android-base/expected.h>
@@ -249,6 +251,12 @@
     int64_t requestId = 1;
 
     for (auto& config : defaultconfig::getDefaultConfigs()) {
+        if (obd2frame::FakeObd2Frame::isDiagnosticProperty(config.config)) {
+            // Ignore storing default value for diagnostic property. They have special get/set
+            // logic.
+            continue;
+        }
+
         int propId = config.config.prop;
         if (isGlobalProp(propId)) {
             if (config.initialValue == RawPropValues{}) {
@@ -901,6 +909,67 @@
             return info.param.name;
         });
 
+TEST_F(FakeVehicleHardwareTest, testGetObd2FreezeFrame) {
+    int64_t timestamp = elapsedRealtimeNano();
+
+    auto result = getValue(VehiclePropValue{.prop = OBD2_FREEZE_FRAME_INFO});
+
+    ASSERT_TRUE(result.ok());
+
+    auto propValue = result.value();
+    ASSERT_GE(propValue.timestamp, timestamp);
+    ASSERT_EQ(propValue.value.int64Values.size(), static_cast<size_t>(3))
+            << "expect 3 obd2 freeze frames stored";
+
+    for (int64_t timestamp : propValue.value.int64Values) {
+        auto freezeFrameResult = getValue(VehiclePropValue{
+                .prop = OBD2_FREEZE_FRAME,
+                .value.int64Values = {timestamp},
+        });
+
+        EXPECT_TRUE(result.ok()) << "expect to get freeze frame for timestamp " << timestamp
+                                 << " ok";
+        EXPECT_GE(freezeFrameResult.value().timestamp, timestamp);
+    }
+}
+
+TEST_F(FakeVehicleHardwareTest, testClearObd2FreezeFrame) {
+    int64_t timestamp = elapsedRealtimeNano();
+
+    auto getValueResult = getValue(VehiclePropValue{.prop = OBD2_FREEZE_FRAME_INFO});
+
+    ASSERT_TRUE(getValueResult.ok());
+
+    auto propValue = getValueResult.value();
+    ASSERT_GE(propValue.timestamp, timestamp);
+    ASSERT_EQ(propValue.value.int64Values.size(), static_cast<size_t>(3))
+            << "expect 3 obd2 freeze frames stored";
+
+    // No int64Values should clear all freeze frames.
+    StatusCode status = setValue(VehiclePropValue{.prop = OBD2_FREEZE_FRAME_CLEAR});
+
+    ASSERT_EQ(status, StatusCode::OK);
+
+    getValueResult = getValue(VehiclePropValue{.prop = OBD2_FREEZE_FRAME_INFO});
+
+    ASSERT_TRUE(getValueResult.ok());
+    ASSERT_EQ(getValueResult.value().value.int64Values.size(), static_cast<size_t>(0))
+            << "expect 0 obd2 freeze frames after cleared";
+}
+
+TEST_F(FakeVehicleHardwareTest, testSetVehicleMapService) {
+    StatusCode status =
+            setValue(VehiclePropValue{.prop = toInt(VehicleProperty::VEHICLE_MAP_SERVICE)});
+
+    EXPECT_EQ(status, StatusCode::OK);
+
+    auto getValueResult =
+            getValue(VehiclePropValue{.prop = toInt(VehicleProperty::VEHICLE_MAP_SERVICE)});
+
+    EXPECT_FALSE(getValueResult.ok());
+    EXPECT_EQ(getValueResult.error(), StatusCode::NOT_AVAILABLE);
+}
+
 }  // namespace fake
 }  // namespace vehicle
 }  // namespace automotive