Support set/get value in fake vehicle hardware.

Support setValues and getValues in fake vehicle hardware
implementation.

Test: atest FakeVehicleHardwareTest
Bug: 201830716
Change-Id: I4b94408434fd8511ab79d757116b776105d69222
diff --git a/automotive/vehicle/TEST_MAPPING b/automotive/vehicle/TEST_MAPPING
index 4820fd4..c5702aa 100644
--- a/automotive/vehicle/TEST_MAPPING
+++ b/automotive/vehicle/TEST_MAPPING
@@ -8,6 +8,9 @@
     },
     {
       "name": "VehicleHalVehicleUtilsTest"
+    },
+    {
+      "name": "FakeVehicleHardwareTest"
     }
   ]
 }
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 b091517..6dc3e76 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -106,15 +106,67 @@
     return mServerSidePropStore->getAllConfigs();
 }
 
-StatusCode FakeVehicleHardware::setValues(FakeVehicleHardware::SetValuesCallback&&,
-                                          const std::vector<SetValueRequest>&) {
-    // TODO(b/201830716): Implement this.
+StatusCode FakeVehicleHardware::setValues(FakeVehicleHardware::SetValuesCallback&& callback,
+                                          const std::vector<SetValueRequest>& requests) {
+    std::vector<VehiclePropValue> updatedValues;
+    std::vector<SetValueResult> results;
+    for (auto& request : requests) {
+        const VehiclePropValue* value = &request.value;
+        ALOGD("setValues(%d)", value->prop);
+
+        auto updatedValue = mValuePool->obtain(*value);
+        int64_t timestamp = elapsedRealtimeNano();
+        updatedValue->timestamp = timestamp;
+
+        auto writeResult = mServerSidePropStore->writeValue(std::move(updatedValue));
+        SetValueResult setValueResult;
+        setValueResult.requestId = request.requestId;
+        if (!writeResult.ok()) {
+            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;
+        } else {
+            setValueResult.status = StatusCode::OK;
+        }
+        results.push_back(std::move(setValueResult));
+    }
+
+    // In the real vhal, the values will be sent to Car ECU. We just pretend it is done here and
+    // send back the updated property values to client.
+    callback(std::move(results));
+
     return StatusCode::OK;
 }
 
-StatusCode FakeVehicleHardware::getValues(FakeVehicleHardware::GetValuesCallback&&,
-                                          const std::vector<GetValueRequest>&) const {
-    // TODO(b/201830716): Implement this.
+StatusCode FakeVehicleHardware::getValues(FakeVehicleHardware::GetValuesCallback&& callback,
+                                          const std::vector<GetValueRequest>& requests) const {
+    std::vector<GetValueResult> results;
+    for (auto& request : requests) {
+        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;
+            } else {
+                ALOGE("failed to get value, error: %s, code: %d", error.message().c_str(),
+                      error.code());
+                getValueResult.status = StatusCode::INVALID_ARG;
+            }
+        } else {
+            getValueResult.status = StatusCode::OK;
+            getValueResult.prop = *readResult.value();
+        }
+        results.push_back(std::move(getValueResult));
+    }
+
+    callback(std::move(results));
+
     return StatusCode::OK;
 }
 
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 d901b38..650a131 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
@@ -41,6 +41,8 @@
 using ::testing::Eq;
 using ::testing::WhenSortedBy;
 
+constexpr int INVALID_PROP_ID = 0;
+
 }  // namespace
 
 class FakeVehicleHardwareTest : public ::testing::Test {
@@ -159,6 +161,268 @@
     ASSERT_EQ(configs.size(), defaultconfig::getDefaultConfigs().size());
 }
 
+TEST_F(FakeVehicleHardwareTest, testGetDefaultValues) {
+    std::vector<GetValueRequest> getValueRequests;
+    std::vector<GetValueResult> expectedGetValueResults;
+    int64_t requestId = 1;
+
+    for (auto& config : defaultconfig::getDefaultConfigs()) {
+        int propId = config.config.prop;
+        if (isGlobalProp(propId)) {
+            if (config.initialValue == RawPropValues{}) {
+                addGetValueRequest(getValueRequests, expectedGetValueResults, requestId++,
+                                   VehiclePropValue{.prop = propId}, StatusCode::NOT_AVAILABLE);
+                continue;
+            }
+            addGetValueRequest(getValueRequests, expectedGetValueResults, requestId++,
+                               VehiclePropValue{
+                                       .prop = propId,
+                                       .value = config.initialValue,
+                               },
+                               StatusCode::OK);
+            continue;
+        }
+        for (auto areaConfig : config.config.areaConfigs) {
+            StatusCode status = StatusCode::OK;
+            VehiclePropValue propValue{
+                    .prop = propId,
+                    .areaId = areaConfig.areaId,
+            };
+            if (config.initialAreaValues.empty()) {
+                if (config.initialValue == RawPropValues{}) {
+                    status = StatusCode::NOT_AVAILABLE;
+                } else {
+                    propValue.value = config.initialValue;
+                }
+            } else if (auto valueForAreaIt = config.initialAreaValues.find(areaConfig.areaId);
+                       valueForAreaIt != config.initialAreaValues.end()) {
+                propValue.value = valueForAreaIt->second;
+            } else {
+                status = StatusCode::NOT_AVAILABLE;
+            }
+            addGetValueRequest(getValueRequests, expectedGetValueResults, requestId++, propValue,
+                               status);
+        }
+    }
+
+    // In our implementation, this would finish immediately.
+    StatusCode status = getValues(getValueRequests);
+
+    ASSERT_EQ(status, StatusCode::OK);
+
+    std::vector<GetValueResult> getValueResultsWithNoTimestamp;
+    for (auto& result : getGetValueResults()) {
+        GetValueResult resultCopy = result;
+        resultCopy.prop->timestamp = 0;
+        getValueResultsWithNoTimestamp.push_back(std::move(resultCopy));
+    }
+    ASSERT_THAT(getValueResultsWithNoTimestamp, ContainerEq(expectedGetValueResults));
+}
+
+TEST_F(FakeVehicleHardwareTest, testSetValues) {
+    std::vector<SetValueRequest> requests;
+    std::vector<SetValueResult> expectedResults;
+
+    int64_t requestId = 1;
+    for (auto& value : getTestPropValues()) {
+        addSetValueRequest(requests, expectedResults, requestId++, value, StatusCode::OK);
+    }
+
+    StatusCode status = setValues(requests);
+
+    ASSERT_EQ(status, StatusCode::OK);
+
+    // Although callback might be called asynchronously, in our implementation, the callback would
+    // be called before setValues returns.
+    ASSERT_THAT(getSetValueResults(), ContainerEq(expectedResults));
+}
+
+TEST_F(FakeVehicleHardwareTest, testSetValuesError) {
+    std::vector<SetValueRequest> requests;
+    std::vector<SetValueResult> expectedResults;
+
+    int64_t requestId = 1;
+
+    VehiclePropValue invalidProp = {
+            .prop = INVALID_PROP_ID,
+    };
+    addSetValueRequest(requests, expectedResults, requestId++, invalidProp,
+                       StatusCode::INVALID_ARG);
+
+    for (auto& value : getTestPropValues()) {
+        addSetValueRequest(requests, expectedResults, requestId++, value, StatusCode::OK);
+    }
+
+    StatusCode status = setValues(requests);
+
+    ASSERT_EQ(status, StatusCode::OK);
+
+    // Although callback might be called asynchronously, in our implementation, the callback would
+    // be called before setValues returns.
+    ASSERT_THAT(getSetValueResults(), ContainerEq(expectedResults));
+}
+
+TEST_F(FakeVehicleHardwareTest, testRegisterOnPropertyChangeEvent) {
+    getHardware()->registerOnPropertyChangeEvent(std::bind(
+            &FakeVehicleHardwareTest_testRegisterOnPropertyChangeEvent_Test::onPropertyChangeEvent,
+            this, std::placeholders::_1));
+
+    auto testValues = getTestPropValues();
+    std::vector<SetValueRequest> requests;
+    std::vector<SetValueResult> expectedResults;
+    int64_t requestId = 1;
+    for (auto& value : testValues) {
+        addSetValueRequest(requests, expectedResults, requestId++, value, StatusCode::OK);
+    }
+    int64_t timestamp = elapsedRealtimeNano();
+
+    StatusCode status = setValues(requests);
+
+    ASSERT_EQ(status, StatusCode::OK);
+
+    auto updatedValues = getChangedProperties();
+    std::vector<VehiclePropValue> updatedValuesWithNoTimestamp;
+    for (auto& value : updatedValues) {
+        ASSERT_GE(value.timestamp, timestamp);
+        VehiclePropValue valueCopy = value;
+        valueCopy.timestamp = 0;
+        updatedValuesWithNoTimestamp.push_back(std::move(valueCopy));
+    }
+
+    ASSERT_THAT(updatedValuesWithNoTimestamp, WhenSortedBy(mPropValueCmp, Eq(testValues)));
+}
+
+TEST_F(FakeVehicleHardwareTest, testReadValues) {
+    std::vector<SetValueRequest> setValueRequests;
+    std::vector<SetValueResult> expectedSetValueResults;
+
+    int64_t requestId = 1;
+    for (auto& value : getTestPropValues()) {
+        addSetValueRequest(setValueRequests, expectedSetValueResults, requestId++, value,
+                           StatusCode::OK);
+    }
+    int64_t timestamp = elapsedRealtimeNano();
+
+    // In our implementation, this would finish immediately.
+    StatusCode status = setValues(setValueRequests);
+
+    ASSERT_EQ(status, StatusCode::OK);
+
+    std::vector<GetValueRequest> getValueRequests;
+    std::vector<GetValueResult> expectedGetValueResults;
+    for (auto& value : getTestPropValues()) {
+        addGetValueRequest(getValueRequests, expectedGetValueResults, requestId++, value,
+                           StatusCode::OK);
+    }
+
+    // In our implementation, this would finish immediately.
+    status = getValues(getValueRequests);
+
+    ASSERT_EQ(status, StatusCode::OK);
+
+    std::vector<GetValueResult> getValueResultsWithNoTimestamp;
+    for (auto& result : getGetValueResults()) {
+        ASSERT_GE(result.prop->timestamp, timestamp);
+        GetValueResult resultCopy = result;
+        resultCopy.prop->timestamp = 0;
+        getValueResultsWithNoTimestamp.push_back(std::move(resultCopy));
+    }
+    ASSERT_THAT(getValueResultsWithNoTimestamp, ContainerEq(expectedGetValueResults));
+}
+
+TEST_F(FakeVehicleHardwareTest, testReadValuesErrorInvalidProp) {
+    std::vector<SetValueRequest> setValueRequests;
+    std::vector<SetValueResult> expectedSetValueResults;
+
+    int64_t requestId = 1;
+    for (auto& value : getTestPropValues()) {
+        addSetValueRequest(setValueRequests, expectedSetValueResults, requestId++, value,
+                           StatusCode::OK);
+    }
+
+    // In our implementation, this would finish immediately.
+    StatusCode status = setValues(setValueRequests);
+
+    ASSERT_EQ(status, StatusCode::OK);
+
+    std::vector<GetValueRequest> getValueRequests;
+    std::vector<GetValueResult> expectedGetValueResults;
+    VehiclePropValue invalidProp = {
+            .prop = INVALID_PROP_ID,
+    };
+    addGetValueRequest(getValueRequests, expectedGetValueResults, requestId++, invalidProp,
+                       StatusCode::INVALID_ARG);
+
+    // In our implementation, this would finish immediately.
+    status = getValues(getValueRequests);
+
+    ASSERT_EQ(status, StatusCode::OK);
+    ASSERT_THAT(getGetValueResults(), ContainerEq(expectedGetValueResults));
+}
+
+TEST_F(FakeVehicleHardwareTest, testReadValuesErrorNotAvailable) {
+    std::vector<GetValueRequest> getValueRequests;
+    std::vector<GetValueResult> expectedGetValueResults;
+    // VEHICLE_MAP_SERVICE does not have initial value, 'get' must always return
+    // StatusCode::NOT_AVAILABLE.
+    addGetValueRequest(getValueRequests, expectedGetValueResults, 0,
+                       VehiclePropValue{
+                               .prop = VEHICLE_MAP_SERVICE,
+                       },
+                       StatusCode::NOT_AVAILABLE);
+
+    // In our implementation, this would finish immediately.
+    StatusCode status = getValues(getValueRequests);
+
+    ASSERT_EQ(status, StatusCode::OK);
+    ASSERT_THAT(getGetValueResults(), ContainerEq(expectedGetValueResults));
+}
+
+TEST_F(FakeVehicleHardwareTest, testSetStatusMustIgnore) {
+    VehiclePropValue testValue = getTestPropValues()[0];
+    testValue.status = VehiclePropertyStatus::UNAVAILABLE;
+
+    std::vector<SetValueRequest> setValueRequests;
+    std::vector<SetValueResult> expectedSetValueResults;
+
+    int64_t requestId = 1;
+    addSetValueRequest(setValueRequests, expectedSetValueResults, requestId++, testValue,
+                       StatusCode::OK);
+
+    // In our implementation, this would finish immediately.
+    StatusCode status = setValues(setValueRequests);
+
+    ASSERT_EQ(status, StatusCode::OK);
+    ASSERT_THAT(getSetValueResults(), ContainerEq(expectedSetValueResults));
+
+    std::vector<GetValueRequest> getValueRequests;
+    getValueRequests.push_back(GetValueRequest{
+            .requestId = requestId++,
+            .prop = testValue,
+    });
+
+    // In our implementation, this would finish immediately.
+    status = getValues(getValueRequests);
+
+    ASSERT_EQ(status, StatusCode::OK);
+    ASSERT_EQ(getGetValueResults().size(), static_cast<size_t>(1));
+    ASSERT_EQ(getGetValueResults()[0].status, StatusCode::OK);
+    // The status should be by-default AVAILABLE for new status.
+    ASSERT_EQ(getGetValueResults()[0].prop->status, VehiclePropertyStatus::AVAILABLE);
+
+    // Try to set the property again. The status should not be overwritten.
+    status = setValues(setValueRequests);
+
+    ASSERT_EQ(status, StatusCode::OK);
+
+    status = getValues(getValueRequests);
+
+    ASSERT_EQ(status, StatusCode::OK);
+    ASSERT_EQ(getGetValueResults().size(), static_cast<size_t>(2));
+    ASSERT_EQ(getGetValueResults()[1].status, StatusCode::OK);
+    ASSERT_EQ(getGetValueResults()[1].prop->status, VehiclePropertyStatus::AVAILABLE);
+}
+
 }  // namespace vehicle
 }  // namespace automotive
 }  // namespace hardware