/* * 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 #include #include "VehiclePropertyStore.h" #include #include #include #include #include namespace android { namespace hardware { namespace automotive { namespace vehicle { using ::aidl::android::hardware::automotive::vehicle::StatusCode; using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig; using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig; using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus; using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; using ::android::base::Result; using ::android::base::StringPrintf; bool VehiclePropertyStore::RecordId::operator==(const VehiclePropertyStore::RecordId& other) const { return area == other.area && token == other.token; } std::string VehiclePropertyStore::RecordId::toString() const { return StringPrintf("RecordID{{.areaId=% " PRId32 ", .token=%" PRId64 "}", area, token); } size_t VehiclePropertyStore::RecordIdHash::operator()(RecordId const& recordId) const { size_t res = 0; hashCombine(res, recordId.area); hashCombine(res, recordId.token); return res; } VehiclePropertyStore::~VehiclePropertyStore() { std::scoped_lock 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); 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; } VhalResult VehiclePropertyStore::readValueLocked( const RecordId& recId, const Record& record) const REQUIRES(mLock) { if (auto it = record.values.find(recId); it != record.values.end()) { return mValuePool->obtain(*(it->second)); } return StatusError(StatusCode::NOT_AVAILABLE) << "Record ID: " << recId.toString() << " is not found"; } void VehiclePropertyStore::registerProperty(const VehiclePropConfig& config, VehiclePropertyStore::TokenFunction tokenFunc) { std::scoped_lock g(mLock); mRecordsByPropId[config.prop] = Record{ .propConfig = config, .tokenFunction = tokenFunc, }; } VhalResult VehiclePropertyStore::writeValue(VehiclePropValuePool::RecyclableType propValue, bool updateStatus, VehiclePropertyStore::EventMode eventMode, bool useCurrentTimestamp) { bool valueUpdated = true; VehiclePropValue updatedValue; OnValueChangeCallback onValueChangeCallback = nullptr; OnValuesChangeCallback onValuesChangeCallback = nullptr; int32_t propId; int32_t areaId; { std::scoped_lock g(mLock); // Must set timestamp inside the lock to make sure no other writeValue will update the // the timestamp to a newer one while we are writing this value. if (useCurrentTimestamp) { propValue->timestamp = elapsedRealtimeNano(); } propId = propValue->prop; areaId = propValue->areaId; VehiclePropertyStore::Record* record = getRecordLocked(propId); if (record == nullptr) { return StatusError(StatusCode::INVALID_ARG) << "property: " << propId << " not registered"; } if (!isGlobalProp(propId) && getAreaConfig(*propValue, record->propConfig) == nullptr) { return StatusError(StatusCode::INVALID_ARG) << "no config for property: " << propId << " area ID: " << propValue->areaId; } VehiclePropertyStore::RecordId recId = getRecordIdLocked(*propValue, *record); if (auto it = record->values.find(recId); it != record->values.end()) { const VehiclePropValue* valueToUpdate = it->second.get(); int64_t oldTimestampNanos = valueToUpdate->timestamp; VehiclePropertyStatus oldStatus = valueToUpdate->status; // propValue is outdated and drops it. if (oldTimestampNanos > propValue->timestamp) { return StatusError(StatusCode::INVALID_ARG) << "outdated timestampNanos: " << propValue->timestamp; } if (!updateStatus) { propValue->status = oldStatus; } // areaId and propId must be the same between valueToUpdate and propValue. valueUpdated = (valueToUpdate->value != propValue->value || valueToUpdate->status != propValue->status); } else if (!updateStatus) { propValue->status = VehiclePropertyStatus::AVAILABLE; } record->values[recId] = std::move(propValue); if (eventMode == EventMode::NEVER) { return {}; } updatedValue = *(record->values[recId]); onValuesChangeCallback = mOnValuesChangeCallback; onValueChangeCallback = mOnValueChangeCallback; } if (onValuesChangeCallback == nullptr && onValueChangeCallback == nullptr) { // No callback registered. return {}; } // Invoke the callback outside the lock to prevent dead-lock. if (eventMode == EventMode::ALWAYS || valueUpdated) { if (onValuesChangeCallback != nullptr) { onValuesChangeCallback({updatedValue}); } else { onValueChangeCallback(updatedValue); } } return {}; } void VehiclePropertyStore::refreshTimestamp(int32_t propId, int32_t areaId, EventMode eventMode) { std::unordered_map eventModeByPropIdAreaId; PropIdAreaId propIdAreaId = { .propId = propId, .areaId = areaId, }; eventModeByPropIdAreaId[propIdAreaId] = eventMode; refreshTimestamps(eventModeByPropIdAreaId); } void VehiclePropertyStore::refreshTimestamps( std::unordered_map eventModeByPropIdAreaId) { std::vector updatedValues; OnValuesChangeCallback onValuesChangeCallback = nullptr; OnValueChangeCallback onValueChangeCallback = nullptr; { std::scoped_lock g(mLock); 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 (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); } } } void VehiclePropertyStore::removeValue(const VehiclePropValue& propValue) { std::scoped_lock 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::scoped_lock g(mLock); VehiclePropertyStore::Record* record = getRecordLocked(propId); if (record == nullptr) { return; } record->values.clear(); } std::vector VehiclePropertyStore::readAllValues() const { std::scoped_lock g(mLock); std::vector allValues; for (auto const& [_, record] : mRecordsByPropId) { for (auto const& [_, value] : record.values) { allValues.push_back(std::move(mValuePool->obtain(*value))); } } return allValues; } VehiclePropertyStore::ValuesResultType VehiclePropertyStore::readValuesForProperty( int32_t propId) const { std::scoped_lock g(mLock); std::vector values; const VehiclePropertyStore::Record* record = getRecordLocked(propId); if (record == nullptr) { return StatusError(StatusCode::INVALID_ARG) << "property: " << propId << " not registered"; } for (auto const& [_, value] : record->values) { values.push_back(std::move(mValuePool->obtain(*value))); } return values; } VehiclePropertyStore::ValueResultType VehiclePropertyStore::readValue( const VehiclePropValue& propValue) const { std::scoped_lock g(mLock); int32_t propId = propValue.prop; const VehiclePropertyStore::Record* record = getRecordLocked(propId); if (record == nullptr) { return StatusError(StatusCode::INVALID_ARG) << "property: " << propId << " not registered"; } VehiclePropertyStore::RecordId recId = getRecordIdLocked(propValue, *record); return readValueLocked(recId, *record); } VehiclePropertyStore::ValueResultType VehiclePropertyStore::readValue(int32_t propId, int32_t areaId, int64_t token) const { std::scoped_lock g(mLock); const VehiclePropertyStore::Record* record = getRecordLocked(propId); if (record == nullptr) { return StatusError(StatusCode::INVALID_ARG) << "property: " << propId << " not registered"; } VehiclePropertyStore::RecordId recId{.area = isGlobalProp(propId) ? 0 : areaId, .token = token}; return readValueLocked(recId, *record); } std::vector VehiclePropertyStore::getAllConfigs() const { std::scoped_lock g(mLock); std::vector configs; configs.reserve(mRecordsByPropId.size()); for (auto& [_, config] : mRecordsByPropId) { configs.push_back(config.propConfig); } return configs; } VhalResult VehiclePropertyStore::getConfig(int32_t propId) const { std::scoped_lock g(mLock); const VehiclePropertyStore::Record* record = getRecordLocked(propId); if (record == nullptr) { return StatusError(StatusCode::INVALID_ARG) << "property: " << propId << " not registered"; } return &record->propConfig; } VhalResult VehiclePropertyStore::getPropConfig(int32_t propId) const { std::scoped_lock g(mLock); const VehiclePropertyStore::Record* record = getRecordLocked(propId); if (record == nullptr) { return StatusError(StatusCode::INVALID_ARG) << "property: " << propId << " not registered"; } return record->propConfig; } void VehiclePropertyStore::setOnValueChangeCallback( const VehiclePropertyStore::OnValueChangeCallback& callback) { std::scoped_lock g(mLock); mOnValueChangeCallback = callback; } void VehiclePropertyStore::setOnValuesChangeCallback( const VehiclePropertyStore::OnValuesChangeCallback& callback) { std::scoped_lock g(mLock); mOnValuesChangeCallback = callback; } } // namespace vehicle } // namespace automotive } // namespace hardware } // namespace android