Loading automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h +13 −3 Original line number Diff line number Diff line Loading @@ -40,11 +40,17 @@ namespace vehicle { // to get value for all areas for particular property. // // This class is thread-safe, however it uses blocking synchronization across all methods. class VehiclePropertyStore { class VehiclePropertyStore final { public: explicit VehiclePropertyStore(std::shared_ptr<VehiclePropValuePool> valuePool) : mValuePool(valuePool) {} ~VehiclePropertyStore(); // Callback when a property value has been updated or a new value added. using OnValueChangeCallback = std::function<void( const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue&)>; // Function that used to calculate unique token for given VehiclePropValue. using TokenFunction = ::std::function<int64_t( const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value)>; Loading Loading @@ -97,6 +103,9 @@ class VehiclePropertyStore { const ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig*> getConfig(int32_t propId) const; // Set a callback that would be called when a property value has been updated. void setOnValueChangeCallback(const OnValueChangeCallback& callback); private: struct RecordId { int32_t area; Loading @@ -117,10 +126,11 @@ class VehiclePropertyStore { std::unordered_map<RecordId, VehiclePropValuePool::RecyclableType, RecordIdHash> values; }; mutable std::mutex mLock; std::unordered_map<int32_t, Record> mRecordsByPropId GUARDED_BY(mLock); // {@code VehiclePropValuePool} is thread-safe. std::shared_ptr<VehiclePropValuePool> mValuePool; mutable std::mutex mLock; std::unordered_map<int32_t, Record> mRecordsByPropId GUARDED_BY(mLock); OnValueChangeCallback mOnValueChangeCallback GUARDED_BY(mLock); const Record* getRecordLocked(int32_t propId) const; Loading automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp +38 −19 Original line number Diff line number Diff line Loading @@ -49,6 +49,14 @@ size_t VehiclePropertyStore::RecordIdHash::operator()(RecordId const& recordId) return res; } VehiclePropertyStore::~VehiclePropertyStore() { std::lock_guard<std::mutex> lockGuard(mLock); // Recycling record requires mValuePool, so need to recycle them before destroying mValuePool. mRecordsByPropId.clear(); mValuePool.reset(); } const VehiclePropertyStore::Record* VehiclePropertyStore::getRecordLocked(int32_t propId) const REQUIRES(mLock) { auto RecordIt = mRecordsByPropId.find(propId); Loading @@ -75,12 +83,11 @@ VehiclePropertyStore::RecordId VehiclePropertyStore::getRecordIdLocked( Result<VehiclePropValuePool::RecyclableType> 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()); } if (auto it = record.values.find(recId); it != record.values.end()) { return mValuePool->obtain(*(it->second)); } return Errorf("Record ID: {} is not found", recId.toString()); } void VehiclePropertyStore::registerProperty(const VehiclePropConfig& config, VehiclePropertyStore::TokenFunction tokenFunc) { Loading Loading @@ -108,26 +115,31 @@ Result<void> VehiclePropertyStore::writeValue(VehiclePropValuePool::RecyclableTy } VehiclePropertyStore::RecordId recId = getRecordIdLocked(*propValue, *record); auto it = record->values.find(recId); if (it == record->values.end()) { record->values[recId] = std::move(propValue); if (!updateStatus) { record->values[recId]->status = VehiclePropertyStatus::AVAILABLE; } return {}; } bool valueUpdated = true; if (auto it = record->values.find(recId); it != record->values.end()) { const VehiclePropValue* valueToUpdate = it->second.get(); long oldTimestamp = valueToUpdate->timestamp; int64_t oldTimestamp = valueToUpdate->timestamp; VehiclePropertyStatus oldStatus = valueToUpdate->status; // propValue is outdated and drops it. if (oldTimestamp > propValue->timestamp) { return Errorf("outdated timestamp: {:d}", propValue->timestamp); } record->values[recId] = std::move(propValue); if (!updateStatus) { record->values[recId]->status = oldStatus; propValue->status = oldStatus; } valueUpdated = (valueToUpdate->value != propValue->value || valueToUpdate->status != propValue->status || valueToUpdate->prop != propValue->prop || valueToUpdate->areaId != propValue->areaId); } else if (!updateStatus) { propValue->status = VehiclePropertyStatus::AVAILABLE; } record->values[recId] = std::move(propValue); if (valueUpdated && mOnValueChangeCallback != nullptr) { mOnValueChangeCallback(*(record->values[recId])); } return {}; } Loading Loading @@ -236,6 +248,13 @@ Result<const VehiclePropConfig*> VehiclePropertyStore::getConfig(int32_t propId) return &record->propConfig; } void VehiclePropertyStore::setOnValueChangeCallback( const VehiclePropertyStore::OnValueChangeCallback& callback) { std::lock_guard<std::mutex> g(mLock); mOnValueChangeCallback = callback; } } // namespace vehicle } // namespace automotive } // namespace hardware Loading automotive/vehicle/aidl/impl/utils/common/test/VehiclePropertyStoreTest.cpp +51 −0 Original line number Diff line number Diff line Loading @@ -382,6 +382,57 @@ TEST_F(VehiclePropertyStoreTest, testWriteValueNoUpdateStatusForNewValue) { ASSERT_EQ(result.value()->status, VehiclePropertyStatus::AVAILABLE); } TEST_F(VehiclePropertyStoreTest, testPropertyChangeCallbackNewValue) { VehiclePropValue updatedValue; mStore->setOnValueChangeCallback( [&updatedValue](const VehiclePropValue& value) { updatedValue = value; }); VehiclePropValue fuelCapacity = { .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY), .value = {.floatValues = {1.0}}, }; ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity))); ASSERT_EQ(updatedValue, fuelCapacity); } TEST_F(VehiclePropertyStoreTest, testPropertyChangeCallbackUpdateValue) { VehiclePropValue updatedValue; VehiclePropValue fuelCapacity = { .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY), .value = {.floatValues = {1.0}}, }; ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity))); mStore->setOnValueChangeCallback( [&updatedValue](const VehiclePropValue& value) { updatedValue = value; }); fuelCapacity.value.floatValues[0] = 2.0; fuelCapacity.timestamp = 1; ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity))); ASSERT_EQ(updatedValue, fuelCapacity); } TEST_F(VehiclePropertyStoreTest, testPropertyChangeCallbackNoUpdate) { VehiclePropValue updatedValue{ .prop = INVALID_PROP_ID, }; VehiclePropValue fuelCapacity = { .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY), .value = {.floatValues = {1.0}}, }; ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity))); mStore->setOnValueChangeCallback( [&updatedValue](const VehiclePropValue& value) { updatedValue = value; }); // Write the same value again should succeed but should not trigger callback. ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity))); ASSERT_EQ(updatedValue.prop, INVALID_PROP_ID); } } // namespace vehicle } // namespace automotive } // namespace hardware Loading Loading
automotive/vehicle/aidl/impl/utils/common/include/VehiclePropertyStore.h +13 −3 Original line number Diff line number Diff line Loading @@ -40,11 +40,17 @@ namespace vehicle { // to get value for all areas for particular property. // // This class is thread-safe, however it uses blocking synchronization across all methods. class VehiclePropertyStore { class VehiclePropertyStore final { public: explicit VehiclePropertyStore(std::shared_ptr<VehiclePropValuePool> valuePool) : mValuePool(valuePool) {} ~VehiclePropertyStore(); // Callback when a property value has been updated or a new value added. using OnValueChangeCallback = std::function<void( const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue&)>; // Function that used to calculate unique token for given VehiclePropValue. using TokenFunction = ::std::function<int64_t( const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value)>; Loading Loading @@ -97,6 +103,9 @@ class VehiclePropertyStore { const ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig*> getConfig(int32_t propId) const; // Set a callback that would be called when a property value has been updated. void setOnValueChangeCallback(const OnValueChangeCallback& callback); private: struct RecordId { int32_t area; Loading @@ -117,10 +126,11 @@ class VehiclePropertyStore { std::unordered_map<RecordId, VehiclePropValuePool::RecyclableType, RecordIdHash> values; }; mutable std::mutex mLock; std::unordered_map<int32_t, Record> mRecordsByPropId GUARDED_BY(mLock); // {@code VehiclePropValuePool} is thread-safe. std::shared_ptr<VehiclePropValuePool> mValuePool; mutable std::mutex mLock; std::unordered_map<int32_t, Record> mRecordsByPropId GUARDED_BY(mLock); OnValueChangeCallback mOnValueChangeCallback GUARDED_BY(mLock); const Record* getRecordLocked(int32_t propId) const; Loading
automotive/vehicle/aidl/impl/utils/common/src/VehiclePropertyStore.cpp +38 −19 Original line number Diff line number Diff line Loading @@ -49,6 +49,14 @@ size_t VehiclePropertyStore::RecordIdHash::operator()(RecordId const& recordId) return res; } VehiclePropertyStore::~VehiclePropertyStore() { std::lock_guard<std::mutex> lockGuard(mLock); // Recycling record requires mValuePool, so need to recycle them before destroying mValuePool. mRecordsByPropId.clear(); mValuePool.reset(); } const VehiclePropertyStore::Record* VehiclePropertyStore::getRecordLocked(int32_t propId) const REQUIRES(mLock) { auto RecordIt = mRecordsByPropId.find(propId); Loading @@ -75,12 +83,11 @@ VehiclePropertyStore::RecordId VehiclePropertyStore::getRecordIdLocked( Result<VehiclePropValuePool::RecyclableType> 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()); } if (auto it = record.values.find(recId); it != record.values.end()) { return mValuePool->obtain(*(it->second)); } return Errorf("Record ID: {} is not found", recId.toString()); } void VehiclePropertyStore::registerProperty(const VehiclePropConfig& config, VehiclePropertyStore::TokenFunction tokenFunc) { Loading Loading @@ -108,26 +115,31 @@ Result<void> VehiclePropertyStore::writeValue(VehiclePropValuePool::RecyclableTy } VehiclePropertyStore::RecordId recId = getRecordIdLocked(*propValue, *record); auto it = record->values.find(recId); if (it == record->values.end()) { record->values[recId] = std::move(propValue); if (!updateStatus) { record->values[recId]->status = VehiclePropertyStatus::AVAILABLE; } return {}; } bool valueUpdated = true; if (auto it = record->values.find(recId); it != record->values.end()) { const VehiclePropValue* valueToUpdate = it->second.get(); long oldTimestamp = valueToUpdate->timestamp; int64_t oldTimestamp = valueToUpdate->timestamp; VehiclePropertyStatus oldStatus = valueToUpdate->status; // propValue is outdated and drops it. if (oldTimestamp > propValue->timestamp) { return Errorf("outdated timestamp: {:d}", propValue->timestamp); } record->values[recId] = std::move(propValue); if (!updateStatus) { record->values[recId]->status = oldStatus; propValue->status = oldStatus; } valueUpdated = (valueToUpdate->value != propValue->value || valueToUpdate->status != propValue->status || valueToUpdate->prop != propValue->prop || valueToUpdate->areaId != propValue->areaId); } else if (!updateStatus) { propValue->status = VehiclePropertyStatus::AVAILABLE; } record->values[recId] = std::move(propValue); if (valueUpdated && mOnValueChangeCallback != nullptr) { mOnValueChangeCallback(*(record->values[recId])); } return {}; } Loading Loading @@ -236,6 +248,13 @@ Result<const VehiclePropConfig*> VehiclePropertyStore::getConfig(int32_t propId) return &record->propConfig; } void VehiclePropertyStore::setOnValueChangeCallback( const VehiclePropertyStore::OnValueChangeCallback& callback) { std::lock_guard<std::mutex> g(mLock); mOnValueChangeCallback = callback; } } // namespace vehicle } // namespace automotive } // namespace hardware Loading
automotive/vehicle/aidl/impl/utils/common/test/VehiclePropertyStoreTest.cpp +51 −0 Original line number Diff line number Diff line Loading @@ -382,6 +382,57 @@ TEST_F(VehiclePropertyStoreTest, testWriteValueNoUpdateStatusForNewValue) { ASSERT_EQ(result.value()->status, VehiclePropertyStatus::AVAILABLE); } TEST_F(VehiclePropertyStoreTest, testPropertyChangeCallbackNewValue) { VehiclePropValue updatedValue; mStore->setOnValueChangeCallback( [&updatedValue](const VehiclePropValue& value) { updatedValue = value; }); VehiclePropValue fuelCapacity = { .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY), .value = {.floatValues = {1.0}}, }; ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity))); ASSERT_EQ(updatedValue, fuelCapacity); } TEST_F(VehiclePropertyStoreTest, testPropertyChangeCallbackUpdateValue) { VehiclePropValue updatedValue; VehiclePropValue fuelCapacity = { .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY), .value = {.floatValues = {1.0}}, }; ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity))); mStore->setOnValueChangeCallback( [&updatedValue](const VehiclePropValue& value) { updatedValue = value; }); fuelCapacity.value.floatValues[0] = 2.0; fuelCapacity.timestamp = 1; ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity))); ASSERT_EQ(updatedValue, fuelCapacity); } TEST_F(VehiclePropertyStoreTest, testPropertyChangeCallbackNoUpdate) { VehiclePropValue updatedValue{ .prop = INVALID_PROP_ID, }; VehiclePropValue fuelCapacity = { .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY), .value = {.floatValues = {1.0}}, }; ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity))); mStore->setOnValueChangeCallback( [&updatedValue](const VehiclePropValue& value) { updatedValue = value; }); // Write the same value again should succeed but should not trigger callback. ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity))); ASSERT_EQ(updatedValue.prop, INVALID_PROP_ID); } } // namespace vehicle } // namespace automotive } // namespace hardware Loading