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