Extend onDump to support debug options.

Extend onDump to support debugging. This CL only adds
genFakeData option.

Test: atest android.hardware.automotive.vehicle@2.0-default-impl-unit-tests
Bug: 193565753
Change-Id: I11d5d0f12c0ef050544c798bff2b4b27962bd3df
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index aabc60c..2e8ca66 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -197,6 +197,7 @@
     static_libs: [
         "libbase",
         "libcutils",
+        "libgmock",
         "libjsoncpp",
         "libprotobuf-cpp-lite",
     ],
@@ -212,6 +213,7 @@
         "android.hardware.automotive.vehicle@2.0-default-impl-lib",
         "android.hardware.automotive.vehicle@2.0-libproto-native",
     ],
+    data: [":vhal_test_json"],
     test_suites: ["general-tests"],
 }
 
@@ -233,7 +235,7 @@
     static_libs: [
         "android.hardware.automotive.vehicle@2.0-manager-lib",
         "android.hardware.automotive.vehicle@2.0-libproto-native",
-        "//device/generic/car/emulator/vhal_v2_0:android.hardware.automotive.vehicle@2.0-emulator-impl-lib",
+        "android.hardware.automotive.vehicle@2.0-emulator-impl-lib",
     ],
 }
 
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 66849bc..f61a93b 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
@@ -20,6 +20,8 @@
 
 #include <android-base/format.h>
 #include <android-base/logging.h>
+#include <android-base/parsedouble.h>
+#include <android-base/parseint.h>
 #include <utils/SystemClock.h>
 
 #include "DefaultConfig.h"
@@ -82,7 +84,7 @@
     }
 }
 
-GeneratorHub* DefaultVehicleHalServer::getGenerator() {
+GeneratorHub* DefaultVehicleHalServer::getGeneratorHub() {
     return &mGeneratorHub;
 }
 
@@ -145,8 +147,8 @@
                 return StatusCode::INVALID_ARG;
             }
             int32_t cookie = v.int32Values[1];
-            getGenerator()->registerGenerator(cookie,
-                                              std::make_unique<LinearFakeValueGenerator>(request));
+            getGeneratorHub()->registerGenerator(
+                    cookie, std::make_unique<LinearFakeValueGenerator>(request));
             break;
         }
         case FakeDataCommand::StartJson: {
@@ -156,8 +158,12 @@
                 return StatusCode::INVALID_ARG;
             }
             int32_t cookie = std::hash<std::string>()(v.stringValue);
-            getGenerator()->registerGenerator(cookie,
-                                              std::make_unique<JsonFakeValueGenerator>(request));
+            auto generator = std::make_unique<JsonFakeValueGenerator>(request);
+            if (!generator->hasNext()) {
+                LOG(ERROR) << __func__ << ": invalid JSON file, no events";
+                return StatusCode::INVALID_ARG;
+            }
+            getGeneratorHub()->registerGenerator(cookie, std::move(generator));
             break;
         }
         case FakeDataCommand::StopLinear: {
@@ -167,7 +173,7 @@
                 return StatusCode::INVALID_ARG;
             }
             int32_t cookie = v.int32Values[1];
-            getGenerator()->unregisterGenerator(cookie);
+            getGeneratorHub()->unregisterGenerator(cookie);
             break;
         }
         case FakeDataCommand::StopJson: {
@@ -177,7 +183,7 @@
                 return StatusCode::INVALID_ARG;
             }
             int32_t cookie = std::hash<std::string>()(v.stringValue);
-            getGenerator()->unregisterGenerator(cookie);
+            getGeneratorHub()->unregisterGenerator(cookie);
             break;
         }
         case FakeDataCommand::KeyPress: {
@@ -331,17 +337,225 @@
 }
 
 IVehicleServer::DumpResult DefaultVehicleHalServer::onDump(
-        const std::vector<std::string>& /* options */) {
+        const std::vector<std::string>& options) {
     DumpResult result;
-    result.callerShouldDumpState = true;
-
-    result.buffer += "Server side properties: \n";
-    auto values = mServerSidePropStore.readAllValues();
-    size_t i = 0;
-    for (const auto& value : values) {
-        result.buffer += fmt::format("[{}]: {}\n", i, toString(value));
-        i++;
+    if (options.size() == 0) {
+        // No options, dump all stored properties.
+        result.callerShouldDumpState = true;
+        result.buffer += "Server side properties: \n";
+        auto values = mServerSidePropStore.readAllValues();
+        size_t i = 0;
+        for (const auto& value : values) {
+            result.buffer += fmt::format("[{}]: {}\n", i, toString(value));
+            i++;
+        }
+        return result;
     }
+    if (options[0] != "--debughal") {
+        // We only expect "debughal" command. This might be some commands that the caller knows
+        // about, so let caller handle it.
+        result.callerShouldDumpState = true;
+        return result;
+    }
+
+    return debug(options);
+}
+
+IVehicleServer::DumpResult DefaultVehicleHalServer::debug(const std::vector<std::string>& options) {
+    DumpResult result;
+    // This is a debug command for the HAL, caller should not continue to dump state.
+    result.callerShouldDumpState = false;
+
+    if (options.size() < 2) {
+        result.buffer += "No command specified\n";
+        result.buffer += getHelpInfo();
+        return result;
+    }
+
+    std::string command = options[1];
+    if (command == "--help") {
+        result.buffer += getHelpInfo();
+        return result;
+    } else if (command == "--genfakedata") {
+        return genFakeData(options);
+    }
+
+    result.buffer += "Unknown command: \"" + command + "\"\n";
+    result.buffer += getHelpInfo();
+    return result;
+}
+
+std::string DefaultVehicleHalServer::getHelpInfo() {
+    return "Help: \n"
+           "Generate Fake Data: \n"
+           "\tStart a linear generator: \n"
+           "\t--debughal --genfakedata --startlinear [propID(int32)] [middleValue(float)] "
+           "[currentValue(float)] [dispersion(float)] [increment(float)] [interval(int64)]\n"
+           "\tStop a linear generator: \n"
+           "\t--debughal --genfakedata --stoplinear [propID(int32)]\n"
+           "\tStart a json generator: \n"
+           "\t--debughal --genfakedata --startjson [jsonFilePath(string)] "
+           "[repetition(int32)(optional)]\n"
+           "\tStop a json generator: \n"
+           "\t--debughal --genfakedata --stopjson [jsonFilePath(string)]\n"
+           "\tGenerate key press: \n"
+           "\t--debughal --genfakedata --keypress [keyCode(int32)] [display[int32]]\n";
+}
+
+IVehicleServer::DumpResult DefaultVehicleHalServer::genFakeData(
+        const std::vector<std::string>& options) {
+    DumpResult result;
+    // This is a debug command for the HAL, caller should not continue to dump state.
+    result.callerShouldDumpState = false;
+
+    if (options.size() < 3) {
+        result.buffer += "No subcommand specified for genfakedata\n";
+        result.buffer += getHelpInfo();
+        return result;
+    }
+
+    std::string command = options[2];
+    if (command == "--startlinear") {
+        LOG(INFO) << __func__ << "FakeDataCommand::StartLinear";
+        // --debughal --genfakedata --startlinear [propID(int32)] [middleValue(float)]
+        // [currentValue(float)] [dispersion(float)] [increment(float)] [interval(int64)]
+        if (options.size() != 9) {
+            result.buffer +=
+                    "incorrect argument count, need 9 arguments for --genfakedata --startlinear\n";
+            result.buffer += getHelpInfo();
+            return result;
+        }
+        int32_t propId;
+        float middleValue;
+        float currentValue;
+        float dispersion;
+        float increment;
+        int64_t interval;
+        if (!android::base::ParseInt(options[3], &propId)) {
+            result.buffer += "failed to parse propdID as int: \"" + options[3] + "\"\n";
+            result.buffer += getHelpInfo();
+            return result;
+        }
+        if (!android::base::ParseFloat(options[4], &middleValue)) {
+            result.buffer += "failed to parse middleValue as float: \"" + options[4] + "\"\n";
+            result.buffer += getHelpInfo();
+            return result;
+        }
+        if (!android::base::ParseFloat(options[5], &currentValue)) {
+            result.buffer += "failed to parse currentValue as float: \"" + options[5] + "\"\n";
+            result.buffer += getHelpInfo();
+            return result;
+        }
+        if (!android::base::ParseFloat(options[6], &dispersion)) {
+            result.buffer += "failed to parse dispersion as float: \"" + options[6] + "\"\n";
+            result.buffer += getHelpInfo();
+            return result;
+        }
+        if (!android::base::ParseFloat(options[7], &increment)) {
+            result.buffer += "failed to parse increment as float: \"" + options[7] + "\"\n";
+            result.buffer += getHelpInfo();
+            return result;
+        }
+        if (!android::base::ParseInt(options[8], &interval)) {
+            result.buffer += "failed to parse interval as int: \"" + options[8] + "\"\n";
+            result.buffer += getHelpInfo();
+            return result;
+        }
+        auto generator = std::make_unique<LinearFakeValueGenerator>(
+                propId, middleValue, currentValue, dispersion, increment, interval);
+        getGeneratorHub()->registerGenerator(propId, std::move(generator));
+        return result;
+    } else if (command == "--stoplinear") {
+        LOG(INFO) << __func__ << "FakeDataCommand::StopLinear";
+        // --debughal --genfakedata --stoplinear [propID(int32)]
+        if (options.size() != 4) {
+            result.buffer +=
+                    "incorrect argument count, need 4 arguments for --genfakedata --stoplinear\n";
+            result.buffer += getHelpInfo();
+            return result;
+        }
+        int32_t propId;
+        if (!android::base::ParseInt(options[3], &propId)) {
+            result.buffer += "failed to parse propdID as int: \"" + options[3] + "\"\n";
+            result.buffer += getHelpInfo();
+            return result;
+        }
+        getGeneratorHub()->unregisterGenerator(propId);
+        return result;
+    } else if (command == "--startjson") {
+        LOG(INFO) << __func__ << "FakeDataCommand::StartJson";
+        // --debughal --genfakedata --startjson [jsonFilePath(string)] [repetition(int32)(optional)]
+        if (options.size() != 4 && options.size() != 5) {
+            result.buffer +=
+                    "incorrect argument count, need 4 or 5 arguments for --genfakedata "
+                    "--startjson\n";
+            result.buffer += getHelpInfo();
+            return result;
+        }
+        std::string fileName = options[3];
+        int32_t cookie = std::hash<std::string>()(fileName);
+        // Iterate infinitely if repetition number is not provided
+        int32_t repetition = -1;
+        if (options.size() == 5) {
+            if (!android::base::ParseInt(options[4], &repetition)) {
+                result.buffer += "failed to parse repetition as int: \"" + options[4] + "\"\n";
+                result.buffer += getHelpInfo();
+                return result;
+            }
+        }
+        auto generator = std::make_unique<JsonFakeValueGenerator>(fileName, repetition);
+        if (!generator->hasNext()) {
+            result.buffer += "invalid JSON file, no events";
+            return result;
+        }
+        getGeneratorHub()->registerGenerator(cookie, std::move(generator));
+        return result;
+    } else if (command == "--stopjson") {
+        LOG(INFO) << __func__ << "FakeDataCommand::StopJson";
+        // --debughal --genfakedata --stopjson [jsonFilePath(string)]
+        if (options.size() != 4) {
+            result.buffer +=
+                    "incorrect argument count, need 4 arguments for --genfakedata --stopjson\n";
+            result.buffer += getHelpInfo();
+            return result;
+        }
+        std::string fileName = options[3];
+        int32_t cookie = std::hash<std::string>()(fileName);
+        getGeneratorHub()->unregisterGenerator(cookie);
+        return result;
+    } else if (command == "--keypress") {
+        LOG(INFO) << __func__ << "FakeDataCommand::KeyPress";
+        int32_t keyCode;
+        int32_t display;
+        // --debughal --genfakedata --keypress [keyCode(int32)] [display[int32]]
+        if (options.size() != 5) {
+            result.buffer +=
+                    "incorrect argument count, need 5 arguments for --genfakedata --keypress\n";
+            result.buffer += getHelpInfo();
+            return result;
+        }
+        if (!android::base::ParseInt(options[3], &keyCode)) {
+            result.buffer += "failed to parse keyCode as int: \"" + options[3] + "\"\n";
+            result.buffer += getHelpInfo();
+            return result;
+        }
+        if (!android::base::ParseInt(options[4], &display)) {
+            result.buffer += "failed to parse display as int: \"" + options[4] + "\"\n";
+            result.buffer += getHelpInfo();
+            return result;
+        }
+        // Send back to HAL
+        onPropertyValueFromCar(
+                *createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_DOWN, keyCode, display),
+                /*updateStatus=*/true);
+        onPropertyValueFromCar(
+                *createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display),
+                /*updateStatus=*/true);
+        return result;
+    }
+
+    result.buffer += "Unknown command: \"" + command + "\"\n";
+    result.buffer += getHelpInfo();
     return result;
 }
 
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 1a42cb8..aa6cf0b 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
@@ -52,7 +52,7 @@
 
   protected:
     using VehiclePropValuePtr = recyclable_ptr<VehiclePropValue>;
-    GeneratorHub* getGenerator();
+    GeneratorHub* getGeneratorHub();
 
     VehiclePropValuePool* getValuePool() const;
 
@@ -67,6 +67,12 @@
 
     void storePropInitialValue(const ConfigDeclaration& config);
 
+    DumpResult debug(const std::vector<std::string>& options);
+
+    std::string getHelpInfo();
+
+    DumpResult genFakeData(const std::vector<std::string>& options);
+
   protected:
     GeneratorHub mGeneratorHub{
             [this](const VehiclePropValue& value) { return onFakeValueGenerated(value); }};
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.cpp
index b62918f..b728d62 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.cpp
@@ -33,12 +33,37 @@
 
 namespace impl {
 
+JsonFakeValueGenerator::JsonFakeValueGenerator(const std::string& path, int32_t repetition) {
+    const char* file = path.c_str();
+    std::ifstream ifs(file);
+    if (!ifs) {
+        ALOGE("%s: couldn't open %s for parsing.", __func__, file);
+        mGenCfg = {
+                .index = 0,
+                .events = {},
+        };
+        mNumOfIterations = 0;
+        return;
+    }
+    mGenCfg = {
+            .index = 0,
+            .events = parseFakeValueJson(ifs),
+    };
+    mNumOfIterations = repetition;
+}
+
 JsonFakeValueGenerator::JsonFakeValueGenerator(const VehiclePropValue& request) {
     const auto& v = request.value;
     const char* file = v.stringValue.c_str();
     std::ifstream ifs(file);
     if (!ifs) {
         ALOGE("%s: couldn't open %s for parsing.", __func__, file);
+        mGenCfg = {
+                .index = 0,
+                .events = {},
+        };
+        mNumOfIterations = 0;
+        return;
     }
     mGenCfg = {
         .index = 0,
@@ -48,10 +73,16 @@
     mNumOfIterations = v.int32Values.size() < 2 ? -1 : v.int32Values[1];
 }
 
-JsonFakeValueGenerator::JsonFakeValueGenerator(std::string path) {
+JsonFakeValueGenerator::JsonFakeValueGenerator(const std::string& path) {
     std::ifstream ifs(path);
     if (!ifs) {
         ALOGE("%s: couldn't open %s for parsing.", __func__, path.c_str());
+        mGenCfg = {
+                .index = 0,
+                .events = {},
+        };
+        mNumOfIterations = 0;
+        return;
     }
     mGenCfg = {
         .index = 0,
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.h
index dc8ff66..caa3417 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.h
@@ -41,7 +41,8 @@
 
 public:
     JsonFakeValueGenerator(const VehiclePropValue& request);
-    JsonFakeValueGenerator(std::string path);
+    JsonFakeValueGenerator(const std::string& path, int32_t repetition);
+    JsonFakeValueGenerator(const std::string& path);
 
     ~JsonFakeValueGenerator() = default;
 
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/LinearFakeValueGenerator.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/LinearFakeValueGenerator.cpp
index 96aaafe..a2278bd 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/LinearFakeValueGenerator.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/LinearFakeValueGenerator.cpp
@@ -29,23 +29,36 @@
 
 namespace impl {
 
+LinearFakeValueGenerator::LinearFakeValueGenerator(int32_t propId, float middleValue,
+                                                   float currentValue, float dispersion,
+                                                   float increment, int64_t interval) {
+    initGenCfg(propId, middleValue, currentValue, dispersion, increment, interval);
+}
+
 LinearFakeValueGenerator::LinearFakeValueGenerator(const VehiclePropValue& request) {
     const auto& v = request.value;
+    initGenCfg(v.int32Values[1], v.floatValues[0], v.floatValues[0], v.floatValues[1],
+               v.floatValues[2], v.int64Values[0]);
+}
+
+void LinearFakeValueGenerator::initGenCfg(int32_t propId, float middleValue, float currentValue,
+                                          float dispersion, float increment, int64_t interval) {
+    if (currentValue < middleValue - dispersion || currentValue >= middleValue + dispersion) {
+        ALOGW("%s: invalid initValue: %f, out of range, default to %f", __func__, currentValue,
+              middleValue);
+        currentValue = middleValue;
+    }
     mGenCfg = GeneratorCfg{
-        .propId = v.int32Values[1],
-        .initialValue = v.floatValues[0],
-        .currentValue = v.floatValues[0],
-        .dispersion = v.floatValues[1],
-        .increment = v.floatValues[2],
-        .interval = Nanos(v.int64Values[0]),
+            .propId = propId,
+            .middleValue = middleValue,
+            .currentValue = currentValue,
+            .dispersion = dispersion,
+            .increment = increment,
+            .interval = Nanos(interval),
     };
 }
 
 VehiclePropValue LinearFakeValueGenerator::nextEvent() {
-    mGenCfg.currentValue += mGenCfg.increment;
-    if (mGenCfg.currentValue > mGenCfg.initialValue + mGenCfg.dispersion) {
-        mGenCfg.currentValue = mGenCfg.initialValue - mGenCfg.dispersion;
-    }
     // TODO: (chenhaosjtuacm) remove "{}" if AGL compiler updated
     VehiclePropValue event = {.timestamp = {}, .areaId = {}, .prop = mGenCfg.propId};
     auto& value = event.value;
@@ -67,6 +80,12 @@
     }
     TimePoint eventTime = Clock::now() + mGenCfg.interval;
     event.timestamp = eventTime.time_since_epoch().count();
+
+    mGenCfg.currentValue += mGenCfg.increment;
+    if (mGenCfg.currentValue >= mGenCfg.middleValue + mGenCfg.dispersion) {
+        // Wrap around, (i - d) + c - (i + d) = c - 2 * d
+        mGenCfg.currentValue = mGenCfg.currentValue - 2 * mGenCfg.dispersion;
+    }
     return event;
 }
 
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/LinearFakeValueGenerator.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/LinearFakeValueGenerator.h
index d3b666d..d870209 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/LinearFakeValueGenerator.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/LinearFakeValueGenerator.h
@@ -35,8 +35,8 @@
 
     struct GeneratorCfg {
         int32_t propId;
-        float initialValue;
-        float currentValue;  //  Should be in range (initialValue +/- dispersion).
+        float middleValue;
+        float currentValue;  //  Should be in range (middleValue +/- 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.
         Nanos interval;
@@ -44,6 +44,11 @@
 
 public:
     LinearFakeValueGenerator(const VehiclePropValue& request);
+    // A linear value generator in range [middleValue - dispersion, middleValue + dispersion),
+    // starts at 'currentValue' and at each 'interval', increase by 'increment' and loop back if
+    // exceeds middleValue + dispersion.
+    LinearFakeValueGenerator(int32_t propId, float middleValue, float currentValue,
+                             float dispersion, float increment, int64_t interval);
     ~LinearFakeValueGenerator() = default;
 
     VehiclePropValue nextEvent();
@@ -52,6 +57,9 @@
 
 private:
     GeneratorCfg mGenCfg;
+
+    void initGenCfg(int32_t propId, float middleValue, float currentValue, float dispersion,
+                    float increment, int64_t interval);
 };
 
 }  // namespace impl
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
new file mode 100644
index 0000000..5762223
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/Android.bp
@@ -0,0 +1,18 @@
+// 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.
+
+filegroup {
+    name: "vhal_test_json",
+    srcs: ["prop.json"],
+}
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 a2ec0dd..3b0d5fb 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
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
+#include <android-base/file.h>
 #include <android/hardware/automotive/vehicle/2.0/types.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <sys/mman.h>
 #include <vhal_v2_0/ConcurrentQueue.h>
@@ -33,6 +35,7 @@
 using ::android::hardware::automotive::vehicle::V2_0::FuelType;
 using ::android::hardware::automotive::vehicle::V2_0::recyclable_ptr;
 using ::android::hardware::automotive::vehicle::V2_0::StatusCode;
+using ::android::hardware::automotive::vehicle::V2_0::VehicleHwKeyInputAction;
 using ::android::hardware::automotive::vehicle::V2_0::VehiclePropConfig;
 using ::android::hardware::automotive::vehicle::V2_0::VehicleProperty;
 using ::android::hardware::automotive::vehicle::V2_0::VehiclePropertyStatus;
@@ -44,8 +47,13 @@
 using ::android::hardware::automotive::vehicle::V2_0::impl::HVAC_LEFT;
 using ::android::hardware::automotive::vehicle::V2_0::impl::kMixedTypePropertyForTest;
 
+using ::testing::HasSubstr;
+
 using VehiclePropValuePtr = recyclable_ptr<VehiclePropValue>;
 
+// The maximum length of property ID in string.
+const size_t MAX_PROP_ID_LENGTH = 100;
+
 class DefaultVhalImplTest : public ::testing::Test {
   public:
     ~DefaultVhalImplTest() { mEventQueue.deactivate(); }
@@ -90,7 +98,7 @@
     auto gotValue = mHal->get(value, &status);
 
     EXPECT_EQ(StatusCode::OK, status);
-    EXPECT_EQ((unsigned int)1, gotValue->value.floatValues.size());
+    ASSERT_EQ((unsigned int)1, gotValue->value.floatValues.size());
     EXPECT_EQ(15000.0f, gotValue->value.floatValues[0]);
 }
 
@@ -102,7 +110,7 @@
     auto gotValue = mHal->get(value, &status);
 
     EXPECT_EQ(StatusCode::OK, status);
-    EXPECT_EQ((unsigned int)1, gotValue->value.int32Values.size());
+    ASSERT_EQ((unsigned int)1, gotValue->value.int32Values.size());
     EXPECT_EQ((int)FuelType::FUEL_TYPE_UNLEADED, gotValue->value.int32Values[0]);
 }
 
@@ -114,7 +122,7 @@
     auto gotValue = mHal->get(value, &status);
 
     EXPECT_EQ(StatusCode::OK, status);
-    EXPECT_EQ((unsigned int)1, gotValue->value.int32Values.size());
+    ASSERT_EQ((unsigned int)1, gotValue->value.int32Values.size());
     EXPECT_EQ(2020, gotValue->value.int32Values[0]);
 }
 
@@ -146,11 +154,11 @@
     value.value.floatValues[0] = 1.0f;
 
     StatusCode status = mHal->set(value);
-    EXPECT_EQ(StatusCode::OK, status);
+    ASSERT_EQ(StatusCode::OK, status);
 
     auto gotValue = mHal->get(value, &status);
     EXPECT_EQ(StatusCode::OK, status);
-    EXPECT_EQ((unsigned int)1, gotValue->value.floatValues.size());
+    ASSERT_EQ((unsigned int)1, gotValue->value.floatValues.size());
     EXPECT_EQ(1.0f, gotValue->value.floatValues[0]);
 }
 
@@ -161,11 +169,11 @@
     value.value.int32Values[0] = (int)FuelType::FUEL_TYPE_LEADED;
 
     StatusCode status = mHal->set(value);
-    EXPECT_EQ(StatusCode::OK, status);
+    ASSERT_EQ(StatusCode::OK, status);
 
     auto gotValue = mHal->get(value, &status);
     EXPECT_EQ(StatusCode::OK, status);
-    EXPECT_EQ((unsigned int)1, gotValue->value.int32Values.size());
+    ASSERT_EQ((unsigned int)1, gotValue->value.int32Values.size());
     EXPECT_EQ((int)FuelType::FUEL_TYPE_LEADED, gotValue->value.int32Values[0]);
 }
 
@@ -190,7 +198,7 @@
     value.value.stringValue = "My Vehicle";
 
     StatusCode status = mHal->set(value);
-    EXPECT_EQ(StatusCode::OK, status);
+    ASSERT_EQ(StatusCode::OK, status);
 
     auto gotValue = mHal->get(value, &status);
     EXPECT_EQ(StatusCode::OK, status);
@@ -212,14 +220,16 @@
     value.value.floatValues[0] = 1.0f;
 
     StatusCode status = mHal->set(value);
-    EXPECT_EQ(StatusCode::OK, status);
+    ASSERT_EQ(StatusCode::OK, status);
 
     auto gotValue = mHal->get(value, &status);
     EXPECT_EQ(StatusCode::OK, status);
     EXPECT_EQ("test", gotValue->value.stringValue);
+    ASSERT_EQ((size_t)3, gotValue->value.int32Values.size());
     EXPECT_EQ(1, gotValue->value.int32Values[0]);
     EXPECT_EQ(2, gotValue->value.int32Values[1]);
     EXPECT_EQ(3, gotValue->value.int32Values[2]);
+    ASSERT_EQ((size_t)1, gotValue->value.floatValues.size());
     EXPECT_EQ(1.0f, gotValue->value.floatValues[0]);
 }
 
@@ -248,7 +258,7 @@
 
     auto status = mHal->subscribe(toInt(VehicleProperty::PERF_VEHICLE_SPEED), 10);
 
-    EXPECT_EQ(StatusCode::OK, status);
+    ASSERT_EQ(StatusCode::OK, status);
 
     std::this_thread::sleep_for(std::chrono::milliseconds(500));
 
@@ -257,18 +267,18 @@
     value.prop = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
     value.value.floatValues.resize(1);
     value.value.floatValues[0] = 1.0f;
-    EXPECT_EQ(StatusCode::OK, mHal->set(value));
+    ASSERT_EQ(StatusCode::OK, mHal->set(value));
 
     std::this_thread::sleep_for(std::chrono::milliseconds(500));
 
     auto events = mEventQueue.flush();
-    EXPECT_LE((size_t)10, events.size());
+    ASSERT_LE((size_t)10, events.size());
 
     // The first event should be the default value.
-    EXPECT_EQ((size_t)1, events[0]->value.floatValues.size());
+    ASSERT_EQ((size_t)1, events[0]->value.floatValues.size());
     EXPECT_EQ(0.0f, events[0]->value.floatValues[0]);
     // The last event should be the value after update.
-    EXPECT_EQ((size_t)1, events[events.size() - 1]->value.floatValues.size());
+    ASSERT_EQ((size_t)1, events[events.size() - 1]->value.floatValues.size());
     EXPECT_EQ(1.0f, events[events.size() - 1]->value.floatValues[0]);
 }
 
@@ -285,13 +295,13 @@
 
 TEST_F(DefaultVhalImplTest, testUnsubscribe) {
     auto status = mHal->subscribe(toInt(VehicleProperty::PERF_VEHICLE_SPEED), 10);
-    EXPECT_EQ(StatusCode::OK, status);
+    ASSERT_EQ(StatusCode::OK, status);
 
     // Wait for 0.5 seconds to generate some events.
     std::this_thread::sleep_for(std::chrono::milliseconds(500));
 
     status = mHal->unsubscribe(toInt(VehicleProperty::PERF_VEHICLE_SPEED));
-    EXPECT_EQ(StatusCode::OK, status);
+    ASSERT_EQ(StatusCode::OK, status);
 
     // Clear all the events.
     mEventQueue.flush();
@@ -308,15 +318,20 @@
     EXPECT_EQ(StatusCode::INVALID_ARG, mHal->unsubscribe(toInt(VehicleProperty::INFO_MAKE)));
 }
 
-TEST_F(DefaultVhalImplTest, testDump) {
-    hidl_vec<hidl_string> options;
-    hidl_handle fd = {};
+int createMemfd(hidl_handle* fd) {
     native_handle_t* handle = native_handle_create(/*numFds=*/1, /*numInts=*/0);
     int memfd = memfd_create("memfile", 0);
     handle->data[0] = dup(memfd);
-    fd.setTo(handle, /*shouldOwn=*/true);
+    fd->setTo(handle, /*shouldOwn=*/true);
+    return memfd;
+}
 
-    EXPECT_TRUE(mHal->dump(fd, options));
+TEST_F(DefaultVhalImplTest, testDump) {
+    hidl_vec<hidl_string> options;
+    hidl_handle fd = {};
+    int memfd = createMemfd(&fd);
+
+    ASSERT_TRUE(mHal->dump(fd, options));
 
     lseek(memfd, 0, SEEK_SET);
     char buf[10240] = {};
@@ -328,12 +343,12 @@
     StatusCode status;
     value.prop = toInt(VehicleProperty::INFO_MAKE);
     auto gotValue = mHal->get(value, &status);
-    EXPECT_EQ(StatusCode::OK, status);
+    ASSERT_EQ(StatusCode::OK, status);
     // Server side prop store does not have timestamp.
     gotValue->timestamp = 0;
 
     std::string infoMake = toString(*gotValue);
-    EXPECT_NE(std::string::npos, std::string(buf).find(infoMake));
+    EXPECT_THAT(std::string(buf), HasSubstr(infoMake));
 }
 
 class DefaultVhalImplSetInvalidPropTest : public DefaultVhalImplTest,
@@ -484,4 +499,229 @@
             return info.param.name;
         });
 
+std::string getPropIdString(VehicleProperty prop) {
+    char s[MAX_PROP_ID_LENGTH] = {};
+    snprintf(s, sizeof(s), "%d", toInt(prop));
+    return std::string(s);
+}
+
+struct OptionsTestCase {
+    std::string name;
+    hidl_vec<hidl_string> options;
+    std::string expectMsg;
+};
+
+class DefaultVhalImplOptionsTest : public DefaultVhalImplTest,
+                                   public testing::WithParamInterface<OptionsTestCase> {};
+
+TEST_P(DefaultVhalImplOptionsTest, testInvalidOptions) {
+    auto tc = GetParam();
+    hidl_handle fd = {};
+    int memfd = createMemfd(&fd);
+
+    bool shouldDump = mHal->dump(fd, tc.options);
+
+    EXPECT_FALSE(shouldDump);
+    char buf[10240] = {};
+    lseek(memfd, 0, SEEK_SET);
+    read(memfd, buf, sizeof(buf));
+    EXPECT_THAT(std::string(buf), HasSubstr(tc.expectMsg));
+}
+
+std::vector<OptionsTestCase> GenInvalidOptions() {
+    return {{"no_command", {"--debughal"}, "No command specified"},
+            {"unknown_command", {"--debughal", "--unknown"}, "Unknown command: \"--unknown\""},
+            {"help", {"--debughal", "--help"}, "Help:"},
+            {"genfakedata_no_subcommand",
+             {"--debughal", "--genfakedata"},
+             "No subcommand specified for genfakedata"},
+            {"genfakedata_unknown_subcommand",
+             {"--debughal", "--genfakedata", "--unknown"},
+             "Unknown command: \"--unknown\""},
+            {"genfakedata_start_linear_no_args",
+             {"--debughal", "--genfakedata", "--startlinear"},
+             "incorrect argument count"},
+            {"genfakedata_start_linear_invalid_propId",
+             {"--debughal", "--genfakedata", "--startlinear", "abcd", "0.1", "0.1", "0.1", "0.1",
+              "100000000"},
+             "failed to parse propdID as int: \"abcd\""},
+            {"genfakedata_start_linear_invalid_middleValue",
+             {"--debughal", "--genfakedata", "--startlinear", "1", "abcd", "0.1", "0.1", "0.1",
+              "100000000"},
+             "failed to parse middleValue as float: \"abcd\""},
+            {"genfakedata_start_linear_invalid_currentValue",
+             {"--debughal", "--genfakedata", "--startlinear", "1", "0.1", "abcd", "0.1", "0.1",
+              "100000000"},
+             "failed to parse currentValue as float: \"abcd\""},
+            {"genfakedata_start_linear_invalid_dispersion",
+             {"--debughal", "--genfakedata", "--startlinear", "1", "0.1", "0.1", "abcd", "0.1",
+              "100000000"},
+             "failed to parse dispersion as float: \"abcd\""},
+            {"genfakedata_start_linear_invalid_increment",
+             {"--debughal", "--genfakedata", "--startlinear", "1", "0.1", "0.1", "0.1", "abcd",
+              "100000000"},
+             "failed to parse increment as float: \"abcd\""},
+            {"genfakedata_start_linear_invalid_interval",
+             {"--debughal", "--genfakedata", "--startlinear", "1", "0.1", "0.1", "0.1", "0.1",
+              "0.1"},
+             "failed to parse interval as int: \"0.1\""},
+            {"genfakedata_stop_linear_no_args",
+             {"--debughal", "--genfakedata", "--stoplinear"},
+             "incorrect argument count"},
+            {"genfakedata_stop_linear_invalid_propId",
+             {"--debughal", "--genfakedata", "--stoplinear", "abcd"},
+             "failed to parse propdID as int: \"abcd\""},
+            {"genfakedata_startjson_no_args",
+             {"--debughal", "--genfakedata", "--startjson"},
+             "incorrect argument count"},
+            {"genfakedata_startjson_invalid_repetition",
+             {"--debughal", "--genfakedata", "--startjson", "file", "0.1"},
+             "failed to parse repetition as int: \"0.1\""},
+            {"genfakedata_startjson_invalid_json_file",
+             {"--debughal", "--genfakedata", "--startjson", "file", "1"},
+             "invalid JSON file"},
+            {"genfakedata_stopjson_no_args",
+             {"--debughal", "--genfakedata", "--stopjson"},
+             "incorrect argument count"},
+            {"genfakedata_keypress_no_args",
+             {"--debughal", "--genfakedata", "--keypress"},
+             "incorrect argument count"},
+            {"genfakedata_keypress_invalid_keyCode",
+             {"--debughal", "--genfakedata", "--keypress", "0.1", "1"},
+             "failed to parse keyCode as int: \"0.1\""},
+            {"genfakedata_keypress_invalid_display",
+             {"--debughal", "--genfakedata", "--keypress", "1", "0.1"},
+             "failed to parse display as int: \"0.1\""}};
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        DefaultVhalImplOptionsTests, DefaultVhalImplOptionsTest,
+        testing::ValuesIn(GenInvalidOptions()),
+        [](const testing::TestParamInfo<DefaultVhalImplOptionsTest::ParamType>& info) {
+            return info.param.name;
+        });
+
+TEST_F(DefaultVhalImplTest, testDebugGenFakeDataLinear) {
+    // Start a fake linear data generator for vehicle speed at 0.1s interval.
+    // range: 0 - 100, current value: 30, step: 20.
+    hidl_vec<hidl_string> options = {"--debughal",
+                                     "--genfakedata",
+                                     "--startlinear",
+                                     getPropIdString(VehicleProperty::PERF_VEHICLE_SPEED),
+                                     /*middleValue=*/"50",
+                                     /*currentValue=*/"30",
+                                     /*dispersion=*/"50",
+                                     /*increment=*/"20",
+                                     /*interval=*/"100000000"};
+    hidl_handle fd = {};
+    int memfd = createMemfd(&fd);
+    // Clear existing events.
+    mEventQueue.flush();
+
+    EXPECT_FALSE(mHal->dump(fd, options));
+
+    lseek(memfd, 0, SEEK_SET);
+    char buf[10240] = {};
+    // The dumped info should be empty.
+    read(memfd, buf, sizeof(buf));
+    EXPECT_STREQ("", buf);
+
+    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+
+    auto events = mEventQueue.flush();
+    // We should get 10 events ideally, but let's be safe here.
+    ASSERT_LE((size_t)5, events.size());
+    int32_t value = 30;
+    for (size_t i = 0; i < 5; i++) {
+        ASSERT_EQ((size_t)1, events[i]->value.floatValues.size());
+        EXPECT_EQ((float)value, events[i]->value.floatValues[0]);
+        value = (value + 20) % 100;
+    }
+
+    // Stop the linear generator.
+    options = {"--debughal", "--genfakedata", "--stoplinear",
+               getPropIdString(VehicleProperty::PERF_VEHICLE_SPEED)};
+    EXPECT_FALSE(mHal->dump(fd, options));
+
+    // The dumped info should be empty.
+    lseek(memfd, 0, SEEK_SET);
+    read(memfd, buf, sizeof(buf));
+    EXPECT_STREQ("", buf);
+
+    close(memfd);
+
+    // Clear existing events.
+    mEventQueue.flush();
+    std::this_thread::sleep_for(std::chrono::milliseconds(500));
+    // There should be no new events generated.
+    EXPECT_EQ((size_t)0, mEventQueue.flush().size());
+}
+
+std::string getTestFilePath(const char* filename) {
+    static std::string baseDir = android::base::GetExecutableDirectory();
+    return baseDir + "/" + filename;
+}
+
+TEST_F(DefaultVhalImplTest, testDebugGenFakeDataJson) {
+    hidl_vec<hidl_string> options = {"--debughal", "--genfakedata", "--startjson",
+                                     getTestFilePath("prop.json"), "2"};
+    hidl_handle fd = {};
+    int memfd = createMemfd(&fd);
+    // Clear existing events.
+    mEventQueue.flush();
+
+    EXPECT_FALSE(mHal->dump(fd, options));
+
+    lseek(memfd, 0, SEEK_SET);
+    char buf[10240] = {};
+    // The dumped info should be empty.
+    read(memfd, buf, sizeof(buf));
+    EXPECT_STREQ("", buf);
+
+    // wait for some time.
+    std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+    auto events = mEventQueue.flush();
+    ASSERT_EQ((size_t)8, events.size());
+    // First set of events, we test 1st and the last.
+    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]);
+    // 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]);
+}
+
+TEST_F(DefaultVhalImplTest, testDebugGenFakeDataKeyPress) {
+    hidl_vec<hidl_string> options = {"--debughal", "--genfakedata", "--keypress", "1", "2"};
+    hidl_handle fd = {};
+    int memfd = createMemfd(&fd);
+    // Clear existing events.
+    mEventQueue.flush();
+
+    EXPECT_FALSE(mHal->dump(fd, options));
+
+    lseek(memfd, 0, SEEK_SET);
+    char buf[10240] = {};
+    // The dumped info should be empty.
+    read(memfd, buf, sizeof(buf));
+    EXPECT_STREQ("", buf);
+
+    auto events = mEventQueue.flush();
+    ASSERT_EQ((size_t)2, events.size());
+    EXPECT_EQ(toInt(VehicleProperty::HW_KEY_INPUT), events[0]->prop);
+    EXPECT_EQ(toInt(VehicleProperty::HW_KEY_INPUT), events[1]->prop);
+    ASSERT_EQ((size_t)3, events[0]->value.int32Values.size());
+    ASSERT_EQ((size_t)3, events[1]->value.int32Values.size());
+    EXPECT_EQ(toInt(VehicleHwKeyInputAction::ACTION_DOWN), events[0]->value.int32Values[0]);
+    EXPECT_EQ(1, events[0]->value.int32Values[1]);
+    EXPECT_EQ(2, events[0]->value.int32Values[2]);
+    EXPECT_EQ(toInt(VehicleHwKeyInputAction::ACTION_UP), events[1]->value.int32Values[0]);
+    EXPECT_EQ(1, events[1]->value.int32Values[1]);
+    EXPECT_EQ(2, events[1]->value.int32Values[2]);
+}
+
 }  // namespace
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
new file mode 100644
index 0000000..2e77a44
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/prop.json
@@ -0,0 +1,26 @@
+[
+  {
+    "timestamp": 1000000,
+    "areaId": 0,
+    "value": 8,
+    "prop": 289408000
+  },
+  {
+    "timestamp": 2000000,
+    "areaId": 0,
+    "value": 4,
+    "prop": 289408000
+  },
+  {
+    "timestamp": 3000000,
+    "areaId": 0,
+    "value": 16,
+    "prop": 289408000
+  },
+  {
+    "timestamp": 4000000,
+    "areaId": 0,
+    "value": 4,
+    "prop": 289408000
+  }
+]