Merge "Use batched property updates in IVehicleHardware." into main
This commit is contained in:
commit
e35cbd5d9a
6 changed files with 365 additions and 68 deletions
|
@ -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);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in a new issue