1 概述
本篇主要讲解VehicleHal的主要运行流程,包括设置属性、获取属性、订阅属性、取消订阅、持续上报属性订阅等。
2 获取属性流程
2.1 获取属性流程源码分析
作为服务注册到hwServiceManager中的类是VehicleHalManager,所以,CarService对服务端的调用的hidl接口都是调用到了VehicleHalManager中。
get(VehiclePropValue requestedPropValue)
generates (StatusCode status, VehiclePropValue propValue);
IVehicle这个hidl接口中的定义如上
Return<void> VehicleHalManager::get(const VehiclePropValue& requestedPropValue, get_cb _hidl_cb) {
const auto* config = getPropConfigOrNull(requestedPropValue.prop);
if (config == nullptr) {
ALOGE("Failed to get value: config not found, property: 0x%x",
requestedPropValue.prop);
_hidl_cb(StatusCode::INVALID_ARG, kEmptyValue);
return Void();
}
if (!checkReadPermission(*config)) {
_hidl_cb(StatusCode::ACCESS_DENIED, kEmptyValue);
return Void();
}
StatusCode status;
auto value = mHal->get(requestedPropValue, &status);
_hidl_cb(status, value.get() ? *value : kEmptyValue);
return Void();
}
调用之后会调用到VehicleHalManager中的get函数
const VehiclePropConfig* VehicleHalManager::getPropConfigOrNull(
int32_t prop) const {
return mConfigIndex->hasConfig(prop)
? &mConfigIndex->getConfig(prop) : nullptr;
}
首先,会从属性配置列表中是否存在这个属性,这个是初始化的时候缓存的,缓存的是所有的属性配置。
如果获取的属性不在属性配置列表中,则不能够获取,如果存在,会判断访问权限,访问权限校验通过之后,会调用mHal的get函数。
VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::get(
const VehiclePropValue& requestedPropValue, StatusCode* outStatus) {
auto propId = requestedPropValue.prop;
ALOGV("get(%d)", propId);
auto& pool = *getValuePool();
VehiclePropValuePtr v = nullptr;
switch (propId) {
case OBD2_FREEZE_FRAME:
v = pool.obtainComplex();
*outStatus = fillObd2FreezeFrame(requestedPropValue, v.get());
break;
case OBD2_FREEZE_FRAME_INFO:
v = pool.obtainComplex();
*outStatus = fillObd2DtcInfo(v.get());
break;
default:
if (mEmulatedUserHal != nullptr && mEmulatedUserHal->isSupported(propId)) {
ALOGI("get(): getting value for prop %d from User HAL", propId);
const auto& ret = mEmulatedUserHal->onGetProperty(requestedPropValue);
if (!ret.ok()) {
ALOGE("get(): User HAL returned error: %s", ret.error().message().c_str());
*outStatus = StatusCode(ret.error().code());
} else {
auto value = ret.value().get();
if (value != nullptr) {
ALOGI("get(): User HAL returned value: %s", toString(*value).c_str());
v = getValuePool()->obtain(*value);
*outStatus = StatusCode::OK;
} else {
ALOGE("get(): User HAL returned null value");
*outStatus = StatusCode::INTERNAL_ERROR;
}
}
break;
}
auto internalPropValue = mPropStore->readValueOrNull(requestedPropValue);
if (internalPropValue != nullptr) {
v = getValuePool()->obtain(*internalPropValue);
}
*outStatus = v != nullptr ? StatusCode::OK : StatusCode::INVALID_ARG;
break;
}
if (v.get()) {
v->timestamp = elapsedRealtimeNano();
}
return v;
}
首先,会对userhal的一些判断操作,这个EmulatedUserHal是跟用户身份相关的hal定义,用于处理用户切换相关事件。如果是用户hal相关的prop获取,则获取完成之后就直接跳出。如果不是,则走普通的property获取路径,从VehiclePropertyStore中读取。
using PropertyMap = std::map<RecordId, VehiclePropValue>;
PropertyMap mPropertyValues;
std::unique_ptr<VehiclePropValue> VehiclePropertyStore::readValueOrNull(
int32_t prop, int32_t area, int64_t token) const {
RecordId recId = {
prop, isGlobalProp(prop) ? 0 : area, token };
MuxGuard g(mLock);
const VehiclePropValue* internalValue = getValueOrNullLocked(recId);
return internalValue ? std::make_unique<VehiclePropValue>(*internalValue) : nullptr;
}
const VehiclePropValue* VehiclePropertyStore::getValueOrNullLocked(
const VehiclePropertyStore::RecordId& recId) const {
auto it = mPropertyValues.find(recId);
return it == mPropertyValues.end() ? nullptr : &it->second;
}
从mPropertyValues这个map中去获取对应propId的property。mPropertyValues里面的值是在哪填充的呢?
bool VehiclePropertyStore::writeValue(const VehiclePropValue& propValue,
bool updateStatus) {
MuxGuard g(mLock);
if (!mConfigs.count(propValue.prop)) return false;
RecordId recId = getRecordIdLocked(propValue);
VehiclePropValue* valueToUpdate = const_cast<VehiclePropValue*>(getValueOrNullLocked(recId));
if (valueToUpdate == nullptr) {
mPropertyValues.insert({
recId, propValue });
return true;
}
// propValue is outdated and drops it.
if (valueToUpdate->timestamp > propValue.timestamp) {
return false;
}
// update the propertyValue.
// The timestamp in propertyStore should only be updated by the server side. It indicates
// the time when the event is generated by the server.
valueToUpdate->timestamp = propValue.timestamp;
valueToUpdate->value = propValue.value;
if (updateStatus) {
valueToUpdate->status = propValue.status;
}
return true;
}
是在这个函数中,这个函数是在VHAL初始化的时候调用的,初始化的时候,会遍历一个定义了所有支持属性的列表,并调用writeValue函数将属性配置和属性值缓存到VehiclePropertyStore中。
以上就是CarService从VHAL获取属性的流程,总结来说就是:从VHAL的缓存map中获取属性。
2.2 获取属性流程图
plantuml
@startuml
participant CarService
box
participant VehicleHalManager
participant EmulatedVehicleHal
participant VehiclePropertyStore
endbox
CarService -> VehicleHalManager: get(const VehiclePropValue& \n\trequestedPropValue, get_cb _hidl_cb)
VehicleHalManager -> EmulatedVehicleHal: get(const VehiclePropValue& \n\trequestedPropValue, get_cb _hidl_cb)
EmulatedVehicleHal -> VehiclePropertyStore: readValueOrNull(int32_t prop, \n\tint32_t area, int64_t token)
VehiclePropertyStore -> EmulatedVehicleHal: propValue
EmulatedVehicleHal -> VehicleHalManager: propValue
VehicleHalManager -> CarService: _hidl_cb(propValue)
@enduml
3 设置属性流程
3.1 设置属性流程源码分析
hidl调用后还是从VehicleHalManager开始的
Return<StatusCode> VehicleHalManager::set(const VehiclePropValue &value) {
auto prop = value.prop;
const auto* config = getPropConfigOrNull(prop);
if (config == nullptr) {
ALOGE("Failed to set value: config not found, property: 0x%x", prop);
return StatusCode::INVALID_ARG;
}
if (!checkWritePermission(*config)) {
return StatusCode::ACCESS_DENIED;
}
handlePropertySetEvent(value);
auto status = mHal->set(value);
return Return<StatusCode>(status);
}
首先判断缓存中是否有该属性的属性配置,有才支持后续的set操作
handlePropertySetEvent是对带有EVENTS_FROM_ANDROID这个订阅标签属性的处理,这种属性的设置需要直接上报给上层。
然后是调用EmulatedVehicleHal的set函数
StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) {
constexpr bool updateStatus = false;
//这里是模拟车辆属性
if (propValue.prop == kGenerateFakeDataControllingProperty) {
// Send the generator controlling request to the server.
// 'updateStatus' flag is only for the value sent by setProperty (propValue in this case)
// instead of the generated values triggered by it. 'propValue' works as a control signal
// here, since we never send the control signal back, the value of 'updateStatus' flag
// does not matter here.
auto status = mVehicleClient->setProperty(propValue, updateStatus);
return status;
//处理空调相关的属性
} else if (mHvacPowerProps.count(propValue.prop)) {
auto hvacPowerOn = mPropStore->readValueOrNull(
toInt(VehicleProperty::HVAC_POWER_ON),
(VehicleAreaSeat::ROW_1_LEFT | VehicleAreaSeat::ROW_1_RIGHT |
VehicleAreaSeat::ROW_2_LEFT | VehicleAreaSeat::ROW_2_CENTER |
VehicleAreaSeat::ROW_2_RIGHT));
if (hvacPowerOn && hvacPowerOn->value.int32Values.size() == 1
&& hvacPowerOn->value.int32Values[0] == 0) {
return StatusCode::NOT_AVAILABLE;
}
} else {
// Handle property specific code
switch (propValue.prop) {
case OBD2_FREEZE_FRAME_CLEAR:
return clearObd2FreezeFrames(propValue);
case VEHICLE_MAP_SERVICE:
// Placeholder for future implementation of VMS property in the default hal. For
// now, just returns OK; otherwise, hal clients crash with property not s