簡介
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<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模塊所實現的功能,均總結完畢,下一篇總結一個上層Car Service的實現。
寫的不是很好,各位同行如果有了解,可以加微信一起交流。