1. 程式人生 > >Android O 前期預研之三:Android Vehicle HAL

Android O 前期預研之三:Android Vehicle HAL

Android Automotive

Android Automotive 是Android Oreo中的一個新的特色功能,從AOSP的程式碼上來看,Android O中已經包含有了從Application到Framework 到HAL的整體框架,這一章節,我們簡單的過以下Android Vehicle 的框架,以及重點看下 Vehicle HAL的東西。總體結構大約是以下這個樣子:
這裡寫圖片描述

上圖的結構應該是Android Oreo當中比較通用的框架結構了,從Application 到Framework到HAL,跟之前的Android版本相比,之前Framework要不通過binder聯絡上一個Daemon,這個Daemon再去load 相關的HAL,要不就是這些Framework的service直接通過JNI去load 這些個HAL 庫。而現在的Android Oreo則是Framework 與HAL之間直接採用HIDL來做聯絡溝通了。接下來我們從下往上的來把Android Vehicle的框架捋一下吧。首先來分析下Vehicle HAL,通過這個來複習並且實踐下之前研究學習過的Android HIDL.

Android Vehicle HAL

這裡寫圖片描述

types.hal 定義的是一些資料結構,IVehicle.hal定義的是從Framework往HAL呼叫的介面,而IVehicleCallback.hal則是HAL往Framework 上報回撥的介面。看起來還是挺清晰的吧。
而IVehicle.hal的介面也不是很多,

package [email protected]2.0;

import IVehicleCallback;

interface IVehicle {
  /**
   * Returns a list of all property configurations supported by
this vehicle * HAL. */ getAllPropConfigs() generates (vec<VehiclePropConfig> propConfigs); /** * Returns a list of property configurations for given properties. * * If requested VehicleProperty wasn't found it must return * StatusCode::INVALID_ARG, otherwise a list of vehicle property
* configurations with StatusCode::OK */ getPropConfigs(vec<int32_t> props) generates (StatusCode status, vec<VehiclePropConfig> propConfigs); /** * Get a vehicle property value. * * For VehiclePropertyChangeMode::STATIC properties, this method must always * return the same value always. * For VehiclePropertyChangeMode::ON_CHANGE properties, it must return the * latest available value. * * Some properties like AUDIO_VOLUME requires to pass additional data in * GET request in VehiclePropValue object. * * If there is no data available yet, which can happen during initial stage, * this call must return immediately with an error code of * StatusCode::TRY_AGAIN. */ get(VehiclePropValue requestedPropValue) generates (StatusCode status, VehiclePropValue propValue); /** * Set a vehicle property value. * * Timestamp of data must be ignored for set operation. * * Setting some properties require having initial state available. If initial * data is not available yet this call must return StatusCode::TRY_AGAIN. * For a property with separate power control this call must return * StatusCode::NOT_AVAILABLE error if property is not powered on. */ set(VehiclePropValue propValue) generates (StatusCode status); /** * Subscribes to property events. * * Clients must be able to subscribe to multiple properties at a time * depending on data provided in options argument. * * @param listener This client must be called on appropriate event. * @param options List of options to subscribe. SubscribeOption contains * information such as property Id, area Id, sample rate, etc. */ subscribe(IVehicleCallback callback, vec<SubscribeOptions> options) generates (StatusCode status); /** * Unsubscribes from property events. * * If this client wasn't subscribed to the given property, this method * must return StatusCode::INVALID_ARG. */ unsubscribe(IVehicleCallback callback, int32_t propId) generates (StatusCode status); /** * Print out debugging state for the vehicle hal. * * The text must be in ASCII encoding only. * * Performance requirements: * * The HAL must return from this call in less than 10ms. This call must avoid * deadlocks, as it may be called at any point of operation. Any synchronization * primitives used (such as mutex locks or semaphores) must be acquired * with a timeout. * */ debugDump() generates (string s); };

而IVehicle.hal則就更少了:

package [email protected]2.0;

interface IVehicleCallback {

    /**
     * Event callback happens whenever a variable that the API user has
     * subscribed to needs to be reported. This may be based purely on
     * threshold and frequency (a regular subscription, see subscribe call's
     * arguments) or when the IVehicle#set method was called and the actual
     * change needs to be reported.
     *
     * These callbacks are chunked.
     *
     * @param values that has been updated.
     */
    oneway onPropertyEvent(vec<VehiclePropValue> propValues);

    /**
     * This method gets called if the client was subscribed to a property using
     * SubscribeFlags::SET_CALL flag and IVehicle#set(...) method was called.
     *
     * These events must be delivered to subscriber immediately without any
     * batching.
     *
     * @param value Value that was set by a client.
     */
    oneway onPropertySet(VehiclePropValue propValue);

    /**
     * Set property value is usually asynchronous operation. Thus even if
     * client received StatusCode::OK from the IVehicle::set(...) this
     * doesn't guarantee that the value was successfully propagated to the
     * vehicle network. If such rare event occurs this method must be called.
     *
     * @param errorCode - any value from StatusCode enum.
     * @param property - a property where error has happened.
     * @param areaId - bitmask that specifies in which areas the problem has
     *                 occurred, must be 0 for global properties
     */
    oneway onPropertySetError(StatusCode errorCode,
                              int32_t propId,
                              int32_t areaId);
};

比較好奇的是這麼些介面就能實現Android 車機的這麼些功能?先還是這麼看看吧,後續仔細研究研究。

Android Vehicle HAL 的編譯

按照我們之前的研究來看看這麼些Android Vehicle HAL被怎麼編譯,編譯成什麼東西。先看.hal檔案編譯生成的標頭檔案:
這裡寫圖片描述

.hal 檔案生成的CPP檔案:
這裡寫圖片描述

.h 檔案我們之前也都做過分析,Bp/Bn/Bs 代表啥意思相信都沒忘記吧,在這裡就不多贅述了,而VehicleAll.cpp/VehicleCallbackAll.cpp 裡其實就是那些生成的.h檔案中所定義的C++ Class 檔案的實現,這些相關實現都是寫在這些.cpp檔案當中。

這些.cpp檔案/.h檔案最終會生成一個名叫[email protected]的庫,詳情請參考hardware/interfaces/automotive/vehicle/2.0/Android.bp。

另外參考hardware/interfaces/automotive/vehicle/2.0/default/Android.mk,
hardware/interfaces/automotive/vehicle/2.0/default/impl/目錄下會被編譯出名叫[email protected]的靜態庫,而hardware/interfaces/automotive/vehicle/2.0/default/common/目錄會被編譯出一個名叫[email protected]的靜態庫,而這兩個靜態庫都會去連結上面生成的[email protected]動態庫。

而最終會被編譯成一個可執行程式:

include $(CLEAR_VARS)
LOCAL_MODULE := $(vhal_v2_0)-service
LOCAL_INIT_RC := $(vhal_v2_0)-service.rc
LOCAL_PROPRIETARY_MODULE := true
LOCAL_MODULE_RELATIVE_PATH := hw

LOCAL_SRC_FILES := \
    VehicleService.cpp

LOCAL_SHARED_LIBRARIES := \
    libbase \
    libhidlbase \
    libhidltransport \
    liblog \
    libprotobuf-cpp-lite \
    libutils \
    $(vhal_v2_0) \

LOCAL_STATIC_LIBRARIES := \
    $(vhal_v2_0)-manager-lib \
    $(vhal_v2_0)-default-impl-lib \
    $(vhal_v2_0)-libproto-native \

LOCAL_CFLAGS += -Wall -Wextra -Werror

include $(BUILD_EXECUTABLE)

而這個可執行程式會被Init 系統在開機的時候啟動,成為一個Daemon:
service vehicle-hal-2.0 /vendor/bin/hw/[email protected]
class hal
user vehicle_network
group system inet

Android Vehicle HAL 的使用

東西都編譯出來後,我們看看Vehicle HAL怎麼來使用。

1) Android Vehicle HAL Service 端使用:
我們來看下hardware/interfaces/automotive/vehicle/2.0/default/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...");
    service->registerAsService();

    ALOGI("Ready");
    joinRpcThreadpool();
}

說實話,剛看到這段程式碼的我是一臉懵逼的,auto是個什麼鬼, std::make_unique是個什麼鬼,這幾年弄的都是JAVA/Android,像這些C++的新規範,新東西越來越層出不窮了,真的是一天不學習就得落伍了哈。

幸虧這個也很簡單, std::make_unique就先等同與new吧,auto的意思就是變數型別在宣告的時候先不確定,等變數被定義的時候根據實際情況來確定變數型別。

其中最關鍵的是這兩句:

auto service = std::make_unique<VehicleHalManager>(hal.get());
ALOGI("Registering as service...");
service->registerAsService();

我們看下 VehicleHalManager的定義:

  class VehicleHalManager : public IVehicle 

VehicleHalManager類是由 IVehicle類派生出來,而IVehicle則是從我們上面介紹的.hal檔案裡編譯出來的。

我們繼續來看下下面這句:

   service->registerAsService();

我們從.hal檔案編譯出來的.cpp檔案來看下,這個中間生成的cpp檔案所在目錄為:out/soong/.intermediates/hardware/interfaces/automotive/vehicle/2.0/[email protected]_genc++/gen/android/hardware/automotive/vehicle/2.0/VehicleAll.cpp

該函式的具體實現為:

::android::status_t IVehicle::registerAsService(const std::string &serviceName) {
    ::android::hardware::details::onRegistration("[email protected]", "IVehicle", serviceName);

    const ::android::sp<::android::hidl::manager::V1_0::IServiceManager> sm
            = ::android::hardware::defaultServiceManager();
    // 獲取到預設的service manager,非常類似於 Binder機制中。有時間的話可以深入研究下。

    if (sm == nullptr) {
        return ::android::INVALID_OPERATION;
    }
    ::android::hardware::Return<bool> ret = sm->add(serviceName.c_str(), this);
    //往service manager 中新增service, 跟Binder機制也是一樣一樣的。

    return ret.isOk() && ret ? ::android::OK : ::android::UNKNOWN_ERROR;
}

但是比較奇怪的是add 進去是this 指標,也就是應該是 IVehicle物件,也就是VehicleHalManager的物件。不應該是Bnxxxx的物件麼????我這裡有一臉懵逼。這是第一個懵點,第二個懵點則來的更加不可思議:

const char* IVehicle::descriptor("andro[email protected]::IVehicle");

__attribute__((constructor))static void static_constructor() {
    ::android::hardware::details::gBnConstructorMap.set(IVehicle::descriptor,
            [](void *iIntf) -> ::android::sp<::android::hardware::IBinder> {
                return new BnHwVehicle(static_cast<IVehicle *>(iIntf));
            });
    ::android::hardware::details::gBsConstructorMap.set(IVehicle::descriptor,
            [](void *iIntf) -> ::android::sp<::android::hidl::base::V1_0::IBase> {
                return new BsVehicle(static_cast<IVehicle *>(iIntf));
            });
};

我們來看看這段程式碼,首先attribute((constructor))也是gcc 的一個機制,大致意思是在main函式被呼叫之前 static_constructor函式會被呼叫。


::android::hardware::details::gBnConstructorMap.set(IVehicle::descriptor,
[](void *iIntf) -> ::android::sp<::android::hardware::IBinder> {
return new BnHwVehicle(static_cast<IVehicle *>(iIntf));
});

這段程式碼意思是指在 gBnConstructorMap中插入一個key/value,這個value 值是

[](void *iIntf) -> ::android::sp<::android::hardware::IBinder> {
                return new BnHwVehicle(static_cast<IVehicle *>(iIntf));
            }

這又是什麼鬼啊???搜了下應該是拉曼達表示式,但是懵點是 void *iIntf 這個是引數,這個引數是怎麼傳遞進來的?我都搜了半天都沒找到引數怎麼傳遞進來的。但是看了下BnHwVehicle這個類的定義,明顯是使用了 IVehicle類。

BnHwVehicle::BnHwVehicle(const ::android::sp<IVehicle> &_hidl_impl)
        : ::android::hidl::base::V1_0::BnHwBase(_hidl_impl, "[email protected]", "IVehicle") { 
            _hidl_mImpl = _hidl_impl;
            auto prio = ::android::hardware::details::gServicePrioMap.get(_hidl_impl, {SCHED_NORMAL, 0});
            mSchedPolicy = prio.sched_policy;
            mSchedPriority = prio.prio;
}


::android::status_t BnHwVehicle::onTransact(
        uint32_t _hidl_code,
        const ::android::hardware::Parcel &_hidl_data,
        ::android::hardware::Parcel *_hidl_reply,
        uint32_t _hidl_flags,
        TransactCallback _hidl_cb) {
    ::android::status_t _hidl_err = ::android::OK;

    switch (_hidl_code) {
        case 1 /* getAllPropConfigs */:
        {
            if (!_hidl_data.enforceInterface(IVehicle::descriptor)) {
                _hidl_err = ::android::BAD_TYPE;
                break;
            }
           …………………
           ………………...
            bool _hidl_callbackCalled = false;

            _hidl_mImpl->getAllPropConfigs([&](const auto &_hidl_out_propConfigs) {
            …………………….
            …………………...

            break;
        }

從BnHwVehicle這個類來看,它確實承擔的是HIDL Service的工作,從BnHwVehicle裡會來呼叫IVehicle類,IVehicle類會被繼承成為真正的實現操作類。這個懵點2,到目前還沒有找到怎麼解決點。

到這裡,我們來簡單總結service端:
1) 從Ixxxx類中會派生出新的類,這個類會作為真正的操作類。
2) 從Ixxxx類的派生類中呼叫 registerAsService 去把自己註冊進hw service manager中。
3) 目前看起來,如果有client 發出請求的話,BnXXXXX會相應,BnXXXXX會呼叫自己的Ixxxx的實現函式來完成某一個操作請求。

1) Android Vehicle HAL Client 端使用:
這個Client端存在Android Framework的Car Service當中
在onCreate 函式當中:

“`
@Override
public void onCreate() {
Log.i(CarLog.TAG_SERVICE, “Service onCreate”);
mCanBusErrorNotifier = new CanBusErrorNotifier(this /* context */);
mVehicle = getVehicle(null /* Any Vehicle HAL interface name */);

    if (mVehicle == null) {
        throw new IllegalStateException("Vehicle HAL service is not available.");
    }
    try {
        mVehicleInterfaceName = mVehicle.interfaceDescriptor();
    } catch (RemoteException e) {
        throw new IllegalStateException("Unable to get Vehicle HAL interface descriptor", e);
    }

    Log.i(CarLog.TAG_SERVICE, "Connected to " + mVehicleInterfaceName);

    mICarImpl = new ICarImpl(this, mVehicle, SystemInterface.getDefault(this),
            mCanBusErrorNotifier);
    mICarImpl.init();
    SystemProperties.set("boot.car_service_created", "1");

    linkToDeath(mVehicle, mVehicleDeathRecipient);

    super.onCreate();
}

mVehicle = getVehicle(null /* Any Vehicle HAL interface name */);

獲取到Vehicle HAL.

@Nullable
private static IVehicle getVehicle(@Nullable String interfaceName) {
    try {
        boolean anyVersion = interfaceName == null || interfaceName.isEmpty();
        IVehicle vehicle = null;
        if (ENABLE_VEHICLE_HAL_V2_1 && (anyVersion || IVHAL_21.equals(interfaceName))) {
            vehicle = android.hardware.automotive.vehicle.V2_1.IVehicle
                    .getService();
        }

        if (vehicle == null && (anyVersion || IVHAL_20.equals(interfaceName))) {
            vehicle = android.hardware.automotive.vehicle.V2_0.IVehicle
                    .getService();
        }
        return vehicle;
    } catch (RemoteException e) {
        Log.e(CarLog.TAG_SERVICE, "Failed to get IVehicle service", e);
    } catch (NoSuchElementException e) {
        Log.e(CarLog.TAG_SERVICE, "IVehicle service not registered yet");
    }
    return null;
}

理論上拿到IVehicle vehicle 物件後,就應該能夠來做對Vehicle HAL做相關呼叫了。這一部分就先到這裡完結吧。

相關推薦

Android O 前期Android Vehicle HAL

Android Automotive Android Automotive 是Android Oreo中的一個新的特色功能,從AOSP的程式碼上來看,Android O中已經包含有了從Application到Framework 到HAL的整體框架,這一章節,我

android中的對話方塊自定義對話方塊

首先看下效果圖 下面講一下具體的實現: 1.修改系統預設的Dialog樣式(風格、主題) 2.自定義Dialog佈局檔案 3.可以自己封裝一個類,繼承自Dialog或者直接使用Dialog類來實現,為了方便以後重複使用,建議自己封裝一個Dialog類 ==

Android電源管理PowerManager.WakeLock原始碼詳讀

PowerManager.WakeLock 有加鎖和解鎖兩種狀態,加鎖的方式有兩種,一種是永久的鎖,這樣的鎖除非顯式的放開,是不會解鎖的,所以這種鎖用起來要非常的小心。第二種鎖是超時鎖,這種鎖會在鎖住後一段時間自動解鎖。         在建立了PowerManager.W

Android問題集錦轉載Javah 常見錯誤記錄-NDK與JNI除錯

測試檔案:hello-jni/src/com/example/hellojni/HelloJni.java/** * 該檔案來自 Android NDK Sample - HelloJni, 為了便於說明問題,我作了一些修改。 */ package com.example

Android系統資訊獲取 IMSI號和IMEI解釋

IMSI號: IMSI是國際移動使用者識別碼的簡稱(International Mobile Subscriber Identity)  它是在公眾陸地行動電話網(PLMN)中用於唯一識別移動使用者的一個號碼。在GSM網路,這個號碼通常被存放在SIM卡中 IMSI共有15位,其結構如下:  MCC

ANDROID音訊系統散記resample-2

這篇是承接上一篇提到的底層resample處理,以Samsung的tiny alsa-lib為例說明。 tiny alsa-lib 這個tiny alsa-lib位於android2.3.1-gingerbread/device/samsung/crespo/libau

Android系統應用---SystemUI狀態列電池圖示的顯示和Android電池管理的探討

電池圖示顯示 電池圖示是SystemUI顯示中不可缺少的一部分,它顯示在SystemUI的電池和訊號組合區域。 從佈局來看,電池的顯示屬於status_bar.xml,包含了system_icons.xml佈局 <includelayout="@layout/

android Service傳遞基本型資料的遠端服務

讓其他應用程式複用本程式的服務。這樣的服務叫遠端(remote)服務,實際上是程序間通訊(RPC)。 如果是呼叫不需要資料互動的遠端服務(startservice呼叫),跟無需資料互動的我本地服務呼叫是類似的 如果呼叫遠端服務進行資料互動,這時需要使用android

VCSA 6.5 HA配置 準備工作

vmware vcenter ha 高可用 vcsa 接著上一篇文章部署完成VCSA 6.5後,還需要做一些準備工作才能開啟高可用功能,本篇文章主要就講述如何為vCenter 高可用進行準備工作配置vCenter HA網絡從vCenter HA的架構圖中可以看出對於vCenter HA的高

Linux學習文件夾系統的結構和相對(絕對)路徑

sharp 二進制 沒有 數據 csharp pan 用戶 ont 臨時 理解每個目錄的作用 bin   二進制文件 boot   系統的啟動文件、內核 dev   設備文件 etc   配置文件 home  用戶的家目錄 lib    鏈接庫文件  l

RabbitMQ系列教程發布/訂閱(Publish/Subscribe)

mqc 標題 整合 參數 cti 事情 return 控制臺 run (本教程是使用Net客戶端,也就是針對微軟技術平臺的) 在前一個教程中,我們創建了一個工作隊列。工作隊列背後的假設是每個任務會被交付給一個【工人】。在這一部分我們將做一些完全不同的事情--我們將向多個

OSPF詳解OSPF LSA詳解

ospf lsa詳解 forwarding address OSPF LSA詳解OSPF V2版本中常用的主要有6類LSA,分別是Router-LSA、Network-LSA、Network-summary-LSA、ASBR-summary-LSA、AS-External-LSA、NSSA-LSA,接

Django運維後臺的搭建用url去精細定制與反向解析

django 反向解析 參數傳遞 url指向 上一篇文章裏,我們做了一個alionlineecs(阿裏雲線上環境服務器)的添加界面,但是要知道我們的計劃裏是有六個分支的,而alionlineecs僅僅是其中之一,要是每一個都這麽寫的話,那麽views.py肯定又臭又長,充滿了大量的復制片段。對

camera攝像原理色溫和自動白平衡【轉】

mil gho 實現 技術分享 處理 目標 紅旗 適應 如果 轉自:http://blog.csdn.net/ghostyu/article/details/7912863 色溫的定義:將黑體從絕對零度開始加溫,溫度每升高一度稱為1開氏度(用字母K表示),當溫度升高到一定

Halcon學習有關圖像通道的函數

spa com detail too pan targe 個數 word pop 黑白攝像機會返回每個像素所對應的能量采用結果,這些結果組成了一幅單通道灰度值圖像,而對於RGB彩色攝像機,它將返回每個像素所對應的三個采樣結果,也就是一幅三通道圖像。下面這些是與圖像通道有關的

SoC嵌入式軟件架構設計代碼分塊(Bank)設計原則

post 介紹 讀寫 cor 層次 clas rom bank 分配 上一節講述了在沒有MMU的CPU(如80251、MIPS M控制器系列、ARM cortex m系列)上實現虛擬內存管理的集成硬件設計方法。新設計的內存管理管理單元要實現虛擬內存管理還須要

初識Redis系列Redis支持的數據類型及使用

ted print 數據類型 eight 排序 sorted ring hang 無序 支持的數據類型有五種: string(字符串)、hash(哈希)、list(列表)、set(集合)及zset(sorted set:有序集合); 下面分別對這幾種類型進行簡單的Redis

緩存系列redis安裝及基本數據類型命令使用

pytho children tile 指令 sed eject 檢測 install 文件的 一:Redis是一個開源的key-value存儲系統。與Memcached類似,Redis將大部分數據存儲在內存中,支持的數據類型包括:字符串、哈希表、鏈表、集合、有序集合以及基

SIPp常用腳本UAC

pause rep iso sof app peer ati test level UAC是作為SIP消息的發起端,可以控制消息速率什麽的,方便極了。 一、uac.xml <?xml version="1.0" encoding="ISO-8859-1" ?>

測試開發linux面試後臺進程操作

狀態 很好 分配 例如 名稱 標識 批處理 推薦 子進程 Hi,大家好我是Tom,繼上次分享之後這次給大家帶來新的知識。 進程是Linux系統中一個非常重要的概念。Linux是一個多任務的操作系統,系統上經常同時運行著多個進程。我們不關心這些進程究竟是如何分配的,或者是內核