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 supported.
return StatusCode::OK;
}
}
if (propValue.status != VehiclePropertyStatus::AVAILABLE) {
// Android side cannot set property status - this value is the
// purview of the HAL implementation to reflect the state of
// its underlying hardware
return StatusCode::INVALID_ARG;
}
//读取当前值
auto currentPropValue = mPropStore->readValueOrNull(propValue);
if (currentPropValue == nullptr) {
return StatusCode::INVALID_ARG;
}
if (currentPropValue->status != VehiclePropertyStatus::AVAILABLE) {
// do not allow Android side to set() a disabled/error property
return StatusCode::NOT_AVAILABLE;
}
/**
* After checking all conditions, such as the property is available, a real vhal will
* sent the events to Car ECU to take actions.
*/
// Send the value to the vehicle server, the server will talk to the (real or emulated) car
//设置属性到VehicleClient,通过这个设置到模拟车辆或者实际车辆
auto setValueStatus = mVehicleClient->setProperty(propValue, updateStatus);
if (setValueStatus != StatusCode::OK) {
return setValueStatus;
}
return StatusCode::OK;
}
其中mVehicleClient是初始化时传入的EmulatedVehicleConnector对象,所以调用的是EmulatedVehicleConnector的setProperty函数。
StatusCode setProperty(const VehiclePropValue& value, bool updateStatus) override {
return this->onSetProperty(value, updateStatus);
}
setProperty函数在EmulatedVehicleConnector的父类IPassThroughConnector中,然后调用onSetProperty函数,这个函数在EmulatedVehicleConnector类中。
StatusCode EmulatedVehicleConnector::onSetProperty(const VehiclePropValue& value,
bool updateStatus) {
if (mEmulatedUserHal.isSupported(value.prop)) {
LOG(INFO) << "onSetProperty(): property " << value.prop << " will be handled by UserHal";
const auto& ret = mEmulatedUserHal.onSetProperty(value);
if (!ret.ok()) {
LOG(ERROR) << "onSetProperty(): HAL returned error: " << ret.error().message();
return StatusCode(ret.error().code());
}
auto updatedValue = ret.value().get();
if (updatedValue != nullptr) {
LOG(INFO) << "onSetProperty(): updating property returned by HAL: "
<< toString(*updatedValue);
onPropertyValueFromCar(*updatedValue, updateStatus);
}
return StatusCode::OK;
}
return this->VehicleHalServer::onSetProperty(value, updateStatus);
}
首先处理UserHal相关的属性操作。然后调用VehicleHalServer中的onSetProperty函数。
StatusCode VehicleHalServer::onSetProperty(const VehiclePropValue& value, bool updateStatus) {
LOG(DEBUG) << "onSetProperty(" << value.prop << ")";
// Some properties need to be treated non-trivially
switch (value.prop) {
case kGenerateFakeDataControllingProperty:
return handleGenerateFakeDataRequest(value);
// set the value from vehicle side, used in end to end test.
case kSetIntPropertyFromVehicleForTest: {
auto updatedPropValue = createVehiclePropValue(VehiclePropertyType::INT32, 1);
updatedPropValue->prop = value.value.int32Values[0];
updatedPropValue->value.int32Values[0] = value.value.int32Values[1];
updatedPropValue->timestamp = value.value.int64Values[0];
updatedPropValue->areaId = value.areaId;
onPropertyValueFromCar(*updatedPropValue, updateStatus);
return StatusCode::OK;
}
case kSetFloatPropertyFromVehicleForTest: {
auto updatedPropValue = createVehiclePropValue(VehiclePropertyType::FLOAT, 1);
updatedPropValue->prop = value.value.int32Values[0];
updatedPropValue->value.floatValues[0] = value.value.floatValues[0];
updatedPropValue->timestamp = value.value.int64Values[0];
updatedPropValue->areaId = value.areaId;
onPropertyValueFromCar(*updatedPropValue, updateStatus);
return StatusCode::OK;
}
case kSetBooleanPropertyFromVehicleForTest: {
auto updatedPropValue = createVehiclePropValue(VehiclePropertyType::BOOLEAN, 1);
updatedPropValue->prop = value.value.int32Values[1];
updatedPropValue->value.int32Values[0] = value.value.int32Values[0];
updatedPropValue->timestamp = value.value.int64Values[0];
updatedPropValue->areaId = value.areaId;
onPropertyValueFromCar(*updatedPropValue, updateStatus);
return StatusCode::OK;
}
case AP_POWER_STATE_REPORT:
switch (value.value.int32Values[0]) {
case toInt(VehicleApPowerStateReport::DEEP_SLEEP_EXIT):
case toInt(VehicleApPowerStateReport::SHUTDOWN_CANCELLED):
case toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL):
// CPMS is in WAIT_FOR_VHAL state, simply move to ON
// Send back to HAL
// ALWAYS update status for generated property value
onPropertyValueFromCar(*createApPowerStateReq(VehicleApPowerStateReq::ON, 0),
true /* updateStatus */);
break;
case toInt(VehicleApPowerStateReport::DEEP_SLEEP_ENTRY):
case toInt(VehicleApPowerStateReport::SHUTDOWN_START):
// CPMS is in WAIT_FOR_FINISH state, send the FINISHED command
// Send back to HAL
// ALWAYS update status for generated property value
onPropertyValueFromCar(
*createApPowerStateReq(VehicleApPowerStateReq::FINISHED, 0),
true /* updateStatus */);
break;
case toInt(VehicleApPowerStateReport::ON):
case toInt(VehicleApPowerStateReport::SHUTDOWN_POSTPONE):
case toInt(VehicleApPowerStateReport::SHUTDOWN_PREPARE):
// Do nothing
break;
default:
// Unknown state
break;
}
break;
default:
break;
}
// In the real vhal, the value will be sent to Car ECU.
// We just pretend it is done here and send back to HAL
auto updatedPropValue = getValuePool()->obtain(value);
updatedPropValue->timestamp = elapsedRealtimeNano();
onPropertyValueFromCar(*updatedPropValue, updateStatus);
return StatusCode::OK;
}
上面是模拟属性设置流程,这里就相当于模拟属性设置完成了。更新属性的值和时间戳之后,调用onPropertyValueFromCar模拟属性设置成功的上报操作
3.2 设置属性流程图
plantuml
@startuml
participant CarService
box
participant VehicleHalManager
participant SubscriptionManager
participant EmulatedVehicleHal
participant IPassThroughConnector
participant EmulatedVehicleConnector
participant VehicleHalServer
endbox
CarService -> VehicleHalManager: set(const VehiclePropValue& value)
VehicleHalManager -> VehicleHalManager: handlePropertySetEvent(const VehiclePropValue& value)
VehicleHalManager -> SubscriptionManager: getSubscribedClients(int32_t propId, SubscribeFlags flags)
SubscriptionManager -> VehicleHalManager: value
alt flags == SubscribeFlags::EVENTS_FROM_ANDROID
VehicleHalManager -> CarService: onPropertySet(value)
end
VehicleHalManager -> EmulatedVehicleHal: set(const VehiclePropValue& value)
EmulatedVehicleHal -> IPassThroughConnector: setProperty(const VehiclePropValue& \n\tvalue, bool updateStatus)
IPassThroughConnector -> EmulatedVehicleConnector: onSetProperty(const VehiclePropValue& \n\tvalue, bool updateStatus)
EmulatedVehicleConnector -> VehicleHalServer: onSetProperty(const VehiclePropValue& value, bool updateStatus)
@enduml
流程图:
这里由于没有实际车辆,没有往下设置,OEM厂商需要设置到ECU中。至于设置之后,VHAL缓存的改变则是由设置成功的通知上报之后才会写入缓存的。
4 订阅属性流程
4.1 普通订阅属性流程源码分析
订阅入口也是在VehicleHalManager中
Return<StatusCode> VehicleHalManager::subscribe(const sp<IVehicleCallback> &callback,
const hidl_vec<SubscribeOptions> &options) {
hidl_vec<SubscribeOptions> verifiedOptions(options);
for (size_t i = 0; i < verifiedOptions.size(); i++) {
SubscribeOptions& ops = verifiedOptions[i];
auto prop = ops.propId;
const auto* config = getPropConfigOrNull(prop);
if (config == nullptr) {
ALOGE("Failed to subscribe: config not found, property: 0x%x",
prop);
return StatusCode::INVALID_ARG;
}
if (ops.flags == SubscribeFlags::UNDEFINED) {
ALOGE("Failed to subscribe: undefined flag in options provided");
return StatusCode::INVALID_ARG;
}
if (!isSubscribable(*config, ops.flags)) {
ALOGE("Failed to subscribe: property 0x%x is not subscribable",
prop);
return StatusCode::INVALID_ARG;
}
ops.sampleRate = checkSampleRate(*config, ops.sampleRate);
}
std::list<SubscribeOptions> updatedOptions;
auto res = mSubscriptionManager.addOrUpdateSubscription(getClientId(callback),
callback, verifiedOptions,
&updatedOptions);
if (StatusCode::OK != res) {
ALOGW("%s failed to subscribe, error code: %d", __func__, res);
return res;
}
for (auto opt : updatedOptions) {
mHal->subscribe(opt.propId, opt.sampleRate);
}
return StatusCode::OK;
}
首先判断属性配置是否存在,存在才支持订阅
然后判断是否可以订阅
bool VehicleHalManager::isSubscribable(const VehiclePropConfig& config,
SubscribeFlags flags) {
bool isReadable = config.access & VehiclePropertyAccess::READ;
if (!isReadable && (SubscribeFlags::EVENTS_FROM_CAR & flags)) {
ALOGW("Cannot subscribe, property 0x%x is not readable", config.prop);
return false;
}
if (config.changeMode == VehiclePropertyChangeMode::STATIC) {
ALOGW("Cannot subscribe, property 0x%x is static", config.prop);
return false;
}
return true;
}
判断访问权限,判断flag,如果flag是从car来的,则返回false。然后是判断属性的changeMode,如果changeMode是STATIC,表示属性不变,则不支持订阅。
然后添加订阅addOrUpdateSubscription,添加之前首先创建clientId
using ClientId = uint64_t;
ClientId VehicleHalManager::getClientId(const sp<IVehicleCallback>& callback) {
//TODO(b/32172906): rework this to get some kind of unique id for callback interface when this
// feature is ready in HIDL.
if (callback->isRemote()) {
BpHwVehicleCallback* hwCallback = static_cast<BpHwVehicleCallback*>(callback.get());
return static_cast<ClientId>(reinterpret_cast<intptr_t>(hwCallback->onAsBinder()));
} else {
return static_cast<ClientId>(reinterpret_cast<intptr_t>(callback.get()));
}
}
根据传入的回调函数的额指针来强转成ClientId这种int类型,作为客户端的唯一标识。
StatusCode SubscriptionManager::addOrUpdateSubscription(
ClientId clientId,
const sp<IVehicleCallback> &callback,
const hidl_vec<SubscribeOptions> &optionList,
std::list<SubscribeOptions>* outUpdatedSubscriptions) {
outUpdatedSubscriptions->clear();
MuxGuard g(mLock);
ALOGI("SubscriptionManager::addOrUpdateSubscription, callback: %p", callback.get());
const sp<HalClient>& client = getOrCreateHalClientLocked(clientId, callback);
if (client.get() == nullptr) {
return StatusCode::INTERNAL_ERROR;
}
for (size_t i = 0; i < optionList.size(); i++) {
const SubscribeOptions& opts = optionList[i];
ALOGI("SubscriptionManager::addOrUpdateSubscription, prop: 0x%x", opts.propId);
client->addOrUpdateSubscription(opts);
addClientToPropMapLocked(opts.propId, client);
if (SubscribeFlags::EVENTS_FROM_CAR & opts.flags) {
SubscribeOptions updated;
if (updateHalEventSubscriptionLocked(opts, &updated)) {
outUpdatedSubscriptions->push_back(updated);
}
}
}
return StatusCode::OK;
}
首先,创建客户端对象HalClient
std::map<ClientId, sp<HalClient>> mClients;
sp<HalClient> SubscriptionManager::getOrCreateHalClientLocked(
ClientId clientId, const sp<IVehicleCallback>& callback) {
auto it = mClients.find(clientId);
if (it == mClients.end()) {
uint64_t cookie = reinterpret_cast<uint64_t>(clientId);
ALOGI("Creating new client and linking to death recipient, cookie: 0x%" PRIx64, cookie);
auto res = callback->linkToDeath(mCallbackDeathRecipient, cookie);
if (!res.isOk()) { // Client is already dead?
ALOGW("%s failed to link to death, client %p, err: %s",
__func__, callback.get(), res.description().c_str());
return nullptr;
}
sp<HalClient> client = new HalClient(callback);
mClients.insert({clientId, client});
return client;
} else {
return it->second;
}
}
mClients是保存订阅客户端的map,key是ClientId,value是HalClient对象。这里会先判断mClients这个map中是否存在对应的客户端,如果没有,则创建HalClient对象,并加入到这个map之中,如果有则直接返回。
然后for循环遍历所有的订阅项,由于订阅的时候,CarService传入的是SubscribeOptions的列表:
struct SubscribeOptions {
/** Property to subscribe */
int32_t propId;
/**
* Sample rate in Hz.
*
* Must be provided for properties with
* VehiclePropertyChangeMode::CONTINUOUS. The value must be within
* VehiclePropConfig#minSamplingRate .. VehiclePropConfig#maxSamplingRate
* for a given property.
* This value indicates how many updates per second client wants to receive.
*/
float sampleRate;
/** Flags that indicate to which event sources to listen. */
SubscribeFlags flags;
};
所以可以一次性订阅多个属性。但是这多个属性是一次订阅,也是走一个回调函数上去的。这里会遍历所有的SubscribeOptions,然后调用addOrUpdateSubscription函数。
std::map<int32_t, SubscribeOptions> mSubscriptions;
void HalClient::addOrUpdateSubscription(const SubscribeOptions &opts) {
ALOGI("%s opts.propId: 0x%x", __func__, opts.propId);
auto it = mSubscriptions.find(opts.propId);
if (it == mSubscriptions.end()) {
mSubscriptions.emplace(opts.propId, opts);
} else {
const SubscribeOptions& oldOpts = it->second;
SubscribeOptions updatedOptions;
if (mergeSubscribeOptions(oldOpts, opts, &updatedOptions)) {
mSubscriptions.erase(it);
mSubscriptions.emplace(opts.propId, updatedOptions);
}
}
}
bool mergeSubscribeOptions(const SubscribeOptions &oldOpts,
const SubscribeOptions &newOpts,
SubscribeOptions *outResult) {
float updatedRate = std::max(oldOpts.sampleRate, newOpts.sampleRate);
SubscribeFlags updatedFlags = SubscribeFlags(oldOpts.flags | newOpts.flags);
bool updated = (updatedRate > oldOpts.sampleRate) || (updatedFlags != oldOpts.flags);
if (updated) {
*outResult = oldOpts;
outResult->sampleRate = updatedRate;
outResult->flags = updatedFlags;
}
return updated;
}
首先判断这个SubscribeOptions中包含的propId是否已经存在于mSubscriptions这个map中,如果存在,则更新flag和采样率和相应的SubscribeOptions对象,如果不存在,则添加。
这个map的key是propId,value是SubscribeOptions。
std::map<int32_t, sp<HalClientVector>> mPropToClients;
void SubscriptionManager::addClientToPropMapLocked(
int32_t propId, const sp<HalClient> &client) {
auto it = mPropToClients.find(propId);
sp<HalClientVector> propClients;
if (it == mPropToClients.end()) {
propClients = new HalClientVector();
mPropToClients.insert(std::make_pair(propId, propClients));
} else {
propClients = it->second;
}
propClients->addOrUpdate(client);
}
mPropToClients是SubscriptionManager类中的一个map,key是propId,value是HalClientVector对象。HalClientVectot中存储的是对于同一个propId订阅的不同HalClient对象。
这里,先判断mPropToClients中是否有对应的propId的HalClientVector,如果没有,则表示这个propId没有客户端订阅过。如果有则将当前的client对象添加到HalClientVector中。
总结一下属性订阅做的事就是:
- 根据传入的callback指针,转换成ClientId,作为客户端标识
- 创建HalClient对象,作为客户端实例,并保存客户端的callback回调
- 缓存所有订阅属性到HalClient对象的mSubscriptions中
- 缓存HalClient到mClients这个map中
- 缓存HalClient到mPropToClients这个map中
4.2 连续类型属性订阅流程源码分析
连续类型属性,订阅之后需要VHAL周期性上报给CarService
连续属性订阅,其他和上面的普通订阅相同,最后会走到EmulatedVehicleHal中进行处理
StatusCode EmulatedVehicleHal::subscribe(int32_t property, float sampleRate) {
ALOGI("%s propId: 0x%x, sampleRate: %f", __func__, property, sampleRate);
if (isContinuousProperty(property)) {
mRecurrentTimer.registerRecurrentEvent(hertzToNanoseconds(sampleRate), property);
}
return StatusCode::OK;
}
bool EmulatedVehicleHal::isContinuousProperty(int32_t propId) const {
const VehiclePropConfig* config = mPropStore->getConfigOrNull(propId);
if (config == nullptr) {
ALOGW("Config not found for property: 0x%x", propId);
return false;
}
return config->changeMode == VehiclePropertyChangeMode::CONTINUOUS;
}
主要就是通过属性配置的changeMode是否是CONTINUOUS来判断,判断如果还连续属性,调用registerRecurrentEvent
std::unordered_map<int32_t, RecurrentEvent> mCookieToEventsMap;
void registerRecurrentEvent(std::chrono::nanoseconds interval, int32_t cookie) {
TimePoint now = Clock::now();
// Align event time point among all intervals. Thus if we have two intervals 1ms and 2ms,
// during every second wake-up both intervals will be triggered.
TimePoint absoluteTime = now - Nanos(now.time_since_epoch().count() % interval.count());
{
std::lock_guard<std::mutex> g(mLock);
mCookieToEventsMap[cookie] = { interval, cookie, absoluteTime };
}
mCond.notify_one();
}
用propId作为key,将定时事件的定时周期,propId,当前转换后时间封装成RecurrentEvent对象,存储到mCookieToEventsMap这个map中,然后通过mCond唤醒线程,至此连续订阅完成,等待特定时间上报。
4.3 订阅流程图
plantuml
@startuml
participant CarService
box
participant VehicleHalManager
participant SubscriptionManager
participant HalClient
participant HalClientVector
participant EmulatedVehicleHal
participant RecurrentTimer
endbox
CarService -> VehicleHalManager: subscribe(const sp<IVehicleCallback> &callback, \n\tconst hidl_vec<SubscribeOptions> &options)
VehicleHalManager -> SubscriptionManager: addOrUpdateSubscription(\n\tClientId clientId, \n\tconst sp<IVehicleCallback> &callback, \n\tconst hidl_vec<SubscribeOptions> &optionList, \n\tstd::list<SubscribeOptions>* outUpdatedSubscriptions)
SubscriptionManager -> SubscriptionManager: getClientId(const sp<IVehicleCallback>& callback)
SubscriptionManager -> SubscriptionManager: getOrCreateHalClientLocked(ClientId clientId, \n\tconst sp<IVehicleCallback>& callback)
alt not in mClients
SubscriptionManager -> HalClient: new HalClient(const sp<IVehicleCallback> &callback)
HalClient -> SubscriptionManager: client
SubscriptionManager -> SubscriptionManager: mClients.insert({clientId, client})
end
loop i in optionList.size
SubscriptionManager -> HalClient: addOrUpdateSubscription(const SubscribeOptions &opts)
alt not in mSubscriptions
HalClient -> HalClient: emplace(opts.propId, opts)
else in mSubscriptions
HalClient -> HalClient: mergeSubscribeOptions(oldOpts, opts, &updatedOptions)
HalClient -> HalClient: erase(it)
HalClient -> HalClient: emplace(opts.propId, opts)
end
SubscriptionManager -> SubscriptionManager: addClientToPropMapLocked(opts.propId, client)
alt not in mPropToClients
SubscriptionManager -> HalClientVector: new HalClientVector()
HalClientVector -> SubscriptionManager: propClients
SubscriptionManager -> SubscriptionManager: mPropToClients.insert(std::make_pair(propId, propClients));
end
SubscriptionManager -> SubscriptionManager: propClients->addOrUpdate(client)
end
loop opt in updatedOptions
SubscriptionManager -> EmulatedVehicleHal: subscribe(opt.propId, opt.sampleRate)
alt isContinuousProperty
EmulatedVehicleHal -> RecurrentTimer: registerRecurrentEvent(hertzToNanoseconds(sampleRate), property)
RecurrentTimer -> RecurrentTimer: mCookieToEventsMap[cookie] = \n\t{ interval, cookie, absoluteTime }
end
end
@enduml
流程图如下:
5 取消订阅流程
5.1 取消订阅流程源码分析
取消订阅入口也是在VehicleHalManager中
Return<StatusCode> VehicleHalManager::unsubscribe(const sp<IVehicleCallback>& callback,
int32_t propId) {
mSubscriptionManager.unsubscribe(getClientId(callback), propId);
return StatusCode::OK;
}
根据callback获取ClientId,并转到SubscriptionManager中处理
void SubscriptionManager::unsubscribe(ClientId clientId,
int32_t propId) {
MuxGuard g(mLock);
auto propertyClients = getClientsForPropertyLocked(propId);
auto clientIter = mClients.find(clientId);
if (clientIter == mClients.end()) {
ALOGW("Unable to unsubscribe: no callback found, propId: 0x%x", propId);
} else {
auto client = clientIter->second;
if (propertyClients != nullptr) {
propertyClients->remove(client);
if (propertyClients->isEmpty()) {
mPropToClients.erase(propId);
}
}
bool isClientSubscribedToOtherProps = false;
for (const auto& propClient : mPropToClients) {
if (propClient.second->indexOf(client) >= 0) {
isClientSubscribedToOtherProps = true;
break;
}
}
if (!isClientSubscribedToOtherProps) {
auto res = client->getCallback()->unlinkToDeath(mCallbackDeathRecipient);
if (!res.isOk()) {
ALOGW("%s failed to unlink to death, client: %p, err: %s",
__func__, client->getCallback().get(), res.description().c_str());
}
mClients.erase(clientIter);
}
}
if (propertyClients == nullptr || propertyClients->isEmpty()) {
mHalEventSubscribeOptions.erase(propId);
mOnPropertyUnsubscribed(propId);
}
}
sp<HalClientVector> SubscriptionManager::getClientsForPropertyLocked(
int32_t propId) const {
auto it = mPropToClients.find(propId);
return it == mPropToClients.end() ? nullptr : it->second;
}
首先,调用getClientsForPropertyLocked函数,根据propId获取HalClientVector对象。然后从mClients中根据ClientId找到对应的HalClient对象,如果该HalClient存在,则从HalClientVector中移除,如果HalClientVector中是最后一个HalClient,则从mPropToClients中移除这个propId的HalClientVector对象。
如果这个client还订阅了其他propId,则不把这个client从mClients中移除,如果没有订阅其他的propId,则从mClients中移除。
同时,如果这个HalClientVector为空了,则移除mHalEventSubscribeOptions中的对应propId的订阅属性,并调用mOnPropertyUnsubscribed这个回调函数。
void VehicleHalManager::onAllClientsUnsubscribed(int32_t propertyId) {
mHal->unsubscribe(propertyId);
}
StatusCode EmulatedVehicleHal::unsubscribe(int32_t property) {
ALOGI("%s propId: 0x%x", __func__, property);
if (isContinuousProperty(property)) {
mRecurrentTimer.unregisterRecurrentEvent(property);
}
return StatusCode::OK;
}
void unregisterRecurrentEvent(int32_t cookie) {
{
std::lock_guard<std::mutex> g(mLock);
mCookieToEventsMap.erase(cookie);
}
mCond.notify_one();
}
这个地方就是对连续属性的取消订阅处理,从mCookieToEventsMap中移除。
5.2 取消订阅流程图
plantuml
@startuml
participant CarService
box
participant VehicleHalManager
participant SubscriptionManager
participant EmulatedVehicleHal
participant RecurrentTimer
endbox
CarService -> VehicleHalManager: unsubscribe(const sp<IVehicleCallback>& callback,\n\t int32_t propId)
VehicleHalManager -> SubscriptionManager: unsubscribe(getClientId(callback), propId)
SubscriptionManager -> SubscriptionManager: propertyClients = getClientsForPropertyLocked(propId)
alt client in mClients
SubscriptionManager -> SubscriptionManager: propertyClients->remove(client)
alt propertyClients->isEmpty()
SubscriptionManager -> SubscriptionManager: mPropToClients.erase(propId)
end
loop propClient in mPropToClients
alt client in propClient
SubscriptionManager -> SubscriptionManager: isClientSubscribedToOtherProps = true
end
end
alt isClientSubscribedToOtherProps == false
SubscriptionManager -> SubscriptionManager: client->getCallback()->\n\tunlinkToDeath(mCallbackDeathRecipient)
end
end
alt propertyClients == nullptr || propertyClients->isEmpty()
SubscriptionManager -> VehicleHalManager: onAllClientsUnsubscribed(propertyId)
VehicleHalManager -> EmulatedVehicleHal: unsubscribe(property)
alt isContinuousProperty
EmulatedVehicleHal -> RecurrentTimer: unregisterRecurrentEvent(property)
RecurrentTimer -> RecurrentTimer: mCookieToEventsMap.erase(cookie)
end
end
@enduml
流程图:
6 属性上报流程
6.1 属性上报流程源码分析
void onPropertyValueFromCar(const VehiclePropValue& value, bool updateStatus) override {
return this->onPropertyValue(value, updateStatus);
}
由于EmulatedVehicleConnector同时继承VehicleHalServer和VehicleHalClient,所以这个onPropertyValue继承于VehicleHalClient,调用的是VehicleHalClient中的onPropertyValue函数。
void VehicleHalClient::onPropertyValue(const VehiclePropValue& value, bool updateStatus) {
if (!mPropCallback) {
LOG(ERROR) << __func__ << ": PropertyCallBackType is not registered!";
return;
}
return mPropCallback(value, updateStatus);
}
调用mPropCallback这个注册的回调函数,这个回调函数是:
void EmulatedVehicleHal::onPropertyValue(const VehiclePropValue& value, bool updateStatus) {
VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(value);
if (mPropStore->writeValue(*updatedPropValue, updateStatus)) {
getEmulatorOrDie()->doSetValueFromClient(*updatedPropValue);
doHalEvent(std::move(updatedPropValue));
}
}
首先,将上报的属性写入缓存中。然后调用doSetValueFromClient
void VehicleEmulator::doSetValueFromClient(const VehiclePropValue& propValue) {
vhal_proto::EmulatorMessage msg;
vhal_proto::VehiclePropValue* val = msg.add_value();
populateProtoVehiclePropValue(val, &propValue);
msg.set_status(vhal_proto::RESULT_OK);
msg.set_msg_type(vhal_proto::SET_PROPERTY_ASYNC);
mSocketComm->sendMessage(msg);
if (mPipeComm) {
mPipeComm->sendMessage(msg);
}
}
这里会通过SocketComm和PipeComm通知其他客户端。
然后会调用doHalEvent上报给CarService。
void doHalEvent(VehiclePropValuePtr v) {
mOnHalEvent(std::move(v));
}
mOnHalEvent是VehicleHalManager::onHalEvent函数
void VehicleHalManager::onHalEvent(VehiclePropValuePtr v) {
mEventQueue.push(std::move(v));
}
将VehiclePropValuePtr传入mEventQueue,这个是初始化的时候与BatchingConsumer中的mQueue绑定的一个queue,用于处理上报事件。同时BatchingConsumer这个类初始化的时候也会创建一个线程来处理上报事件,运行的函数为:
void runInternal(const OnBatchReceivedFunc& onBatchReceived) {
if (mState.exchange(State::RUNNING) == State::INIT) {
while (State::RUNNING == mState) {
mQueue->waitForItems();
if (State::STOP_REQUESTED == mState) break;
std::this_thread::sleep_for(mBatchInterval);
if (State::STOP_REQUESTED == mState) break;
std::vector<T> items = mQueue->flush();
if (items.size() > 0) {
onBatchReceived(items);
}
}
}
mState = State::STOPPED;
}
void waitForItems() {
std::unique_lock<std::mutex> g(mLock);
while (mQueue.empty() && mIsActive) {
mCond.wait(g);
}
}
std::vector<T> flush() {
std::vector<T> items;
MuxGuard g(mLock);
if (mQueue.empty() || !mIsActive) {
return items;
}
while (!mQueue.empty()) {
items.push_back(std::move(mQueue.front()));
mQueue.pop();
}
return items;
}
void push(T&& item) {
{
MuxGuard g(mLock);
if (!mIsActive) {
return;
}
mQueue.push(std::move(item));
}
mCond.notify_one();
}
当没有上报事件时,会waitForItems阻塞线程。如果有事件,则会在循环中处理,等待一个时间间隔mBatchInterval=10,如果在这个事件内没有上报事件,会等10s,如果有,则会在push的时候立马唤醒线程执行。
然后将queue中的所有事件都取出,调用回调函数处理,回调函数是:
void VehicleHalManager::onBatchHalEvent(const std::vector<VehiclePropValuePtr>& values) {
const auto& clientValues =
mSubscriptionManager.distributeValuesToClients(values, SubscribeFlags::EVENTS_FROM_CAR);
for (const HalClientValues& cv : clientValues) {
auto vecSize = cv.values.size();
hidl_vec<VehiclePropValue> vec;
if (vecSize < kMaxHidlVecOfVehiclPropValuePoolSize) {
vec.setToExternal(&mHidlVecOfVehiclePropValuePool[0], vecSize);
} else {
vec.resize(vecSize);
}
int i = 0;
for (VehiclePropValue* pValue : cv.values) {
shallowCopy(&(vec)[i++], *pValue);
}
auto status = cv.client->getCallback()->onPropertyEvent(vec);
if (!status.isOk()) {
ALOGE("Failed to notify client %s, err: %s",
toString(cv.client->getCallback()).c_str(),
status.description().c_str());
}
}
}
先看下封装
std::list<HalClientValues> SubscriptionManager::distributeValuesToClients(
const std::vector<recyclable_ptr<VehiclePropValue>>& propValues,
SubscribeFlags flags) const {
//创建一个map,key是HalClient,value是VehiclePropValue
std::map<sp<HalClient>, std::list<VehiclePropValue*>> clientValuesMap;
{
MuxGuard g(mLock);
//遍历所有的propValue
for (const auto& propValue: propValues) {
VehiclePropValue* v = propValue.get();
//获取所有订阅了该属性的客户端HalClient对象
auto clients = getSubscribedClientsLocked(v->prop, flags);
//遍历所有HalClient,并将其和propValue一起封装加入clientValuesMap中
for (const auto& client : clients) {
clientValuesMap[client].push_back(v);
}
}
}
//遍历map中所有的对象,并封装成HalClientValues对象,存入clientValues中
std::list<HalClientValues> clientValues;
for (const auto& entry : clientValuesMap) {
clientValues.push_back(HalClientValues {
.client = entry.first,
.values = entry.second
});
}
return clientValues;
}
std::list<sp<HalClient>> SubscriptionManager::getSubscribedClientsLocked(
int32_t propId, SubscribeFlags flags) const {
std::list<sp<HalClient>> subscribedClients;
//通过propId获取该id对应的HalClientVector对象
sp<HalClientVector> propClients = getClientsForPropertyLocked(propId);
if (propClients.get() != nullptr) {
//遍历HalClientVector中HalClient,并返回
for (size_t i = 0; i < propClients->size(); i++) {
const auto& client = propClients->itemAt(i);
if (client->isSubscribed(propId, flags)) {
subscribedClients.push_back(client);
}
}
}
return subscribedClients;
}
sp<HalClientVector> SubscriptionManager::getClientsForPropertyLocked(
int32_t propId) const {
auto it = mPropToClients.find(propId);
return it == mPropToClients.end() ? nullptr : it->second;
}
对客户端和属性值进行封装,然后遍历封装后的数组,逐个调用其回调函数onPropertyEvent,这个就是客户端传入的回调函数。
6.2 连续属性上报流程源码分析
连续属性订阅之后,会根据传入的采样率转换后的频率进行上报,订阅时,将订阅事件保存在mCookieToEventsMap这个map中,并唤醒了处理线程。
这个线程是什么时候初始化,在执行什么呢?这个可以参考https://editor.csdn.net/md/?articleId=140752076
void loop(const Action& action) {
static constexpr auto kInvalidTime = TimePoint(Nanos::max());
std::vector<int32_t> cookies;
while (!mStopRequested) {
auto now = Clock::now();
auto nextEventTime = kInvalidTime;
cookies.clear();
{
std::unique_lock<std::mutex> g(mLock);
for (auto&& it : mCookieToEventsMap) {
RecurrentEvent& event = it.second;
if (event.absoluteTime <= now) {
event.updateNextEventTime(now);
cookies.push_back(event.cookie);
}
if (nextEventTime > event.absoluteTime) {
nextEventTime = event.absoluteTime;
}
}
}
if (cookies.size() != 0) {
action(cookies);
}
std::unique_lock<std::mutex> g(mLock);
mCond.wait_until(g, nextEventTime); // nextEventTime can be nanoseconds::max()
}
}
唤醒这个线程,并在这个里面从mCookieToEventsMap中取出RecurrentEvent,判断时间戳,如果已经到时了,则执行里面的action回调函数。
void EmulatedVehicleHal::onContinuousPropertyTimer(const std::vector<int32_t>& properties) {
VehiclePropValuePtr v;
auto& pool = *getValuePool();
for (int32_t property : properties) {
if (isContinuousProperty(property)) {
auto internalPropValue = mPropStore->readValueOrNull(property);
if (internalPropValue != nullptr) {
v = pool.obtain(*internalPropValue);
}
} else {
ALOGE("Unexpected onContinuousPropertyTimer for property: 0x%x", property);
}
if (v.get()) {
v->timestamp = elapsedRealtimeNano();
doHalEvent(std::move(v));
}
}
}
如果是连续属性,则从VehiclePropertyStore读取缓存,并调用doHalEvent往上报,这个和6.1节中的上报流程相同。
6.3 属性上报流程图
plantuml
@startuml
participant CarService
box
participant EmulatedVehicleHal
participant VehicleHalManager
participant SubscriptionManager
participant HalClient
participant IPassThroughConnector
participant VehicleHalClient
participant VehiclePropertyStore
participant VehicleEmulator
participant SocketComm
participant ConcurrentQueue
participant BatchingConsumer
endbox
EmulatedVehicleHal -> IPassThroughConnector: onPropertyValueFromCar(*updatedPropValue, updateStatus)
IPassThroughConnector -> VehicleHalClient: onPropertyValue(value, updateStatus)
VehicleHalClient -> EmulatedVehicleHal: onPropertyValue(value, updateStatus)
EmulatedVehicleHal -> VehiclePropertyStore: writeValue(*updatedPropValue, updateStatus)
alt writeValue success
EmulatedVehicleHal -> VehicleEmulator: doSetValueFromClient(*updatedPropValue)
VehicleEmulator -> SocketComm: sendMessage(msg)
EmulatedVehicleHal -> EmulatedVehicleHal: doHalEvent(std::move(updatedPropValue))
EmulatedVehicleHal -> VehicleHalManager::onHalEvent(updatedPropValue)
VehicleHalManager -> VehicleHalManager: mEventQueue.push(std::move(updatedPropValue))
VehicleHalManager -> ConcurrentQueue: mQueue.push(updatedPropValue)
ConcurrentQueue -> ConcurrentQueue: mCond.notify_one()
ConcurrentQueue -> BatchingConsumer: runInternal(const OnBatchReceivedFunc& onBatchReceived)
BatchingConsumer -> VehicleHalManager: onBatchHalEvent(const std::vector<VehiclePropValuePtr>& values)
VehicleHalManager -> SubscriptionManager: distributeValuesToClients(values, \n\tSubscribeFlags::EVENTS_FROM_CAR)
SubscriptionManager -> VehicleHalManager: clientValues
loop cv in clientValues
VehicleHalManager -> HalClient: getCallback()
HalClient -> VehicleHalManager: callback
VehicleHalManager -> CarService: callback->onPropertyEvent(values)
end
end
@enduml
流程图
连续属性上报流程图省略,大致和这个上面差不多,只是触发在定时器中。