blob: b879850d64ddb2cbf8671a4e50239e8bf6a5fdd2 [file] [log] [blame]
/*
* 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 <utils/SystemClock.h>
#include "VehiclePropertyStore.h"
#include <VehicleHalTypes.h>
#include <VehicleUtils.h>
#include <android-base/stringprintf.h>
#include <math/HashCombine.h>
#include <inttypes.h>
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<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);
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<VehiclePropValuePool::RecyclableType> 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<std::mutex> g(mLock);
mRecordsByPropId[config.prop] = Record{
.propConfig = config,
.tokenFunction = tokenFunc,
};
}
VhalResult<void> VehiclePropertyStore::writeValue(VehiclePropValuePool::RecyclableType propValue,
bool updateStatus,
VehiclePropertyStore::EventMode eventMode,
bool useCurrentTimestamp) {
bool valueUpdated = true;
VehiclePropValue updatedValue;
OnValueChangeCallback onValueChangeCallback = nullptr;
{
std::scoped_lock<std::mutex> 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();
}
int32_t propId = propValue->prop;
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;
}
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 (eventMode == EventMode::NEVER) {
return {};
}
updatedValue = *(record->values[recId]);
if (mOnValueChangeCallback == nullptr) {
return {};
}
onValueChangeCallback = mOnValueChangeCallback;
}
// Invoke the callback outside the lock to prevent dead-lock.
if (eventMode == EventMode::ALWAYS || valueUpdated) {
onValueChangeCallback(updatedValue);
}
return {};
}
void VehiclePropertyStore::removeValue(const VehiclePropValue& propValue) {
std::scoped_lock<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::scoped_lock<std::mutex> g(mLock);
VehiclePropertyStore::Record* record = getRecordLocked(propId);
if (record == nullptr) {
return;
}
record->values.clear();
}
std::vector<VehiclePropValuePool::RecyclableType> VehiclePropertyStore::readAllValues() const {
std::scoped_lock<std::mutex> g(mLock);
std::vector<VehiclePropValuePool::RecyclableType> 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<std::mutex> g(mLock);
std::vector<VehiclePropValuePool::RecyclableType> 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<std::mutex> 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<std::mutex> 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<VehiclePropConfig> VehiclePropertyStore::getAllConfigs() const {
std::scoped_lock<std::mutex> g(mLock);
std::vector<VehiclePropConfig> configs;
configs.reserve(mRecordsByPropId.size());
for (auto& [_, config] : mRecordsByPropId) {
configs.push_back(config.propConfig);
}
return configs;
}
VhalResult<const VehiclePropConfig*> VehiclePropertyStore::getConfig(int32_t propId) const {
std::scoped_lock<std::mutex> 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<std::mutex> g(mLock);
mOnValueChangeCallback = callback;
}
} // namespace vehicle
} // namespace automotive
} // namespace hardware
} // namespace android