Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 81fbbea6 authored by Yu Shan's avatar Yu Shan
Browse files

Migrate VehiclePropertyStore.

Test: atest VehicleHalVehicleUtilsTest
Bug: 199337732
Change-Id: Ia18699a0115fdb004c57c0e6fb02b043ddb138b5
parent 9af220ec
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -32,5 +32,6 @@ cc_defaults {
        "-Wall",
        "-Wextra",
        "-Werror",
        "-Wthread-safety",
    ],
}
+136 −0
Original line number Diff line number Diff line
/*
 * 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_
+231 −0
Original line number Diff line number Diff line
/*
 * 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
+5 −1
Original line number Diff line number Diff line
@@ -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"],
}
+314 −0
Original line number Diff line number Diff line
/*
 * 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