Merge "Use batched property updates in IVehicleHardware." into main

This commit is contained in:
Yu Shan 2023-12-11 22:29:45 +00:00 committed by Android (Google) Code Review
commit e35cbd5d9a
6 changed files with 365 additions and 68 deletions

View file

@ -142,6 +142,16 @@ class FakeVehicleHardware : public IVehicleHardware {
void handleRequestsOnce();
};
struct RefreshInfo {
VehiclePropertyStore::EventMode eventMode;
int64_t intervalInNanos;
};
struct ActionForInterval {
std::unordered_set<PropIdAreaId, PropIdAreaIdHash> propIdAreaIdsToRefresh;
std::shared_ptr<RecurrentTimer::Callback> recurrentAction;
};
const std::unique_ptr<obd2frame::FakeObd2Frame> mFakeObd2Frame;
const std::unique_ptr<FakeUserHal> mFakeUserHal;
// RecurrentTimer is thread-safe.
@ -154,8 +164,9 @@ class FakeVehicleHardware : public IVehicleHardware {
std::unique_ptr<const PropertySetErrorCallback> mOnPropertySetErrorCallback;
std::mutex mLock;
std::unordered_map<PropIdAreaId, std::shared_ptr<RecurrentTimer::Callback>, PropIdAreaIdHash>
mRecurrentActions GUARDED_BY(mLock);
std::unordered_map<PropIdAreaId, RefreshInfo, PropIdAreaIdHash> mRefreshInfoByPropIdAreaId
GUARDED_BY(mLock);
std::unordered_map<int64_t, ActionForInterval> mActionByIntervalInNanos GUARDED_BY(mLock);
std::unordered_map<PropIdAreaId, VehiclePropValuePool::RecyclableType, PropIdAreaIdHash>
mSavedProps GUARDED_BY(mLock);
std::unordered_set<PropIdAreaId, PropIdAreaIdHash> mSubOnChangePropIdAreaIds GUARDED_BY(mLock);
@ -183,6 +194,10 @@ class FakeVehicleHardware : public IVehicleHardware {
void onValueChangeCallback(
const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value)
EXCLUDES(mLock);
// The callback that would be called when multiple vehicle property value changes happen.
void onValuesChangeCallback(
std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue> values)
EXCLUDES(mLock);
// Load the config files in format '*.json' from the directory and parse the config files
// into a map from property ID to ConfigDeclarations.
void loadPropConfigsFromDir(const std::string& dirPath,
@ -276,6 +291,11 @@ class FakeVehicleHardware : public IVehicleHardware {
const aidl::android::hardware::automotive::vehicle::VehiclePropConfig&
vehiclePropConfig) REQUIRES(mLock);
void registerRefreshLocked(PropIdAreaId propIdAreaId, VehiclePropertyStore::EventMode eventMode,
float sampleRateHz) REQUIRES(mLock);
void unregisterRefreshLocked(PropIdAreaId propIdAreaId) REQUIRES(mLock);
void refreshTimeStampForInterval(int64_t intervalInNanos) EXCLUDES(mLock);
static aidl::android::hardware::automotive::vehicle::VehiclePropValue createHwInputKeyProp(
aidl::android::hardware::automotive::vehicle::VehicleHwKeyInputAction action,
int32_t keyCode, int32_t targetDisplay);

View file

@ -346,8 +346,9 @@ void FakeVehicleHardware::init() {
mFakeObd2Frame->initObd2FreezeFrame(maybeObd2FreezeFrame.value());
}
mServerSidePropStore->setOnValueChangeCallback(
[this](const VehiclePropValue& value) { return onValueChangeCallback(value); });
mServerSidePropStore->setOnValuesChangeCallback([this](std::vector<VehiclePropValue> values) {
return onValuesChangeCallback(std::move(values));
});
}
std::vector<VehiclePropConfig> FakeVehicleHardware::getAllPropertyConfigs() const {
@ -2080,6 +2081,81 @@ bool FakeVehicleHardware::isVariableUpdateRateSupported(const VehiclePropConfig&
return false;
}
void FakeVehicleHardware::refreshTimeStampForInterval(int64_t intervalInNanos) {
std::unordered_map<PropIdAreaId, VehiclePropertyStore::EventMode, PropIdAreaIdHash>
eventModeByPropIdAreaId;
{
std::scoped_lock<std::mutex> lockGuard(mLock);
if (mActionByIntervalInNanos.find(intervalInNanos) == mActionByIntervalInNanos.end()) {
ALOGE("No actions scheduled for the interval: %" PRId64 ", ignore the refresh request",
intervalInNanos);
return;
}
ActionForInterval actionForInterval = mActionByIntervalInNanos[intervalInNanos];
// Make a copy so that we don't hold the lock while trying to refresh the timestamp.
// Refreshing the timestamp will inovke onValueChangeCallback which also requires lock, so
// we must not hold lock.
for (const PropIdAreaId& propIdAreaId : actionForInterval.propIdAreaIdsToRefresh) {
const RefreshInfo& refreshInfo = mRefreshInfoByPropIdAreaId[propIdAreaId];
eventModeByPropIdAreaId[propIdAreaId] = refreshInfo.eventMode;
}
}
mServerSidePropStore->refreshTimestamps(eventModeByPropIdAreaId);
}
void FakeVehicleHardware::registerRefreshLocked(PropIdAreaId propIdAreaId,
VehiclePropertyStore::EventMode eventMode,
float sampleRateHz) {
if (mRefreshInfoByPropIdAreaId.find(propIdAreaId) != mRefreshInfoByPropIdAreaId.end()) {
unregisterRefreshLocked(propIdAreaId);
}
int64_t intervalInNanos = static_cast<int64_t>(1'000'000'000. / sampleRateHz);
RefreshInfo refreshInfo = {
.eventMode = eventMode,
.intervalInNanos = intervalInNanos,
};
mRefreshInfoByPropIdAreaId[propIdAreaId] = refreshInfo;
if (mActionByIntervalInNanos.find(intervalInNanos) != mActionByIntervalInNanos.end()) {
// If we have already registered for this interval, then add the action info to the
// actions list.
mActionByIntervalInNanos[intervalInNanos].propIdAreaIdsToRefresh.insert(propIdAreaId);
return;
}
// This is the first action for the interval, register a timer callback for that interval.
auto action = std::make_shared<RecurrentTimer::Callback>(
[this, intervalInNanos] { refreshTimeStampForInterval(intervalInNanos); });
mActionByIntervalInNanos[intervalInNanos] = ActionForInterval{
.propIdAreaIdsToRefresh = {propIdAreaId},
.recurrentAction = action,
};
mRecurrentTimer->registerTimerCallback(intervalInNanos, action);
}
void FakeVehicleHardware::unregisterRefreshLocked(PropIdAreaId propIdAreaId) {
if (mRefreshInfoByPropIdAreaId.find(propIdAreaId) == mRefreshInfoByPropIdAreaId.end()) {
ALOGW("PropId: %" PRId32 ", areaId: %" PRId32 " was not registered for refresh, ignore",
propIdAreaId.propId, propIdAreaId.areaId);
return;
}
int64_t intervalInNanos = mRefreshInfoByPropIdAreaId[propIdAreaId].intervalInNanos;
auto& actionForInterval = mActionByIntervalInNanos[intervalInNanos];
actionForInterval.propIdAreaIdsToRefresh.erase(propIdAreaId);
if (actionForInterval.propIdAreaIdsToRefresh.empty()) {
mRecurrentTimer->unregisterTimerCallback(actionForInterval.recurrentAction);
mActionByIntervalInNanos.erase(intervalInNanos);
}
mRefreshInfoByPropIdAreaId.erase(propIdAreaId);
}
StatusCode FakeVehicleHardware::subscribePropIdAreaIdLocked(
int32_t propId, int32_t areaId, float sampleRateHz, bool enableVariableUpdateRate,
const VehiclePropConfig& vehiclePropConfig) {
@ -2099,11 +2175,6 @@ StatusCode FakeVehicleHardware::subscribePropIdAreaIdLocked(
ALOGE("Must not use sample rate 0 for a continuous property");
return StatusCode::INTERNAL_ERROR;
}
if (mRecurrentActions.find(propIdAreaId) != mRecurrentActions.end()) {
mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propIdAreaId]);
}
int64_t intervalInNanos = static_cast<int64_t>(1'000'000'000. / sampleRateHz);
// For continuous properties, we must generate a new onPropertyChange event
// periodically according to the sample rate.
auto eventMode = VehiclePropertyStore::EventMode::ALWAYS;
@ -2111,12 +2182,8 @@ StatusCode FakeVehicleHardware::subscribePropIdAreaIdLocked(
enableVariableUpdateRate) {
eventMode = VehiclePropertyStore::EventMode::ON_VALUE_CHANGE;
}
auto action =
std::make_shared<RecurrentTimer::Callback>([this, propId, areaId, eventMode] {
mServerSidePropStore->refreshTimestamp(propId, areaId, eventMode);
});
mRecurrentTimer->registerTimerCallback(intervalInNanos, action);
mRecurrentActions[propIdAreaId] = action;
registerRefreshLocked(propIdAreaId, eventMode, sampleRateHz);
return StatusCode::OK;
}
}
@ -2127,39 +2194,47 @@ StatusCode FakeVehicleHardware::unsubscribe(int32_t propId, int32_t areaId) {
.propId = propId,
.areaId = areaId,
};
if (mRecurrentActions.find(propIdAreaId) != mRecurrentActions.end()) {
mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propIdAreaId]);
mRecurrentActions.erase(propIdAreaId);
if (mRefreshInfoByPropIdAreaId.find(propIdAreaId) != mRefreshInfoByPropIdAreaId.end()) {
unregisterRefreshLocked(propIdAreaId);
}
mSubOnChangePropIdAreaIds.erase(propIdAreaId);
return StatusCode::OK;
}
void FakeVehicleHardware::onValueChangeCallback(const VehiclePropValue& value) {
if (mOnPropertyChangeCallback == nullptr) {
return;
}
ATRACE_CALL();
onValuesChangeCallback({value});
}
PropIdAreaId propIdAreaId{
.propId = value.prop,
.areaId = value.areaId,
};
void FakeVehicleHardware::onValuesChangeCallback(std::vector<VehiclePropValue> values) {
ATRACE_CALL();
std::vector<VehiclePropValue> subscribedUpdatedValues;
{
std::scoped_lock<std::mutex> lockGuard(mLock);
if (mRecurrentActions.find(propIdAreaId) == mRecurrentActions.end() &&
mSubOnChangePropIdAreaIds.find(propIdAreaId) == mSubOnChangePropIdAreaIds.end()) {
if (FAKE_VEHICLEHARDWARE_DEBUG) {
ALOGD("The updated property value: %s is not subscribed, ignore",
value.toString().c_str());
}
if (mOnPropertyChangeCallback == nullptr) {
return;
}
for (const auto& value : values) {
PropIdAreaId propIdAreaId{
.propId = value.prop,
.areaId = value.areaId,
};
if (mRefreshInfoByPropIdAreaId.find(propIdAreaId) == mRefreshInfoByPropIdAreaId.end() &&
mSubOnChangePropIdAreaIds.find(propIdAreaId) == mSubOnChangePropIdAreaIds.end()) {
if (FAKE_VEHICLEHARDWARE_DEBUG) {
ALOGD("The updated property value: %s is not subscribed, ignore",
value.toString().c_str());
}
continue;
}
subscribedUpdatedValues.push_back(value);
}
}
std::vector<VehiclePropValue> updatedValues;
updatedValues.push_back(value);
(*mOnPropertyChangeCallback)(std::move(updatedValues));
(*mOnPropertyChangeCallback)(std::move(subscribedUpdatedValues));
}
void FakeVehicleHardware::loadPropConfigsFromDir(

View file

@ -48,21 +48,21 @@ class VehiclePropertyStore final {
enum class EventMode : uint8_t {
/**
* Only invoke OnValueChangeCallback if the new property value (ignoring timestamp) is
* different than the existing value.
* Only invoke OnValueChangeCallback or OnValuesChangeCallback if the new property value
* (ignoring timestamp) is different than the existing value.
*
* This should be used for regular cases.
*/
ON_VALUE_CHANGE,
/**
* Always invoke OnValueChangeCallback.
* Always invoke OnValueChangeCallback or OnValuesChangeCallback.
*
* This should be used for the special properties that are used for delivering event, e.g.
* HW_KEY_INPUT.
*/
ALWAYS,
/**
* Never invoke OnValueChangeCallback.
* Never invoke OnValueChangeCallback or OnValuesChangeCalblack.
*
* This should be used for continuous property subscription when the sample rate for the
* subscription is smaller than the refresh rate for the property. E.g., the vehicle speed
@ -82,6 +82,10 @@ class VehiclePropertyStore final {
using OnValueChangeCallback = std::function<void(
const aidl::android::hardware::automotive::vehicle::VehiclePropValue&)>;
// Callback when one or more property values have been updated or new values added.
using OnValuesChangeCallback = std::function<void(
std::vector<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)>;
@ -99,7 +103,8 @@ class VehiclePropertyStore final {
// 'status' would be initialized to {@code VehiclePropertyStatus::AVAILABLE}, if this is to
// override an existing value, the status for the existing value would be used for the
// overridden value.
// 'EventMode' controls whether the 'OnValueChangeCallback' will be called for this operation.
// 'EventMode' controls whether the 'OnValueChangeCallback' or 'OnValuesChangeCallback' will be
// called for this operation.
// If 'useCurrentTimestamp' is true, the property value will be set to the current timestamp.
VhalResult<void> writeValue(VehiclePropValuePool::RecyclableType propValue,
bool updateStatus = false,
@ -111,6 +116,11 @@ class VehiclePropertyStore final {
// without generating event. This operation is atomic with other writeValue operations.
void refreshTimestamp(int32_t propId, int32_t areaId, EventMode eventMode) EXCLUDES(mLock);
// Refresh the timestamp for multiple [propId, areaId]s.
void refreshTimestamps(
std::unordered_map<PropIdAreaId, EventMode, PropIdAreaIdHash> eventModeByPropIdAreaId)
EXCLUDES(mLock);
// 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(
@ -157,6 +167,13 @@ class VehiclePropertyStore final {
// Set a callback that would be called when a property value has been updated.
void setOnValueChangeCallback(const OnValueChangeCallback& callback) EXCLUDES(mLock);
// Set a callback that would be called when one or more property values have been updated.
// For backward compatibility, this is optional. If this is not set, then multiple property
// updates will be delivered through multiple OnValueChangeCallback instead.
// It is recommended to set this and batch the property update events for better performance.
// If this is set, then OnValueChangeCallback will not be used.
void setOnValuesChangeCallback(const OnValuesChangeCallback& callback) EXCLUDES(mLock);
inline std::shared_ptr<VehiclePropValuePool> getValuePool() { return mValuePool; }
private:
@ -184,6 +201,7 @@ class VehiclePropertyStore final {
mutable std::mutex mLock;
std::unordered_map<int32_t, Record> mRecordsByPropId GUARDED_BY(mLock);
OnValueChangeCallback mOnValueChangeCallback GUARDED_BY(mLock);
OnValuesChangeCallback mOnValuesChangeCallback GUARDED_BY(mLock);
const Record* getRecordLocked(int32_t propId) const;

View file

@ -113,6 +113,9 @@ VhalResult<void> VehiclePropertyStore::writeValue(VehiclePropValuePool::Recyclab
bool valueUpdated = true;
VehiclePropValue updatedValue;
OnValueChangeCallback onValueChangeCallback = nullptr;
OnValuesChangeCallback onValuesChangeCallback = nullptr;
int32_t propId;
int32_t areaId;
{
std::scoped_lock<std::mutex> g(mLock);
@ -122,7 +125,8 @@ VhalResult<void> VehiclePropertyStore::writeValue(VehiclePropValuePool::Recyclab
propValue->timestamp = elapsedRealtimeNano();
}
int32_t propId = propValue->prop;
propId = propValue->prop;
areaId = propValue->areaId;
VehiclePropertyStore::Record* record = getRecordLocked(propId);
if (record == nullptr) {
@ -163,52 +167,96 @@ VhalResult<void> VehiclePropertyStore::writeValue(VehiclePropValuePool::Recyclab
return {};
}
updatedValue = *(record->values[recId]);
if (mOnValueChangeCallback == nullptr) {
return {};
}
onValuesChangeCallback = mOnValuesChangeCallback;
onValueChangeCallback = mOnValueChangeCallback;
}
if (onValuesChangeCallback == nullptr && onValueChangeCallback == nullptr) {
ALOGW("No callback registered, ignoring property update for propId: %" PRId32
", area ID: %" PRId32,
propId, areaId);
return {};
}
// Invoke the callback outside the lock to prevent dead-lock.
if (eventMode == EventMode::ALWAYS || valueUpdated) {
onValueChangeCallback(updatedValue);
if (onValuesChangeCallback != nullptr) {
onValuesChangeCallback({updatedValue});
} else {
onValueChangeCallback(updatedValue);
}
}
return {};
}
void VehiclePropertyStore::refreshTimestamp(int32_t propId, int32_t areaId, EventMode eventMode) {
VehiclePropValue updatedValue;
std::unordered_map<PropIdAreaId, EventMode, PropIdAreaIdHash> eventModeByPropIdAreaId;
PropIdAreaId propIdAreaId = {
.propId = propId,
.areaId = areaId,
};
eventModeByPropIdAreaId[propIdAreaId] = eventMode;
refreshTimestamps(eventModeByPropIdAreaId);
}
void VehiclePropertyStore::refreshTimestamps(
std::unordered_map<PropIdAreaId, EventMode, PropIdAreaIdHash> eventModeByPropIdAreaId) {
std::vector<VehiclePropValue> updatedValues;
OnValuesChangeCallback onValuesChangeCallback = nullptr;
OnValueChangeCallback onValueChangeCallback = nullptr;
{
std::scoped_lock<std::mutex> g(mLock);
VehiclePropertyStore::Record* record = getRecordLocked(propId);
if (record == nullptr) {
return;
}
VehiclePropValue propValue = {
.areaId = areaId,
.prop = propId,
.value = {},
};
VehiclePropertyStore::RecordId recId = getRecordIdLocked(propValue, *record);
if (auto it = record->values.find(recId); it != record->values.end()) {
it->second->timestamp = elapsedRealtimeNano();
updatedValue = *(it->second);
} else {
return;
}
if (!mOnValueChangeCallback) {
return;
}
onValuesChangeCallback = mOnValuesChangeCallback;
onValueChangeCallback = mOnValueChangeCallback;
for (const auto& [propIdAreaId, eventMode] : eventModeByPropIdAreaId) {
int32_t propId = propIdAreaId.propId;
int32_t areaId = propIdAreaId.areaId;
VehiclePropertyStore::Record* record = getRecordLocked(propId);
if (record == nullptr) {
continue;
}
VehiclePropValue propValue = {
.areaId = areaId,
.prop = propId,
.value = {},
};
VehiclePropertyStore::RecordId recId = getRecordIdLocked(propValue, *record);
if (auto it = record->values.find(recId); it != record->values.end()) {
it->second->timestamp = elapsedRealtimeNano();
if (eventMode == EventMode::ALWAYS) {
updatedValues.push_back(*(it->second));
}
} else {
continue;
}
}
}
// Invoke the callback outside the lock to prevent dead-lock.
if (eventMode == EventMode::ALWAYS) {
onValueChangeCallback(updatedValue);
if (updatedValues.empty()) {
return;
}
if (!onValuesChangeCallback && !onValueChangeCallback) {
// If no callback is set, then we don't have to do anything.
for (const auto& updateValue : updatedValues) {
ALOGW("No callback registered, ignoring property update for propId: %" PRId32
", area ID: %" PRId32,
updateValue.prop, updateValue.areaId);
}
return;
}
if (onValuesChangeCallback != nullptr) {
onValuesChangeCallback(updatedValues);
} else {
// Fallback to use multiple onValueChangeCallback
for (const auto& updateValue : updatedValues) {
onValueChangeCallback(updateValue);
}
}
}
@ -336,6 +384,13 @@ void VehiclePropertyStore::setOnValueChangeCallback(
mOnValueChangeCallback = callback;
}
void VehiclePropertyStore::setOnValuesChangeCallback(
const VehiclePropertyStore::OnValuesChangeCallback& callback) {
std::scoped_lock<std::mutex> g(mLock);
mOnValuesChangeCallback = callback;
}
} // namespace vehicle
} // namespace automotive
} // namespace hardware

View file

@ -20,6 +20,7 @@
#include <VehicleUtils.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <utils/SystemClock.h>
namespace android {
namespace hardware {
@ -527,6 +528,133 @@ TEST_F(VehiclePropertyStoreTest, testPropertyChangeCallbackUseVehiclePropertySto
ASSERT_EQ(configs.size(), static_cast<size_t>(2));
}
TEST_F(VehiclePropertyStoreTest, testOnValuesChangeCallback) {
std::vector<VehiclePropValue> updatedValues;
VehiclePropValue fuelCapacity = {
.prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY),
.value = {.floatValues = {1.0}},
};
ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity)));
mStore->setOnValuesChangeCallback(
[&updatedValues](std::vector<VehiclePropValue> values) { updatedValues = values; });
fuelCapacity.value.floatValues[0] = 2.0;
fuelCapacity.timestamp = 1;
ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity)));
ASSERT_THAT(updatedValues, ElementsAre(fuelCapacity));
}
TEST_F(VehiclePropertyStoreTest, testRefreshTimestamp) {
std::vector<VehiclePropValue> updatedValues;
mStore->setOnValuesChangeCallback(
[&updatedValues](std::vector<VehiclePropValue> values) { updatedValues = values; });
int64_t now = elapsedRealtimeNano();
int propId = toInt(VehicleProperty::TIRE_PRESSURE);
int areaId = WHEEL_FRONT_LEFT;
VehiclePropValue tirePressure = {
.prop = propId,
.areaId = areaId,
.value = {.floatValues = {1.0}},
};
ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(tirePressure)));
updatedValues.clear();
mStore->refreshTimestamp(propId, areaId, VehiclePropertyStore::EventMode::ALWAYS);
ASSERT_EQ(updatedValues.size(), 1u);
ASSERT_EQ(updatedValues[0].prop, propId);
ASSERT_EQ(updatedValues[0].areaId, areaId);
ASSERT_EQ(updatedValues[0].value.floatValues[0], 1.0);
int64_t timestamp = updatedValues[0].timestamp;
ASSERT_GE(timestamp, now);
auto result = mStore->readValue(tirePressure);
ASSERT_RESULT_OK(result);
ASSERT_EQ((result.value())->timestamp, timestamp);
}
TEST_F(VehiclePropertyStoreTest, testRefreshTimestamp_eventModeOnValueChange) {
std::vector<VehiclePropValue> updatedValues;
mStore->setOnValuesChangeCallback(
[&updatedValues](std::vector<VehiclePropValue> values) { updatedValues = values; });
int64_t now = elapsedRealtimeNano();
int propId = toInt(VehicleProperty::TIRE_PRESSURE);
int areaId = WHEEL_FRONT_LEFT;
VehiclePropValue tirePressure = {
.prop = propId,
.areaId = areaId,
.value = {.floatValues = {1.0}},
};
ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(tirePressure)));
updatedValues.clear();
mStore->refreshTimestamp(propId, areaId, VehiclePropertyStore::EventMode::ON_VALUE_CHANGE);
ASSERT_EQ(updatedValues.size(), 0u)
<< "Must generate no property update events if only the timestamp is refreshed";
auto result = mStore->readValue(tirePressure);
ASSERT_RESULT_OK(result);
ASSERT_GE((result.value())->timestamp, now)
<< "Even though event mode is on value change, the store timestamp must be updated";
}
TEST_F(VehiclePropertyStoreTest, testRefreshTimestamps) {
std::vector<VehiclePropValue> updatedValues;
mStore->setOnValuesChangeCallback(
[&updatedValues](std::vector<VehiclePropValue> values) { updatedValues = values; });
int64_t now = elapsedRealtimeNano();
int propId1 = toInt(VehicleProperty::INFO_FUEL_CAPACITY);
int areaId1 = 0;
VehiclePropValue fuelCapacity = {
.prop = propId1,
.areaId = areaId1,
.value = {.floatValues = {1.0}},
};
ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity)));
int propId2 = toInt(VehicleProperty::TIRE_PRESSURE);
int areaId2 = WHEEL_FRONT_LEFT;
VehiclePropValue tirePressure = {
.prop = propId2,
.areaId = areaId2,
.value = {.floatValues = {2.0}},
};
ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(tirePressure)));
updatedValues.clear();
std::unordered_map<PropIdAreaId, VehiclePropertyStore::EventMode, PropIdAreaIdHash>
eventModeByPropIdAreaId;
eventModeByPropIdAreaId[PropIdAreaId{
.propId = propId1,
.areaId = areaId1,
}] = VehiclePropertyStore::EventMode::ALWAYS;
eventModeByPropIdAreaId[PropIdAreaId{
.propId = propId2,
.areaId = areaId2,
}] = VehiclePropertyStore::EventMode::ALWAYS;
mStore->refreshTimestamps(eventModeByPropIdAreaId);
ASSERT_EQ(updatedValues.size(), 2u);
ASSERT_EQ(updatedValues[0].prop, propId1);
ASSERT_EQ(updatedValues[0].areaId, areaId1);
ASSERT_EQ(updatedValues[0].value.floatValues[0], 1.0);
ASSERT_GE(updatedValues[0].timestamp, now);
ASSERT_EQ(updatedValues[1].prop, propId2);
ASSERT_EQ(updatedValues[1].areaId, areaId2);
ASSERT_EQ(updatedValues[1].value.floatValues[0], 2.0);
ASSERT_GE(updatedValues[1].timestamp, now);
}
} // namespace vehicle
} // namespace automotive
} // namespace hardware

View file

@ -194,6 +194,7 @@ void DefaultVehicleHal::handleBatchedPropertyEvents(std::vector<VehiclePropValue
void DefaultVehicleHal::onPropertyChangeEvent(
const std::weak_ptr<SubscriptionManager>& subscriptionManager,
std::vector<VehiclePropValue>&& updatedValues) {
ATRACE_CALL();
auto manager = subscriptionManager.lock();
if (manager == nullptr) {
ALOGW("the SubscriptionManager is destroyed, DefaultVehicleHal is ending");