diff --git a/automotive/vehicle/aidl/impl/Android.bp b/automotive/vehicle/aidl/impl/Android.bp index 0b98c7edd0..a97d54492e 100644 --- a/automotive/vehicle/aidl/impl/Android.bp +++ b/automotive/vehicle/aidl/impl/Android.bp @@ -32,5 +32,6 @@ cc_defaults { "-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 0000000000..b19ab841a8 --- /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 +#include +#include +#include +#include + +#include +#include +#include + +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; + + // 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 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 values; + }; + + mutable std::mutex mLock; + std::unordered_map 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 0000000000..b660f36d65 --- /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 + +#include "VehiclePropertyStore.h" + +#include +#include + +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> 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(it->second); +} + +void VehiclePropertyStore::registerProperty(const VehiclePropConfig& config, + VehiclePropertyStore::TokenFunction tokenFunc) { + std::lock_guard g(mLock); + + mRecordsByPropId[config.prop] = Record{ + .propConfig = config, + .tokenFunction = tokenFunc, + }; +} + +Result VehiclePropertyStore::writeValue(const VehiclePropValue& propValue) { + std::lock_guard 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 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 g(mLock); + + VehiclePropertyStore::Record* record = getRecordLocked(propId); + if (record == nullptr) { + return; + } + + record->values.clear(); +} + +std::vector VehiclePropertyStore::readAllValues() const { + std::lock_guard g(mLock); + + std::vector allValues; + + for (auto const& [_, record] : mRecordsByPropId) { + for (auto const& [_, value] : record.values) { + allValues.push_back(value); + } + } + + return allValues; +} + +Result> VehiclePropertyStore::readValuesForProperty( + int32_t propId) const { + std::lock_guard g(mLock); + + std::vector 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> VehiclePropertyStore::readValue( + const VehiclePropValue& propValue) const { + std::lock_guard 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> VehiclePropertyStore::readValue(int32_t propId, + int32_t areaId, + int64_t token) const { + std::lock_guard 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 VehiclePropertyStore::getAllConfigs() const { + std::lock_guard g(mLock); + + std::vector configs; + configs.reserve(mRecordsByPropId.size()); + for (auto& [_, config] : mRecordsByPropId) { + configs.push_back(config.propConfig); + } + return configs; +} + +Result VehiclePropertyStore::getConfig(int32_t propId) const { + std::lock_guard 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 40744cef8e..65b0e4685c 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 @@ cc_test { 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 0000000000..8c70fea924 --- /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 +#include +#include +#include +#include + +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 configs = mStore.getAllConfigs(); + + ASSERT_EQ(configs.size(), static_cast(2)); +} + +TEST_F(VehiclePropertyStoreTest, testGetConfig) { + Result result = + mStore.getConfig(toInt(VehicleProperty::INFO_FUEL_CAPACITY)); + + ASSERT_RESULT_OK(result); + ASSERT_EQ(*(result.value()), mConfigFuelCapacity); +} + +TEST_F(VehiclePropertyStoreTest, testGetConfigWithInvalidPropId) { + Result result = mStore.getConfig(INVALID_PROP_ID); + + ASSERT_FALSE(result.ok()) << "expect error when getting a config for an invalid property ID"; +} + +std::vector 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(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