AutoMotive是啥? 就是google看大家现在都用Android操作系统做中控车机,但是你们每家跟车子沟通的方式都不一样呀,所以它就提供了一个平台给你,把车子的属性定义,值更新,值设置等都分离出来了,上层应用开发的不管下面如何与汽车进行通讯,按照Car API开发就行;各个Tier1/车厂你也不要管我上层如何实现,按照底层标准接口,对接实现通讯就完事。
Android 9.0中的Vehicle模块实现相对8.0有了一定的变化,其实现的架构图如下所示,像HVAC这一类的,就是上层应用的具体实现,car-lib是一个java的分享库,是提供API提供给app调用设置以及查询汽车属性的,CarService是一个服务层,编译出来就是类似于SystemUI这种系统级服务应用,该服务内部了定义了诸多的子服务来与系统通信,与hal层的vehicle通信。hidl interface层主要是提供连两个接口定义文件,一个是IVehicle.hal,另外一个是IVehicleCallBack.hal; 前面一个是定义了Service层访问hal层的接口,后面一个顾名思义就是Hal层回调数据到Service层的回调接口。.hal文件是Android 8.0开始加入的hidl接口定义文件。关于hidl,后续在framework专栏里面再梳理一下。hal层的实现定义在hardware/interface/automotive里面,这里按照以前的话讲,叫做硬件抽象层。升级到8.0以后,原来以共享库存在,并提供jni的方式给到应用访问的方式叫做直通式访问,vhal给到应用的访问方式是通过hwservicemanager来进行的,这种方式叫做绑定式访问。各层编译出来的产物,以及路径定义如下:
功能: 上层API库
代码位置:/platform/packages/services/Car/car-lib
编译产物: android.car.jar
功能: 服务层
代码位置: /platform/packages/services/Car/service
编译产物: CarService.apk
功能: hidl通信接口
代码位置: /platform/hardware/interface/automotive/vehicle/2.0
Android.bp
IVehicleCallback.hal
IVehicle.hal
types.hal
编译产物: android.hardware.automotive.vehicle-V2.0-java.jar
[email protected]
功能: hal层具体实现
代码位置: /platform/hardware/interface/automotive/vehicle/2.0/default
编译产物: 可执行文件 [email protected]
动态库 [email protected]
静态库 [email protected]
Hal层功能:首先看一下整个hal层中的vehicle模块代码目录。
这是vhal层的实现,首先一步步来看, 分析Android.bp文件,我们就知道该模块下哪些文件对应的被编译成了哪些产物。Android.bp里面一共配置编译了三个产物,其中包括一个动态库,对应如下:
// Vehicle reference implementation lib
cc_library {
srcs: [
"common/src/Obd2SensorStore.cpp",
"common/src/SubscriptionManager.cpp",
"common/src/VehicleHalManager.cpp",
"common/src/VehicleObjectPool.cpp",
"common/src/VehiclePropertyStore.cpp",
"common/src/VehicleUtils.cpp",
"common/src/VmsUtils.cpp",
],
}
以上包含的cpp文件将会被编译后将会生成名为[email protected]的库文件,名称太长了,姑且称之为manager-lib左先锋;
接下来另外一个
// Vehicle default VehicleHAL implementation
srcs: [
"impl/vhal_v2_0/EmulatedVehicleHal.cpp",
"impl/vhal_v2_0/VehicleEmulator.cpp",
"impl/vhal_v2_0/PipeComm.cpp",
"impl/vhal_v2_0/SocketComm.cpp",
"impl/vhal_v2_0/LinearFakeValueGenerator.cpp",
"impl/vhal_v2_0/JsonFakeValueGenerator.cpp",
],
以上几个文件将会被编译成一个名为
[email protected]的库文件,名称也太长了,姑且称之为impl-lib右先锋。
大家都知道左右先锋都是归中军帐下,为元帅所有,所以这两个库跟VehicleService.cpp文件最终被一起编译成[email protected]这个可执行文件,这里称它为service大帅。
众所周知,大帅一般要有虎符以及皇帝的命令才会调兵遣将,开始工作,[email protected] 这个玩意就类似皇帝密令,开机的时候,init进程扫到这个文件之后,会调用其中的命令拉起service大帅运行。
这里还要介绍一下左右先锋的作用,manager-lib左先锋主要负责与空中系统的通讯,就是我们所说的上层,VehicleHalManager类正是集成了IVehicle接口,所以只有它有天线,基本能够实现上下通达;另外还有一个数据缓存的功能;另外一个类基本就是一些数据订阅管理,还有工具类,实现一些模块的基础。
impl-lib右先锋是一个虚拟车身属性模块的实现,从它的组成就知道,骨血里流的都是Emulator, EmulatedVehicleHal是VehicleHal类的子类,而VehicleHal这个类,正是Android定义给我们自行客制化实现hal层功能的interface. 所以这里,右先锋是一个模拟车身通讯实现。所以这里右先锋是不能上战场的,它是个泥菩萨,只能跟模拟器玩玩,我们后期要么改造它,要么仿制它。
介绍完了之后,按照启动过程,来看下它的运行流程。
首先,我们知道可执行模块的入口,那必定是main函数,我们大帅的main函数就在VehicleService.cpp这个文件中,这个文件的内容比较简单,可以看下
int main(int /* argc */, char* /* argv */ []) {
auto store = std::make_unique<VehiclePropertyStore>();
auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get());
auto emulator = std::make_unique<impl::VehicleEmulator>(hal.get());
auto service = std::make_unique<VehicleHalManager>(hal.get());
configureRpcThreadpool(4, true /* callerWillJoin */);
ALOGI("Registering as service...");
status_t status = service->registerAsService();
if (status != OK) {
ALOGE("Unable to register vehicle service (%d)", status);
return 1;
}
ALOGI("Ready");
joinRpcThreadpool();
return 1;
}
把代码翻译成汉语,流程如下:
1.初始化VehiclePropertyStore,得到store指针
2.初始化EmulatedVehicleHal,得到hal指针,初始化时,将VehiclePropertyStore指针作为参数传输了EmulatedVehicleHal构造方法中
3.初始化VehicleEmulator,得到emulator指针,初始化时,将EmulatedVehicleHal指针作为参数传入VehicleEmulator的构造方法中。
4.初始化VehicleHalManager,获得service智能指针。
5.之前介绍了VehicleHalManager继承自IVehicle hidl接口,该接口在编译的时候自动生成了registerAsService方法,该方法就是将服务本身通过binder注册到hwservicemanager里面供其他进程连接。这里不再深入介绍。
这里初始化模块总共只有那么几个东东,我们来一个个分析。首先是VehiclePropertyStore, 在看它之前,首先了解几个数据结构,
/**
* Encapsulates the property name and the associated value. It
* is used across various API calls to set values, get values or to register for
* events.
*/
struct VehiclePropValue {
/** Time is elapsed nanoseconds since boot */
int64_t timestamp;
/**
* Area type(s) for non-global property it must be one of the value from
* VehicleArea* enums or 0 for global properties.
*/
int32_t areaId;
/** Property identifier */
int32_t prop;
/** Status of the property */
VehiclePropertyStatus status;
/**
* Contains value for a single property. Depending on property data type of
* this property (VehiclePropetyType) one field of this structure must be filled in.
*/
struct RawValue {
/**
* This is used for properties of types VehiclePropertyType#INT
* and VehiclePropertyType#INT_VEC
*/
vec<int32_t> int32Values;
/**
* This is used for properties of types VehiclePropertyType#FLOAT
* and VehiclePropertyType#FLOAT_VEC
*/
vec<float> floatValues;
/** This is used for properties of type VehiclePropertyType#INT64 */
vec<int64_t> int64Values;
/** This is used for properties of type VehiclePropertyType#BYTES */
vec<uint8_t> bytes;
/** This is used for properties of type VehiclePropertyType#STRING */
string stringValue;
};
RawValue value;
};
VehiclePropValue 作为一个数据结构包含属性名称和关联的值。它
用于各种API调用,以设置值、获取值或注册事件。
struct VehiclePropConfig {
/** Property identifier */
int32_t prop;
/**
* Defines if the property is read or write or both.
*/
VehiclePropertyAccess access;
/**
* Defines the change mode of the property.
*/
VehiclePropertyChangeMode changeMode;
/**
* Contains per-area configuration.
*/
vec<VehicleAreaConfig> areaConfigs;
/** Contains additional configuration parameters */
vec<int32_t> configArray;
/**
* Some properties may require additional information passed over this
* string. Most properties do not need to set this.
*/
string configString;
/**
* Min sample rate in Hz.
* Must be defined for VehiclePropertyChangeMode::CONTINUOUS
*/
float minSampleRate;
/**
* Must be defined for VehiclePropertyChangeMode::CONTINUOUS
* Max sample rate in Hz.
*/
float maxSampleRate;
};
VehiclePropConfig 是车身属性值配置。以上两个数据结构均定义在type.hal文件中,type.hal文件是hidl定义数据结构的文件,在编译时会自动生成对应的数据结构,了解了这两个数据结构的内容之后,再看看下VehiclePropertyStore中怎么用的,首先也从数据接口开始切入,再VehiclePropertyStore.h文件中,定义了如下结构体
struct RecordConfig {
VehiclePropConfig propConfig;
TokenFunction tokenFunction;
};
struct RecordId {
int32_t prop;
int32_t area;
int64_t token;
bool operator==(const RecordId& other) const;
bool operator<(const RecordId& other) const;
}
RecordConfig可以理解为属性记录配置,RecordId可以理解为属性记录id.
定义了一个PropertyMap的map表来保存属性值。
using PropertyMap = std::map<RecordId, VehiclePropValue>;
PropertyMap mPropertyValues; // Sorted map of RecordId : VehiclePropValue.
定义了一个无序map来保存属性配置
std::unordered_map<int32_t /* VehicleProperty */, RecordConfig> mConfigs;
既然知道了保存的值是什么,保存在哪里了,接下来看看store的增删查改。
注册属性:
void VehiclePropertyStore::registerProperty(const VehiclePropConfig& config,
VehiclePropertyStore::TokenFunction tokenFunc) {
MuxGuard g(mLock);
//很简单,mConfigs键值对插入key为config.prop, 值为RecordConfig, RecordConfig是个结构体,成员就是VehiclePropConfig跟一个函数指针。
mConfigs.insert({ config.prop, RecordConfig { config, tokenFunc } });
}
写入属性值:
bool VehiclePropertyStore::writeValue(const VehiclePropValue& propValue,
bool updateStatus) {
MuxGuard g(mLock);
//首先从键值对的key集合里面查看是否当前需要写入属性值的属性id是否已经注册,如果当前属性id没有注册,则返回false,写入失败。
if (!mConfigs.count(propValue.prop)) return false;
//查找RecordId
RecordId recId = getRecordIdLocked(propValue);
//根据RecordId从map中获取Value值
VehiclePropValue* valueToUpdate = const_cast<VehiclePropValue*>(getValueOrNullLocked(recId));
//如果当前没有保存该属性,则加入一条新的记录,否则的话,更新对应的值
if (valueToUpdate == nullptr) {
mPropertyValues.insert({ recId, propValue });
} else {
valueToUpdate->timestamp = propValue.timestamp;
valueToUpdate->value = propValue.value;
if (updateStatus) {
valueToUpdate->status = propValue.status;
}
}
return true;
}
属性临时存储的增删改查基本都差不多,都是操作map, 这个不多说了,感兴趣的朋友可以自行查阅VehiclePropertyStore.cpp这个文件。
上面介绍了属性值临时保存方式,接下来看下EmulatedVehicleHal这个类,该类继承自EmulatedVehicleHal接口,而该接口又继承自VehicleHal,上文说过,VehicleHal接口是android定义在hal层用于实现VHal相关功能的接口。 因此EmulatedVehicleHal就里就实现了类似车身数据管理的功能。
首先看下其初始化的构造函数:
EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore)
: mPropStore(propStore),
mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)),
//mRecurrentTimer是一个工具类,内部维护一个线程,用来处理指定时间触发的事件,这个跟上层的Handler比较类似。
mRecurrentTimer(
std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this, std::placeholders::_1)),
//LinearFakeValueGenerator是一个模拟事件生成器,内部跟RecurrentTimer相互配合 mLinearFakeValueGenerator(std::make_unique(
std::bind(&EmulatedVehicleHal::onFakeValueGenerated, this, std::placeholders::_1))),
//JsonFakeValueGenerator跟LinearFakeValueGenerator类似,不过它是根据json配置产生假事件
mJsonFakeValueGenerator(std::make_unique<JsonFakeValueGenerator>(
std::bind(&EmulatedVehicleHal::onFakeValueGenerated, this, std::placeholders::_1))) {
//注册DefaultConfig.h中定义的属性值
initStaticConfig();
for (size_t i = 0; i < arraysize(kVehicleProperties); i++) {
mPropStore->registerProperty(kVehicleProperties[i].config);
}
}
构造函数完事之后,简单看下VHal接口中的set/get/subscribe是怎么实现的
VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::get(
const VehiclePropValue& requestedPropValue, StatusCode* outStatus) {
//当前我们要拿的属性值的属性ID是多少
auto propId = requestedPropValue.prop;
//这个pool是一个用于存储VehiclePropValue的对象池,这个跟Message的实现好像。
auto& pool = *getValuePool();
VehiclePropValuePtr v = nullptr;
//这个就是根据propId来获取值了,OBD2_FREEZE_FRAME是OBD检测到故障信
//息,OBD2_FREEZE_FRAME_INFO是故障检测到得时间戳。一般要获取OBD2_FREEZE_FRAME的数据之前,都要通过OBD2_FREEZE_FRAME_INFO获取时间戳。
//除了这两个属性值,其他的都直接从临时的Store里面获取当前属性的状态值。
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:
auto internalPropValue = mPropStore->readValueOrNull(requestedPropValue);
if (internalPropValue != nullptr) {
v = getValuePool()->obtain(*internalPropValue);
}
*outStatus = v != nullptr ? StatusCode::OK : StatusCode::INVALID_ARG;
break;
}
return v;
}
分析完了get函数,接下来就是set函数了,按照国际惯例,先贴代码,再分析,这个函数代码有点长,主要是各种判断,下面依次看下每个步骤都干啥了。
StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) {
//这个常量定义为false,是因为这个set函数是给上层调用的,Android层
//不能够改变属性值的状态,只有车身发送了该属性值过来了,才可改变
//属性状态,这个在下面会有体现。
static constexpr bool shouldUpdateStatus = false;
//这段代码用于测试的,生产一个假的数据请求事件。
if (propValue.prop == kGenerateFakeDataControllingProperty) {
StatusCode status = handleGenerateFakeDataRequest(propValue);
if (status != StatusCode::OK) {
return status;
}
} else if (mHvacPowerProps.count(propValue.prop)) {
//这里是判断当前属性值是否属于空调电源开关,如果是的情况下,去拿它值,如果当前开关没开,则返回当前状态不可用,设置失败的CODE
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;
case AP_POWER_STATE_REPORT:
// This property has different behavior between get/set. When it is set, the value
// goes to the vehicle but is NOT updated in the property store back to Android.
// Commented out for now, because it may mess up automated testing that use the
// emulator interface.
// getEmulatorOrDie()->doSetValueFromClient(propValue);
return StatusCode::OK;
}
}
//status默认值为AVAILABLE
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;
}
//读取该属性值id的当前存储的Prop
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;
}
//更新属性值,开头说过,shouldUpdateStatus为false, 也就是android层更新属性值,不改变属性状态
if (!mPropStore->writeValue(propValue, shouldUpdateStatus)) {
return StatusCode::INVALID_ARG;
}
//通知汽车,设置属性值,这里是告诉模拟器,该值需要重新设置,调用的这个函数等下再说。
getEmulatorOrDie()->doSetValueFromClient(propValue);
return StatusCode::OK;
}
通过set函数我们知道,属性值的设置,先将属性值写入到内存中保存,然后再通知车身更新该属性值,doSetValueFromClient这个函数就实现了相关的功能,这里暂且按下不表。这个set事件是来自上层service的调用,那车身信息如果发生变化时,如何set呢,答案是该模块还有一个名称setPropertyFromVehicle的函数,正是这个函数实现了车身数据变化之后,更新缓存的属性,并通知上层。
bool EmulatedVehicleHal::setPropertyFromVehicle(const VehiclePropValue& propValue) {
static constexpr bool shouldUpdateStatus = true;
if (propValue.prop == kGenerateFakeDataControllingProperty) {
StatusCode status = handleGenerateFakeDataRequest(propValue);
if (status != StatusCode::OK) {
return false;
}
}
//更新属性值,注意这个shouldUpdateStaus为true,也就是要更新属性的status,
//刚刚上面那个set函数该值为false,这是为啥? 因为属性值只有由车身改变的时候才能改变其状态值,android层不行。
if (mPropStore->writeValue(propValue, shouldUpdateStatus)) {
//触发回调,通知上层
doHalEvent(getValuePool()->obtain(propValue));
return true;
} else {
return false;
}
}
既然车身属性设置,跟上层设置都讲完了,下面看下该模块的属性值订阅实现,老规矩,贴代码:
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;
}
这个个代码比较简单,传输的参数有两个 property是属性ID,sampleRate是属性值更新的频率。 isContinuousProperty主要是判断该属性值的change类型是不是连续类型的,如果是连续类型的,就向RecurrentTimer中注册事件。RecurrentTimer是一个工具类,具体代码这里就不分析了,可以自行查阅,可以把它理解为一个另类的Handler, 其内部运行着一个线程维护着一个循环,当向其注册一个事件时,内部根据事件频率算出触发事件的事件,然后定期触发回调方法,跟Handler唯一不同的是,Handler的sendMesssageAtTime发完就没了,这个RecurrentTimer是如果你注册了事件,如果不取消注册,则事件会一直定期触发。
这里说了RecurrentTimer有个触发回调,那我们订阅了一个属性id,当达到时间后,触发的是哪个回调呢? 当然是EmulatedVehicleHal中的onContinuousPropertyTimer函数啦,这个函数指针在EmulatedVehicleHal初始化的时候,就作为参数传给RecurrentTimer,然后在这个函数中调用 doHalEvent(std::move(v)); 触发回调事件,将属性值上报。doHalEvent中其实没做啥,可以看下它的代码
void doHalEvent(VehiclePropValuePtr v) {
mOnHalEvent(std::move(v));
}
这里mOnHalEvent是一个函数指针,其对应函数定义在VehicleHalManager中,如下
void VehicleHalManager::onHalEvent(VehiclePropValuePtr v) {
mEventQueue.push(std::move(v));
}
最终由BatchingConsumer取出该事件,回调给上层;mOnHalEvent函数指针再VehicleHalManager初始化的时候,会将其作为参数传给EmulatedVehicleHal
写到这里, 基本上把hal层数据管理讲的差不多了;算一下,还剩下两个点,一个点是上层service如何跟hal层通信,一个是hal层如何跟车身通信。
Vhal如何跟VService进行数据交互?
上文介绍过,实现IVehicle接口的天线宝宝是谁? 就是VehicleHalManager这个宝宝,先看下IVehicle.hal通过hidl-gen生成的接口文件中定义了哪些方法, 大概摘录了其中主要的一些方法定义,如下:
getAllPropConfigs作用是获取所有的属性配置,对应实现在VehicleHalManager中的getlAllPropConfigs方法,代码如下:
Return<void> VehicleHalManager::getAllPropConfigs(getAllPropConfigs_cb _hidl_cb) {
ALOGI("getAllPropConfigs called");
//_hidl_cb是一个函数指针,定义在IVehicle.h里面
hidl_vec<VehiclePropConfig> hidlConfigs;
//从vector集合中读取所有当前的属性配置数据
auto& halConfig = mConfigIndex->getAllConfigs();
//写入到集合中
hidlConfigs.setToExternal(
const_cast<VehiclePropConfig *>(halConfig.data()),
halConfig.size());
//回调将数据发送到上层
_hidl_cb(hidlConfigs);
return Void();
}
上面这个函数是上层获取当前hal层支持的所有属性配置,接下来看根据属性id获取属性配置,对应的函数为getPropConfigs,代码如下:
Return<void> VehicleHalManager::getPropConfigs(const hidl_vec<int32_t> &properties,
getPropConfigs_cb _hidl_cb) {
//这个函数也比较简单,基本上就是判断当前属性id是否已经默认配置了,如果存在,则保存配置到集合中,否则的话,就针对未配置的属性id上报状态错误的消息。
std::vector<VehiclePropConfig> configs;
for (size_t i = 0; i < properties.size(); i++) {
auto prop = properties[i];
if (mConfigIndex->hasConfig(prop)) {
configs.push_back(mConfigIndex->getConfig(prop));
} else {
ALOGW("Requested config for undefined property: 0x%x", prop);
_hidl_cb(StatusCode::INVALID_ARG, hidl_vec<VehiclePropConfig>());
}
}
_hidl_cb(StatusCode::OK, configs);
return Void();
}
上面两个是关于属性配置获取的,属性配置跟属性值的区别是啥,一个是描述你这个配置的一些行为,如访问权限,更新最大的频率等;一个是用于具聚合一些具体值信息的载体,下面看一下属性值的设置跟获取,set/get函数,首先是set函数
//这个函数是上层调用set设置属性值的时候,参数value来自上层
Return<StatusCode> VehicleHalManager::set(const VehiclePropValue &value) {
auto prop = value.prop;
//获取属性配置
const auto* config = getPropConfigOrNull(prop);
//如果获取不到这个属性ID的配置,就证明当前hal层没配这个属性值,也就是不支持它
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;
}
//告诉上层订阅了该属性id的监听器,该值有更新。
handlePropertySetEvent(value);
//这里就到了上面介绍EmulatorVehicleHal中的set函数
auto status = mHal->set(value);
return Return<StatusCode>(status);
}
接下来是get函数,获取属性值
Return<void> VehicleHalManager::get(const VehiclePropValue& requestedPropValue, get_cb _hidl_cb) {
//逻辑跟set差不读,先获取属性配置,然后再检查权限,最后从缓存中取值。
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();
}
get函数跟set函数最终都是EmulatorVehicleHal.cpp中去执行具体的数据更新。基本上讲到这里,咱们VHal层的功能,除了具体跟车身通讯相关的部分没讲,其他的都说完了,对照下图,简单的总结一下。
VHal中的VehicleHalManager跟上层进行数据通讯,包括数据回调,跟上层调用,都均由此实现。
EmulatorVehicleHal模块承接了VehicleHalManager中的一部分属性值设置获取的功能,当上层设置值的时候,EmulatorVehicleHal会去更新内存中缓存的值,然后通知车身;当车身有数据更新时,也会该模块更新缓存值,并触发回调,通知上层。
VehicleEmulator这里不准备总结了,这个通讯实现比较简单,主要是运用Pipe管道或者socket通讯的方式,跟模拟器之间收发通过protobuf封装的数据,模块内部实现了protobuf数据的解析与封装,用来触发设置,获取属性值的事件等。
当然vhal不一定会跟can总线直接通讯,这里需要根据项目的硬件架构方案而定,各家实现不一。
至此,VHal模块所实现的功能,均总结完毕,下一篇总结一个上层Car Service的实现。