Adding a custom property to the default VHAL impl

This property is supposed to be used mostly from e2e tests (a simple
test-case is provided in separate CL)

Test: make -j && runtest -x packages/services/Car/tests/vehiclehal_test/

Bug: b/36510399
Change-Id: I09b24f22ab328eee1ef6add60901ed03bf046874
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index bf16a9b..c4f935b 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -28,6 +28,25 @@
 
 namespace impl {
 
+/*
+ * This property is used for test purpose to generate fake events.
+ *
+ * It has the following format:
+ *
+ * int32Values[0] - command (1 - start fake data generation, 0 - stop)
+ * int32Values[1] - VehicleProperty to which command applies
+ *
+ * For start command, additional data should be provided:
+ *   int64Values[0] - periodic interval in nanoseconds
+ *   floatValues[0] - initial value
+ *   floatValues[1] - dispersion defines min and max range relative to initial value
+ *   floatValues[2] - increment, with every timer tick the value will be incremented by this amount
+ */
+const int32_t kGenerateFakeDataControllingProperty = 0x0666
+        | VehiclePropertyGroup::VENDOR
+        | VehicleArea::GLOBAL
+        | VehiclePropertyType::COMPLEX;
+
 const int32_t kHvacPowerProperties[] = {
     toInt(VehicleProperty::HVAC_FAN_SPEED),
     toInt(VehicleProperty::HVAC_FAN_DIRECTION),
@@ -64,6 +83,24 @@
 
     {
         .config = {
+            .prop = toInt(VehicleProperty::PERF_ODOMETER),
+            .access = VehiclePropertyAccess::READ,
+            .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+        },
+        .initialValue = { .floatValues = {0.0f} }
+    },
+
+    {
+        .config = {
+            .prop = toInt(VehicleProperty::ENGINE_RPM),
+            .access = VehiclePropertyAccess::READ,
+            .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
+        },
+        .initialValue = { .floatValues = {0.0f} }
+    },
+
+    {
+        .config = {
             .prop = toInt(VehicleProperty::CURRENT_GEAR),
             .access = VehiclePropertyAccess::READ,
             .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
@@ -284,6 +321,14 @@
             .maxSampleRate = 10,  // 10 Hz, every 100 ms
         },
         .initialValue = { .floatValues = {101.0f} }
+    },
+
+    {
+        .config = {
+            .prop = kGenerateFakeDataControllingProperty,
+            .access = VehiclePropertyAccess::WRITE,
+            .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+        },
     }
 };
 
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
index 0ac6ada..ea40cc5 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
@@ -27,11 +27,18 @@
 
 namespace impl {
 
+enum class FakeDataCommand : int32_t {
+    Stop = 0,
+    Start = 1,
+};
+
 EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore)
     : mPropStore(propStore),
       mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)),
       mRecurrentTimer(std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer,
-                                  this, std::placeholders::_1)) {
+                                  this, std::placeholders::_1)),
+      mFakeValueGenerator(std::bind(&EmulatedVehicleHal::onFakeValueGenerated,
+                                    this, std::placeholders::_1, std::placeholders::_2)) {
 
     for (size_t i = 0; i < arraysize(kVehicleProperties); i++) {
         mPropStore->registerProperty(kVehicleProperties[i].config);
@@ -52,6 +59,10 @@
 }
 
 StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) {
+    if (propValue.prop == kGenerateFakeDataControllingProperty) {
+        return handleGenerateFakeDataRequest(propValue);
+    };
+
     if (mHvacPowerProps.count(propValue.prop)) {
         auto hvacPowerOn = mPropStore->readValueOrNull(toInt(VehicleProperty::HVAC_POWER_ON),
                                                       toInt(VehicleAreaZone::ROW_1));
@@ -176,6 +187,81 @@
     return mPropStore->readAllValues();
 }
 
+StatusCode EmulatedVehicleHal::handleGenerateFakeDataRequest(const VehiclePropValue& request) {
+    ALOGI("%s", __func__);
+    const auto& v = request.value;
+    if (v.int32Values.size() < 2) {
+        ALOGE("%s: expected at least 2 elements in int32Values, got: %zu", __func__,
+                v.int32Values.size());
+        return StatusCode::INVALID_ARG;
+    }
+
+    FakeDataCommand command = static_cast<FakeDataCommand>(v.int32Values[0]);
+    int32_t propId = v.int32Values[1];
+
+    switch (command) {
+        case FakeDataCommand::Start: {
+            if (!v.int64Values.size()) {
+                ALOGE("%s: interval is not provided in int64Values", __func__);
+                return StatusCode::INVALID_ARG;
+            }
+            auto interval = std::chrono::nanoseconds(v.int64Values[0]);
+
+            if (v.floatValues.size() < 3) {
+                ALOGE("%s: expected at least 3 element sin floatValues, got: %zu", __func__,
+                        v.floatValues.size());
+                return StatusCode::INVALID_ARG;
+            }
+            float initialValue = v.floatValues[0];
+            float dispersion = v.floatValues[1];
+            float increment = v.floatValues[2];
+
+            ALOGI("%s, propId: %d, initalValue: %f", __func__, propId, initialValue);
+            mFakeValueGenerator.startGeneratingHalEvents(
+                interval, propId, initialValue, dispersion, increment);
+
+            break;
+        }
+        case FakeDataCommand::Stop: {
+            ALOGI("%s, FakeDataCommandStop", __func__);
+            mFakeValueGenerator.stopGeneratingHalEvents(propId);
+            break;
+        }
+        default: {
+            ALOGE("%s: unexpected command: %d", __func__, command);
+            return StatusCode::INVALID_ARG;
+        }
+    }
+    return StatusCode::OK;
+}
+
+void EmulatedVehicleHal::onFakeValueGenerated(int32_t propId, float value) {
+    VehiclePropValuePtr updatedPropValue {};
+    switch (getPropType(propId)) {
+        case VehiclePropertyType::FLOAT:
+            updatedPropValue = getValuePool()->obtainFloat(value);
+            break;
+        case VehiclePropertyType::INT32:
+            updatedPropValue = getValuePool()->obtainInt32(static_cast<int32_t>(value));
+            break;
+        default:
+            ALOGE("%s: data type for property: 0x%x not supported", __func__, propId);
+            return;
+
+    }
+
+    if (updatedPropValue) {
+        updatedPropValue->prop = propId;
+        updatedPropValue->areaId = 0;  // Add area support if necessary.
+        updatedPropValue->timestamp = elapsedRealtimeNano();
+        mPropStore->writeValue(*updatedPropValue);
+        auto changeMode = mPropStore->getConfigOrDie(propId)->changeMode;
+        if (VehiclePropertyChangeMode::ON_CHANGE == changeMode) {
+            doHalEvent(move(updatedPropValue));
+        }
+    }
+}
+
 }  // impl
 
 }  // namespace V2_0
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
index e0874e2..009485d 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
@@ -34,6 +34,7 @@
 #include "DefaultConfig.h"
 #include "VehicleHalProto.pb.h"
 #include "VehicleEmulator.h"
+#include "FakeValueGenerator.h"
 
 namespace android {
 namespace hardware {
@@ -67,6 +68,9 @@
         return std::chrono::nanoseconds(static_cast<int64_t>(1000000000L / hz));
     }
 
+    StatusCode handleGenerateFakeDataRequest(const VehiclePropValue& request);
+    void onFakeValueGenerated(int32_t propId, float value);
+
     void onContinuousPropertyTimer(const std::vector<int32_t>& properties);
     bool isContinuousProperty(int32_t propId) const;
 
@@ -74,6 +78,7 @@
     VehiclePropertyStore* mPropStore;
     std::unordered_set<int32_t> mHvacPowerProps;
     RecurrentTimer mRecurrentTimer;
+    FakeValueGenerator mFakeValueGenerator;
 };
 
 }  // impl
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeValueGenerator.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeValueGenerator.h
new file mode 100644
index 0000000..7bbbb08
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeValueGenerator.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef android_hardware_automotive_vehicle_V2_0_impl_FakeHalEventGenerator_H_
+#define android_hardware_automotive_vehicle_V2_0_impl_FakeHalEventGenerator_H_
+
+#include <chrono>
+
+#include <android/hardware/automotive/vehicle/2.0/types.h>
+
+#include <vhal_v2_0/RecurrentTimer.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+namespace impl {
+
+class FakeValueGenerator {
+private:
+    // In every timer tick we may want to generate new value based on initial value for debug
+    // purpose. It's better to have sequential values to see if events gets delivered in order
+    // to the client.
+
+    struct GeneratorCfg {
+        float initialValue;  //
+        float currentValue;  //  Should be in range (initialValue +/- dispersion).
+        float dispersion;    //  Defines minimum and maximum value based on initial value.
+        float increment;     //  Value that we will be added to currentValue with each timer tick.
+    };
+
+public:
+    using OnHalEvent = std::function<void(int32_t propId, float value)>;
+
+    FakeValueGenerator(const OnHalEvent& onHalEvent) :
+        mOnHalEvent(onHalEvent),
+        mRecurrentTimer(std::bind(&FakeValueGenerator::onTimer, this,
+                                  std::placeholders::_1))
+    {}
+
+    ~FakeValueGenerator() = default;
+
+
+    void startGeneratingHalEvents(std::chrono::nanoseconds interval, int propId, float initialValue,
+                                  float dispersion, float increment) {
+        MuxGuard g(mLock);
+
+        removeLocked(propId);
+
+        mGenCfg.insert({propId, GeneratorCfg {
+            .initialValue = initialValue,
+            .currentValue = initialValue,
+            .dispersion = dispersion,
+            .increment = increment,
+        }});
+
+        mRecurrentTimer.registerRecurrentEvent(interval, propId);
+    }
+
+    void stopGeneratingHalEvents(int propId) {
+        MuxGuard g(mLock);
+        if (propId == 0) {
+            // Remove all.
+            for (auto&& it : mGenCfg) {
+                removeLocked(it.first);
+            }
+        } else {
+            removeLocked(propId);
+        }
+    }
+
+private:
+    void removeLocked(int propId) {
+        if (mGenCfg.erase(propId)) {
+            mRecurrentTimer.unregisterRecurrentEvent(propId);
+        }
+    }
+
+    void onTimer(const std::vector<int32_t>& properties) {
+        MuxGuard g(mLock);
+
+        for (int32_t propId : properties) {
+            auto& cfg = mGenCfg[propId];
+            cfg.currentValue += cfg.increment;
+            if (cfg.currentValue > cfg.initialValue + cfg.dispersion) {
+                cfg.currentValue = cfg.initialValue - cfg.dispersion;
+            }
+            mOnHalEvent(propId, cfg.currentValue);
+        }
+    }
+
+private:
+    using MuxGuard = std::lock_guard<std::mutex>;
+
+    mutable std::mutex mLock;
+    OnHalEvent mOnHalEvent;
+    RecurrentTimer mRecurrentTimer;
+    std::unordered_map<int32_t, GeneratorCfg> mGenCfg;
+};
+
+
+}  // impl
+
+}  // namespace V2_0
+}  // namespace vehicle
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+
+
+#endif //android_hardware_automotive_vehicle_V2_0_impl_FakeHalEventGenerator_H_