Migrate VehiclePropertyStore.
Test: atest VehicleHalVehicleUtilsTest
Bug: 199337732
Change-Id: Ia18699a0115fdb004c57c0e6fb02b043ddb138b5
diff --git a/automotive/vehicle/aidl/impl/Android.bp b/automotive/vehicle/aidl/impl/Android.bp
index 0b98c7e..a97d544 100644
--- a/automotive/vehicle/aidl/impl/Android.bp
+++ b/automotive/vehicle/aidl/impl/Android.bp
@@ -32,5 +32,6 @@
"-Wall",
"-Wextra",
"-Werror",
+ "-Wthread-safety",
],
}
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h b/automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h
new file mode 100644
index 0000000..b19ab84
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h
@@ -0,0 +1,136 @@
+/*
+ * 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_aidl_impl_utils_common_include_VehiclePropertyStore_H_
+#define android_hardware_automotive_vehicle_aidl_impl_utils_common_include_VehiclePropertyStore_H_
+
+#include <cstdint>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <unordered_map>
+
+#include <VehicleHalTypes.h>
+#include <android-base/result.h>
+#include <android-base/thread_annotations.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+// Encapsulates work related to storing and accessing configuration, storing and modifying
+// vehicle property values.
+//
+// VehiclePropertyValues stored in a sorted map thus it makes easier to get range of values, e.g.
+// to get value for all areas for particular property.
+//
+// This class is thread-safe, however it uses blocking synchronization across all methods.
+class VehiclePropertyStore {
+ public:
+ // Function that used to calculate unique token for given VehiclePropValue.
+ using TokenFunction = ::std::function<int64_t(
+ const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value)>;
+
+ // Register the given property according to the config. A property has to be registered first
+ // before write/read. If tokenFunc is not nullptr, it would be used to generate a unique
+ // property token to act as the key the property store. Otherwise, {propertyID, areaID} would be
+ // used as the key.
+ void registerProperty(
+ const ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig& config,
+ TokenFunction tokenFunc = nullptr);
+
+ // Stores provided value. Returns true if value was written returns false if config wasn't
+ // registered.
+ ::android::base::Result<void> writeValue(
+ const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& propValue);
+
+ // Remove a given property value from the property store. The 'propValue' would be used to
+ // generate the key for the value to remove.
+ void removeValue(
+ const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& propValue);
+
+ // Remove all the values for the property.
+ void removeValuesForProperty(int32_t propId);
+
+ // Read all the stored values.
+ std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropValue> readAllValues()
+ const;
+
+ // Read all the values for the property.
+ ::android::base::Result<
+ std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>>
+ readValuesForProperty(int32_t propId) const;
+
+ // Read the value for the requested property.
+ ::android::base::Result<
+ std::unique_ptr<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>>
+ readValue(
+ const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& request) const;
+
+ // Read the value for the requested property.
+ ::android::base::Result<
+ std::unique_ptr<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>>
+ readValue(int32_t prop, int32_t area = 0, int64_t token = 0) const;
+
+ // Get all property configs.
+ std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropConfig> getAllConfigs()
+ const;
+
+ // Get the property config for the requested property.
+ ::android::base::Result<
+ const ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig*>
+ getConfig(int32_t propId) const;
+
+ private:
+ struct RecordId {
+ int32_t area;
+ int64_t token;
+
+ bool operator==(const RecordId& other) const;
+ bool operator<(const RecordId& other) const;
+
+ std::string toString() const;
+ };
+
+ struct Record {
+ ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig propConfig;
+ TokenFunction tokenFunction;
+ std::map<RecordId, ::aidl::android::hardware::automotive::vehicle::VehiclePropValue> values;
+ };
+
+ mutable std::mutex mLock;
+ std::unordered_map<int32_t, Record> mRecordsByPropId GUARDED_BY(mLock);
+
+ const Record* getRecordLocked(int32_t propId) const;
+
+ Record* getRecordLocked(int32_t propId);
+
+ RecordId getRecordIdLocked(
+ const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& propValue,
+ const Record& record) const;
+
+ ::android::base::Result<
+ std::unique_ptr<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>>
+ readValueLocked(const RecordId& recId, const Record& record) const;
+};
+
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // android_hardware_automotive_vehicle_aidl_impl_utils_common_include_VehiclePropertyStore_H_
diff --git a/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp b/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp
new file mode 100644
index 0000000..b660f36
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp
@@ -0,0 +1,231 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "VehiclePropertyStore"
+#include <utils/Log.h>
+
+#include "VehiclePropertyStore.h"
+
+#include <VehicleUtils.h>
+#include <android-base/format.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+using ::android::base::Result;
+
+bool VehiclePropertyStore::RecordId::operator==(const VehiclePropertyStore::RecordId& other) const {
+ return area == other.area && token == other.token;
+}
+
+bool VehiclePropertyStore::RecordId::operator<(const VehiclePropertyStore::RecordId& other) const {
+ return area < other.area || (area == other.area && token < other.token);
+}
+
+std::string VehiclePropertyStore::RecordId::toString() const {
+ return ::fmt::format("RecordID{{.areaId={:d}, .token={:d}}}", area, token);
+}
+
+const VehiclePropertyStore::Record* VehiclePropertyStore::getRecordLocked(int32_t propId) const
+ REQUIRES(mLock) {
+ auto RecordIt = mRecordsByPropId.find(propId);
+ return RecordIt == mRecordsByPropId.end() ? nullptr : &RecordIt->second;
+}
+
+VehiclePropertyStore::Record* VehiclePropertyStore::getRecordLocked(int32_t propId)
+ REQUIRES(mLock) {
+ auto RecordIt = mRecordsByPropId.find(propId);
+ return RecordIt == mRecordsByPropId.end() ? nullptr : &RecordIt->second;
+}
+
+VehiclePropertyStore::RecordId VehiclePropertyStore::getRecordIdLocked(
+ const VehiclePropValue& propValue, const VehiclePropertyStore::Record& record) const
+ REQUIRES(mLock) {
+ VehiclePropertyStore::RecordId recId{
+ .area = isGlobalProp(propValue.prop) ? 0 : propValue.areaId, .token = 0};
+
+ if (record.tokenFunction != nullptr) {
+ recId.token = record.tokenFunction(propValue);
+ }
+ return recId;
+}
+
+Result<std::unique_ptr<VehiclePropValue>> VehiclePropertyStore::readValueLocked(
+ const RecordId& recId, const Record& record) const REQUIRES(mLock) {
+ auto it = record.values.find(recId);
+ if (it == record.values.end()) {
+ return Errorf("Record ID: {} is not found", recId.toString());
+ }
+ return std::make_unique<VehiclePropValue>(it->second);
+}
+
+void VehiclePropertyStore::registerProperty(const VehiclePropConfig& config,
+ VehiclePropertyStore::TokenFunction tokenFunc) {
+ std::lock_guard<std::mutex> g(mLock);
+
+ mRecordsByPropId[config.prop] = Record{
+ .propConfig = config,
+ .tokenFunction = tokenFunc,
+ };
+}
+
+Result<void> VehiclePropertyStore::writeValue(const VehiclePropValue& propValue) {
+ std::lock_guard<std::mutex> g(mLock);
+
+ VehiclePropertyStore::Record* record = getRecordLocked(propValue.prop);
+ if (record == nullptr) {
+ return Errorf("property: {:d} not registered", propValue.prop);
+ }
+
+ if (!isGlobalProp(propValue.prop) && getAreaConfig(propValue, record->propConfig) == nullptr) {
+ return Errorf("no config for property: {:d} area: {:d}", propValue.prop, propValue.areaId);
+ }
+
+ VehiclePropertyStore::RecordId recId = getRecordIdLocked(propValue, *record);
+ auto it = record->values.find(recId);
+ if (it == record->values.end()) {
+ record->values[recId] = propValue;
+ return {};
+ }
+ VehiclePropValue* valueToUpdate = &(it->second);
+
+ // propValue is outdated and drops it.
+ if (valueToUpdate->timestamp > propValue.timestamp) {
+ return Errorf("outdated timestamp: {:d}", propValue.timestamp);
+ }
+ // Update the propertyValue.
+ // The timestamp in propertyStore should only be updated by the server side. It indicates
+ // the time when the event is generated by the server.
+ valueToUpdate->timestamp = propValue.timestamp;
+ valueToUpdate->value = propValue.value;
+ valueToUpdate->status = propValue.status;
+ return {};
+}
+
+void VehiclePropertyStore::removeValue(const VehiclePropValue& propValue) {
+ std::lock_guard<std::mutex> g(mLock);
+
+ VehiclePropertyStore::Record* record = getRecordLocked(propValue.prop);
+ if (record == nullptr) {
+ return;
+ }
+
+ VehiclePropertyStore::RecordId recId = getRecordIdLocked(propValue, *record);
+ if (auto it = record->values.find(recId); it != record->values.end()) {
+ record->values.erase(it);
+ }
+}
+
+void VehiclePropertyStore::removeValuesForProperty(int32_t propId) {
+ std::lock_guard<std::mutex> g(mLock);
+
+ VehiclePropertyStore::Record* record = getRecordLocked(propId);
+ if (record == nullptr) {
+ return;
+ }
+
+ record->values.clear();
+}
+
+std::vector<VehiclePropValue> VehiclePropertyStore::readAllValues() const {
+ std::lock_guard<std::mutex> g(mLock);
+
+ std::vector<VehiclePropValue> allValues;
+
+ for (auto const& [_, record] : mRecordsByPropId) {
+ for (auto const& [_, value] : record.values) {
+ allValues.push_back(value);
+ }
+ }
+
+ return allValues;
+}
+
+Result<std::vector<VehiclePropValue>> VehiclePropertyStore::readValuesForProperty(
+ int32_t propId) const {
+ std::lock_guard<std::mutex> g(mLock);
+
+ std::vector<VehiclePropValue> values;
+
+ const VehiclePropertyStore::Record* record = getRecordLocked(propId);
+ if (record == nullptr) {
+ return Errorf("property: {:d} not registered", propId);
+ }
+
+ for (auto const& [_, value] : record->values) {
+ values.push_back(value);
+ }
+ return values;
+}
+
+Result<std::unique_ptr<VehiclePropValue>> VehiclePropertyStore::readValue(
+ const VehiclePropValue& propValue) const {
+ std::lock_guard<std::mutex> g(mLock);
+
+ const VehiclePropertyStore::Record* record = getRecordLocked(propValue.prop);
+ if (record == nullptr) {
+ return Errorf("property: {:d} not registered", propValue.prop);
+ }
+
+ VehiclePropertyStore::RecordId recId = getRecordIdLocked(propValue, *record);
+ return readValueLocked(recId, *record);
+}
+
+Result<std::unique_ptr<VehiclePropValue>> VehiclePropertyStore::readValue(int32_t propId,
+ int32_t areaId,
+ int64_t token) const {
+ std::lock_guard<std::mutex> g(mLock);
+
+ const VehiclePropertyStore::Record* record = getRecordLocked(propId);
+ if (record == nullptr) {
+ return Errorf("property: {:d} not registered", propId);
+ }
+
+ VehiclePropertyStore::RecordId recId{.area = isGlobalProp(propId) ? 0 : areaId, .token = token};
+ return readValueLocked(recId, *record);
+}
+
+std::vector<VehiclePropConfig> VehiclePropertyStore::getAllConfigs() const {
+ std::lock_guard<std::mutex> g(mLock);
+
+ std::vector<VehiclePropConfig> configs;
+ configs.reserve(mRecordsByPropId.size());
+ for (auto& [_, config] : mRecordsByPropId) {
+ configs.push_back(config.propConfig);
+ }
+ return configs;
+}
+
+Result<const VehiclePropConfig*> VehiclePropertyStore::getConfig(int32_t propId) const {
+ std::lock_guard<std::mutex> g(mLock);
+
+ const VehiclePropertyStore::Record* record = getRecordLocked(propId);
+ if (record == nullptr) {
+ return Errorf("property: {:d} not registered", propId);
+ }
+
+ return &record->propConfig;
+}
+
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/vehicle/aidl/impl/utils/common/test/Android.bp b/automotive/vehicle/aidl/impl/utils/common/test/Android.bp
index 40744ce..65b0e46 100644
--- a/automotive/vehicle/aidl/impl/utils/common/test/Android.bp
+++ b/automotive/vehicle/aidl/impl/utils/common/test/Android.bp
@@ -22,7 +22,11 @@
name: "VehicleHalVehicleUtilsTest",
srcs: ["*.cpp"],
vendor: true,
- static_libs: ["VehicleHalUtils"],
+ static_libs: [
+ "VehicleHalUtils",
+ "libgtest",
+ "libgmock",
+ ],
defaults: ["VehicleHalDefaults"],
test_suites: ["general-tests"],
}
diff --git a/automotive/vehicle/aidl/impl/utils/common/test/VehiclePropertyStoreTest.cpp b/automotive/vehicle/aidl/impl/utils/common/test/VehiclePropertyStoreTest.cpp
new file mode 100644
index 0000000..8c70fea
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/utils/common/test/VehiclePropertyStoreTest.cpp
@@ -0,0 +1,314 @@
+/*
+ * 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 <PropertyUtils.h>
+#include <VehiclePropertyStore.h>
+#include <VehicleUtils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+namespace {
+
+using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
+using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+using ::android::base::Result;
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::WhenSortedBy;
+
+constexpr int INVALID_PROP_ID = 0;
+
+struct PropValueCmp {
+ bool operator()(const VehiclePropValue& a, const VehiclePropValue& b) const {
+ return (a.prop < b.prop) || ((a.prop == b.prop) && (a.value < b.value)) ||
+ ((a.prop == b.prop) && (a.value == b.value) && (a.areaId < b.areaId));
+ }
+} propValueCmp;
+
+int64_t timestampToken(const VehiclePropValue& value) {
+ return value.timestamp;
+}
+
+} // namespace
+
+class VehiclePropertyStoreTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ mConfigFuelCapacity = {
+ .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY),
+ .access = VehiclePropertyAccess::READ,
+ .changeMode = VehiclePropertyChangeMode::STATIC,
+ };
+ VehiclePropConfig configTirePressure = {
+ .prop = toInt(VehicleProperty::TIRE_PRESSURE),
+ .access = VehiclePropertyAccess::READ,
+ .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
+ .areaConfigs = {VehicleAreaConfig{.areaId = WHEEL_FRONT_LEFT},
+ VehicleAreaConfig{.areaId = WHEEL_FRONT_RIGHT},
+ VehicleAreaConfig{.areaId = WHEEL_REAR_LEFT},
+ VehicleAreaConfig{.areaId = WHEEL_REAR_RIGHT}},
+ };
+ mStore.registerProperty(mConfigFuelCapacity);
+ mStore.registerProperty(configTirePressure);
+ }
+
+ VehiclePropertyStore mStore;
+ VehiclePropConfig mConfigFuelCapacity;
+};
+
+TEST_F(VehiclePropertyStoreTest, testGetAllConfigs) {
+ std::vector<VehiclePropConfig> configs = mStore.getAllConfigs();
+
+ ASSERT_EQ(configs.size(), static_cast<size_t>(2));
+}
+
+TEST_F(VehiclePropertyStoreTest, testGetConfig) {
+ Result<const VehiclePropConfig*> result =
+ mStore.getConfig(toInt(VehicleProperty::INFO_FUEL_CAPACITY));
+
+ ASSERT_RESULT_OK(result);
+ ASSERT_EQ(*(result.value()), mConfigFuelCapacity);
+}
+
+TEST_F(VehiclePropertyStoreTest, testGetConfigWithInvalidPropId) {
+ Result<const VehiclePropConfig*> result = mStore.getConfig(INVALID_PROP_ID);
+
+ ASSERT_FALSE(result.ok()) << "expect error when getting a config for an invalid property ID";
+}
+
+std::vector<VehiclePropValue> getTestPropValues() {
+ VehiclePropValue fuelCapacity = {
+ .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY),
+ .value = {.floatValues = {1.0}},
+ };
+
+ VehiclePropValue leftTirePressure = {
+ .prop = toInt(VehicleProperty::TIRE_PRESSURE),
+ .value = {.floatValues = {170.0}},
+ .areaId = WHEEL_FRONT_LEFT,
+ };
+
+ VehiclePropValue rightTirePressure = {
+ .prop = toInt(VehicleProperty::TIRE_PRESSURE),
+ .value = {.floatValues = {180.0}},
+ .areaId = WHEEL_FRONT_RIGHT,
+ };
+
+ return {fuelCapacity, leftTirePressure, rightTirePressure};
+}
+
+TEST_F(VehiclePropertyStoreTest, testWriteValueOk) {
+ auto values = getTestPropValues();
+
+ ASSERT_RESULT_OK(mStore.writeValue(values[0]));
+}
+
+TEST_F(VehiclePropertyStoreTest, testReadAllValues) {
+ auto values = getTestPropValues();
+ for (const auto& value : values) {
+ ASSERT_RESULT_OK(mStore.writeValue(value));
+ }
+
+ auto gotValues = mStore.readAllValues();
+
+ ASSERT_THAT(gotValues, WhenSortedBy(propValueCmp, Eq(values)));
+}
+
+TEST_F(VehiclePropertyStoreTest, testReadValuesForPropertyOneValue) {
+ auto values = getTestPropValues();
+ for (const auto& value : values) {
+ ASSERT_RESULT_OK(mStore.writeValue(value));
+ }
+
+ auto result = mStore.readValuesForProperty(toInt(VehicleProperty::INFO_FUEL_CAPACITY));
+
+ ASSERT_RESULT_OK(result);
+ ASSERT_THAT(result.value(), ElementsAre(values[0]));
+}
+
+TEST_F(VehiclePropertyStoreTest, testReadValuesForPropertyMultipleValues) {
+ auto values = getTestPropValues();
+ for (const auto& value : values) {
+ ASSERT_RESULT_OK(mStore.writeValue(value));
+ }
+
+ auto result = mStore.readValuesForProperty(toInt(VehicleProperty::TIRE_PRESSURE));
+
+ ASSERT_RESULT_OK(result);
+ ASSERT_THAT(result.value(), WhenSortedBy(propValueCmp, ElementsAre(values[1], values[2])));
+}
+
+TEST_F(VehiclePropertyStoreTest, testReadValuesForPropertyError) {
+ auto result = mStore.readValuesForProperty(INVALID_PROP_ID);
+
+ ASSERT_FALSE(result.ok()) << "expect error when reading values for an invalid property";
+}
+
+TEST_F(VehiclePropertyStoreTest, testReadValueOk) {
+ auto values = getTestPropValues();
+ for (const auto& value : values) {
+ ASSERT_RESULT_OK(mStore.writeValue(value));
+ }
+
+ VehiclePropValue requestValue = {
+ .prop = toInt(VehicleProperty::TIRE_PRESSURE),
+ .areaId = WHEEL_FRONT_LEFT,
+ };
+
+ auto result = mStore.readValue(requestValue);
+
+ ASSERT_RESULT_OK(result);
+ ASSERT_EQ(*(result.value()), values[1]);
+}
+
+TEST_F(VehiclePropertyStoreTest, testReadValueByPropIdOk) {
+ auto values = getTestPropValues();
+ for (const auto& value : values) {
+ ASSERT_RESULT_OK(mStore.writeValue(value));
+ }
+
+ auto result = mStore.readValue(toInt(VehicleProperty::TIRE_PRESSURE), WHEEL_FRONT_RIGHT);
+
+ ASSERT_EQ(*(result.value()), values[2]);
+}
+
+TEST_F(VehiclePropertyStoreTest, testReadValueError) {
+ auto values = getTestPropValues();
+ for (const auto& value : values) {
+ ASSERT_RESULT_OK(mStore.writeValue(value));
+ }
+
+ auto result = mStore.readValue(toInt(VehicleProperty::TIRE_PRESSURE), WHEEL_REAR_LEFT);
+
+ ASSERT_FALSE(result.ok()) << "expect error when reading a value that has not been written";
+}
+
+TEST_F(VehiclePropertyStoreTest, testWriteValueError) {
+ ASSERT_FALSE(mStore.writeValue({
+ .prop = INVALID_PROP_ID,
+ .value = {.floatValues = {1.0}},
+ })
+ .ok())
+ << "expect error when writing value for an invalid property ID";
+}
+
+TEST_F(VehiclePropertyStoreTest, testWriteValueNoAreaConfig) {
+ ASSERT_FALSE(mStore.writeValue({
+ .prop = toInt(VehicleProperty::TIRE_PRESSURE),
+ .value = {.floatValues = {180.0}},
+ // There is no config for ALL_WHEELS.
+ .areaId = ALL_WHEELS,
+ })
+ .ok())
+ << "expect error when writing value for an area without config";
+}
+
+TEST_F(VehiclePropertyStoreTest, testWriteOutdatedValue) {
+ ASSERT_RESULT_OK(mStore.writeValue({
+ .timestamp = 1,
+ .prop = toInt(VehicleProperty::TIRE_PRESSURE),
+ .value = {.floatValues = {180.0}},
+ .areaId = WHEEL_FRONT_LEFT,
+ }));
+
+ // Write an older value.
+ ASSERT_FALSE(mStore.writeValue({
+ .timestamp = 0,
+ .prop = toInt(VehicleProperty::TIRE_PRESSURE),
+ .value = {.floatValues = {180.0}},
+ .areaId = WHEEL_FRONT_LEFT,
+ })
+ .ok())
+ << "expect error when writing an outdated value";
+}
+
+TEST_F(VehiclePropertyStoreTest, testToken) {
+ int propId = toInt(VehicleProperty::INFO_FUEL_CAPACITY);
+ VehiclePropConfig config = {
+ .prop = propId,
+ };
+
+ // Replace existing config.
+ mStore.registerProperty(config, timestampToken);
+
+ VehiclePropValue fuelCapacityValueToken1 = {
+ .timestamp = 1,
+ .prop = propId,
+ .value = {.floatValues = {1.0}},
+ };
+
+ VehiclePropValue fuelCapacityValueToken2 = {
+ .timestamp = 2,
+ .prop = propId,
+ .value = {.floatValues = {2.0}},
+ };
+
+ ASSERT_RESULT_OK(mStore.writeValue(fuelCapacityValueToken1));
+ ASSERT_RESULT_OK(mStore.writeValue(fuelCapacityValueToken2));
+
+ auto result = mStore.readValuesForProperty(propId);
+
+ ASSERT_RESULT_OK(result);
+ ASSERT_EQ(result.value().size(), static_cast<size_t>(2));
+
+ auto tokenResult = mStore.readValue(propId, /*areaId=*/0, /*token=*/2);
+
+ ASSERT_RESULT_OK(tokenResult);
+ ASSERT_EQ(*(tokenResult.value()), fuelCapacityValueToken2);
+}
+
+TEST_F(VehiclePropertyStoreTest, testRemoveValue) {
+ auto values = getTestPropValues();
+ for (const auto& value : values) {
+ ASSERT_RESULT_OK(mStore.writeValue(value));
+ }
+
+ mStore.removeValue(values[0]);
+
+ ASSERT_FALSE(mStore.readValue(values[0]).ok()) << "expect error when reading a removed value";
+
+ auto leftTirePressureResult = mStore.readValue(values[1]);
+
+ ASSERT_RESULT_OK(leftTirePressureResult);
+ ASSERT_EQ(*(leftTirePressureResult.value()), values[1]);
+}
+
+TEST_F(VehiclePropertyStoreTest, testRemoveValuesForProperty) {
+ auto values = getTestPropValues();
+ for (const auto& value : values) {
+ ASSERT_RESULT_OK(mStore.writeValue(value));
+ }
+
+ mStore.removeValuesForProperty(toInt(VehicleProperty::INFO_FUEL_CAPACITY));
+ mStore.removeValuesForProperty(toInt(VehicleProperty::TIRE_PRESSURE));
+
+ auto gotValues = mStore.readAllValues();
+ ASSERT_TRUE(gotValues.empty());
+}
+
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android