Android 9.0 AutoMotive模块之Vehicle

Android 9.0 AutoMotive模块之Vehicle

Vehicle模块

简介架构功能流程

简介

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();

auto hal = std::make_unique(store.get());

auto emulator = std::make_unique(hal.get());

auto service = std::make_unique(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 int32Values;

/**

* This is used for properties of types VehiclePropertyType#FLOAT

* and VehiclePropertyType#FLOAT_VEC

*/

vec floatValues;

/** This is used for properties of type VehiclePropertyType#INT64 */

vec int64Values;

/** This is used for properties of type VehiclePropertyType#BYTES */

vec 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 areaConfigs;

/** Contains additional configuration parameters */

vec 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;

PropertyMap mPropertyValues; // Sorted map of RecordId : VehiclePropValue.

定义了一个无序map来保存属性配置

std::unordered_map 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(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(

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 VehicleHalManager::getAllPropConfigs(getAllPropConfigs_cb _hidl_cb) {

ALOGI("getAllPropConfigs called");

//_hidl_cb是一个函数指针,定义在IVehicle.h里面

hidl_vec hidlConfigs;

//从vector集合中读取所有当前的属性配置数据

auto& halConfig = mConfigIndex->getAllConfigs();

//写入到集合中

hidlConfigs.setToExternal(

const_cast(halConfig.data()),

halConfig.size());

//回调将数据发送到上层

_hidl_cb(hidlConfigs);

return Void();

}

上面这个函数是上层获取当前hal层支持的所有属性配置,接下来看根据属性id获取属性配置,对应的函数为getPropConfigs,代码如下:

Return VehicleHalManager::getPropConfigs(const hidl_vec &properties,

getPropConfigs_cb _hidl_cb) {

//这个函数也比较简单,基本上就是判断当前属性id是否已经默认配置了,如果存在,则保存配置到集合中,否则的话,就针对未配置的属性id上报状态错误的消息。

std::vector 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());

}

}

_hidl_cb(StatusCode::OK, configs);

return Void();

}

上面两个是关于属性配置获取的,属性配置跟属性值的区别是啥,一个是描述你这个配置的一些行为,如访问权限,更新最大的频率等;一个是用于具聚合一些具体值信息的载体,下面看一下属性值的设置跟获取,set/get函数,首先是set函数

//这个函数是上层调用set设置属性值的时候,参数value来自上层

Return 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(status);

}

接下来是get函数,获取属性值

Return 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的实现。

清芳推荐

百达翡丽陀飞轮手表价格(了解百达翡丽陀飞轮手表的价格区间)
LOL鬼影森森婕拉为什么贵
28365365tw五大联赛

LOL鬼影森森婕拉为什么贵

📅 06-28 👀 1426
[求助]旋变极对数和PMSM极对数匹配问题
365bet最快线路检测中心

[求助]旋变极对数和PMSM极对数匹配问题

📅 07-08 👀 4203