Android 9.0 AutotoMotive模塊之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
[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的實現。

寫的不是很好,各位同行如果有了解,可以加微信一起交流。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章