赞
踩
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
android.hardware.automotive.vehicle@2.0.so
功能: hal层具体实现
代码位置: /platform/hardware/interface/automotive/vehicle/2.0/default
编译产物: 可执行文件 android.hardware.automotive.vehicle@2.0-service
动态库 android.hardware.automotive.vehicle@2.0-manager-lib.so
静态库 android.hardware.automotive.vehicle@2.0-default-impl-lib.a
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文件将会被编译后将会生成名为android.hardware.automotive.vehicle@2.0-manager-lib的库文件,名称太长了,姑且称之为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",
],
以上几个文件将会被编译成一个名为
android.hardware.automotive.vehicle@2.0-default-impl-lib的库文件,名称也太长了,姑且称之为impl-lib右先锋。
大家都知道左右先锋都是归中军帐下,为元帅所有,所以这两个库跟VehicleService.cpp文件最终被一起编译成android.hardware.automotive.vehicle@2.0-service这个可执行文件,这里称它为service大帅。
众所周知,大帅一般要有虎符以及皇帝的命令才会调兵遣将,开始工作,android.hardware.automotive.vehicle@2.0-service.rc 这个玩意就类似皇帝密令,开机的时候,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<LinearFakeValueGenerator>( 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的实现。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。