AndroidQ 打通應用層到HAL層---(HIDL服務實現)

AndroidQ 打通應用層到HAL層—(HAL模塊實現)這篇文章中我們已經實現了自己的HAL,本篇我們實現一個HIDL服務,通過這個服務來調用HAL模塊的函數

什麼是HIDL

HIDL 全稱爲HAL interface definition language(發音爲“hide-l”)是用於指定 HAL 和其用戶之間的接口的一種接口描述語言 (IDL),Android O開始引入了HIDL這個概念,HIDL和應用層AIDL差不多,AIDL常用於連接App和Framework,HIDL則是用來連接Framework和HAL,AIDL使用Binder通信,HIDL則使用HwBinder通信,他們都是通過Binder驅動完成通信,只不過兩個Binder域不一樣

爲什麼需要HIDL

目前Android系統生態是幾乎每年google都會出一個Android大版本,而普通手機用戶一部手機一般要用兩三年,所以你會發現儘管Android系統已經升級到了10,馬上11出來了,然後還是有很多用戶依然使用的是Android 5,6,7等版本,對普通用戶來說如果不更換手機就很難跟上Android版本,這是因爲OEM廠商在同一設備上進行系統升級需要花費時間金錢成本很高,導致他們不願意升級,成本高的原因是Android O之前Android Framework的升級需要OEM將HAL也進行對應升級,Framework和HAL是一起被編譯成system.img,它們存在高耦合,針對這種情況google在Android O中引入了Treble計劃,Treble的目的就是解耦Framework和HAL,就是通過HIDL來實現,Framework不再直接調用HAL,而是通過HIDL來間接使用HAL模塊,每個HAL模塊都可以對應一個HIDL服務,Framework層通過HwBinder創建HIDL服務,通過HIDL服務來獲取HAL相關模塊繼而打開HAL下的設備,而最終HAL也從system.img中分離,被編進一個單獨的分區vendor.img,從而簡化了Android系統升級的影響與難度

HIDL的使用

HIDL可以分爲:HIDL C++(C++實現)、HIDL Java(Java 實現),並且還主要分爲直通式和綁定式,本篇文章使用的C++和直通式的HIDL,HIDL用起來非常簡單,AOSP的hardware/interfaces/目錄下有很多的HIDL,我們仿照其他HIDL創建自己的HIDL目錄:hardware/interfaces/hello_hidl/1.0

並在此目錄下創建一個IHello.hal文件:

package [email protected];

interface IHello {

    addition_hidl(uint32_t a,uint32_t b) generates (uint32_t total);
    
};

這個文件定義了一個addition_hidl函數,這個函數用來調用HAL的加法函數

然後就可以使用Android提供的工具hidl-gen來生成HIDL框架,執行如下命令:

 [email protected]
 LOC=hardware/interfaces/hello_hidl/1.0/default/
 hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
 hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE

執行命令成功之後我們會發現hardware/interfaces/hello_hidl/1.0下多了一個default目錄,進入default目錄,裏面有三個文件Android.bp,Hello.cpp,Hello.h
在這裏插入圖片描述
之後再在執行./hardware/interfaces/update-makefiles.sh這個命令,update-makefiles.sh這個腳本目的是爲HIDL生成對應Android.bp文件

最後目錄結構爲:
在這裏插入圖片描述

接着我們還需要在default目錄下增加一個空文件service.cpp,用作註冊HIDL服務,我們採用直通式的HIDL,所以service.cpp的內容爲:

#include <android/hardware/hello_hidl/1.0/IHello.h>
#include <hidl/LegacySupport.h>
// Generated HIDL files
using android::hardware::hello_hidl::V1_0::IHello;
using android::hardware::defaultPassthroughServiceImplementation;

int main() {
    return defaultPassthroughServiceImplementation<IHello>();
}

defaultPassthroughServiceImplementation函數最終會向HwServiceManager註冊HIDL服務

接着我們來看看之前生成的文件,首先看Hello.h

// FIXME: your file license if you have one

#pragma once

#include <android/hardware/hello_hidl/1.0/IHello.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>

namespace android {
namespace hardware {
namespace hello_hidl {
namespace V1_0 {
namespace implementation {
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_memory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;

struct Hello : public IHello {
    // Methods from ::android::hardware::hello_hidl::V1_0::IHello follow.
    Return<uint32_t> addition_hidl(uint32_t a, uint32_t b) override;

    // Methods from ::android::hidl::base::V1_0::IBase follow.

};

// FIXME: most likely delete, this is only for passthrough implementations
//去掉註釋
extern "C" IHello* HIDL_FETCH_IHello(const char* name);

}  // namespace implementation
}  // namespace V1_0
}  // namespace hello_hidl
}  // namespace hardware
}  // namespace android

系統自動生成了Hello結構體(當然也可以自己改爲class),繼承IHello接口,addition_hidl函數就需要在Hello.cpp中去實現了,因爲我們採用直通式HIDL,所以需要將// extern “C” IHello* HIDL_FETCH_IHello(const char* name);的註釋去掉

接着來看看Hello.cpp:

// FIXME: your file license if you have one

#include "Hello.h"

namespace android {
namespace hardware {
namespace hello_hidl {
namespace V1_0 {
namespace implementation {

// Methods from ::android::hardware::hello_hidl::V1_0::IHello follow.
Return<uint32_t> Hello::addition_hidl(uint32_t a, uint32_t b) {
    // TODO implement
    return uint32_t {};
}
// Methods from ::android::hidl::base::V1_0::IBase follow.

IHello* HIDL_FETCH_IHello(const char* /* name */) {
    return new Hello();
}
}  // namespace implementation
}  // namespace V1_0
}  // namespace hello_hidl
}  // namespace hardware
}  // namespace android

同樣需要去掉HIDL_FETCH_IHello函數的註釋,採用直通式HIDL時,通過前面service.cpp中的defaultPassthroughServiceImplementation函數註冊HIDL服務時,內部原理就是通過“HIDL_FETCH_”字串拼接defaultPassthroughServiceImplementation傳遞的IHello,找到HIDL_FETCH_IHello函數並獲取IHello對象,我們可以看到HIDL_FETCH_IHello初始代碼就是創建了一個Hello對象

在接着看default目錄下的Android.bp:

cc_library_shared {
    name: "[email protected]",
    relative_install_path: "hw",
    proprietary: true,
    srcs: [
        "Hello.cpp",
    ],
    shared_libs: [
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "[email protected]",
    ],
}

這個Android.bp會將Hello這個HIDL服務編譯成一個[email protected],它還依賴一個[email protected],這個so哪來的呢?

再接着看1.0目錄下的Android.bp:

// This file is autogenerated by hidl-gen -Landroidbp.
hidl_interface {
    name: "[email protected]",
    root: "android.hardware",
    vndk: {
        enabled: true,
    },
    srcs: [
        "IHello.hal",
    ],
    interfaces: [
        "[email protected]",
    ],
    gen_java: true,
}

這個Android.bp會將hardware/interfaces/hello_hidl/1.0這個HIDL編譯成一個[email protected],到這裏我們發現service.cpp沒有用到,所以我們還需要修改default目錄下的Android.bp:

// FIXME: your file license if you have one

cc_library_shared {
    name: "[email protected]",
    relative_install_path: "hw",
    proprietary: true,
    srcs: [
        "Hello.cpp",
    ],
    shared_libs: [
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "liblog",
        "libhardware",
        "[email protected]",
    ],
}
cc_binary {
    name: "[email protected]",
    defaults: ["hidl_defaults"],
    relative_install_path: "hw",
    vendor: true,
    srcs: ["service.cpp"],
    shared_libs: [
        "[email protected]",
        "libhardware",
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "liblog",
    ],

}

新增加對service.cpp的編譯,我們將service.cpp編譯成一個二進制可執行文件[email protected],用來啓動HIDL服務,好了,最終我們這個HIDL會編譯出來如下三個so:
[email protected]
[email protected]
[email protected]

還有一點需要注意的是,這個HIDL想要被Framework獲取使用還需要在manifest.xml中註冊,
manifest.xml在手機vendor/etc/vintf/manifest.xml下,我們將這個文件pull出來然後添加如下代碼:

   <hal format="hidl">
        <name>android.hardware.hello_hidl</name>
        <transport>hwbinder</transport>
        <version>1.0</version>
        <interface>
            <name>IHello</name>
            <instance>default</instance>
        </interface>
        <fqname>@1.0::IHello/default</fqname>
    </hal>

然後在Hello.cpp中添加一行log,之後進行編譯

IHello* HIDL_FETCH_IHello(const char* /* name */) {

    ALOGE("hello_hidl service is init success....");
    
    return new Hello();
}

執行mmm hardware/interfaces/hello_hidl/1.0/
在這裏插入圖片描述
編譯成功後我們將生成的三個so分別push到手機vendor/lib64/hw/,vendor/lib64/,vendor/bin/hw/目錄下

adb push vendor/lib64/hw/[email protected] vendor/lib64/hw/

adb push system/lib64/[email protected] vendor/lib64/

adb push vendor/bin/hw/[email protected] vendor/bin/hw/

接着我們到手機vendor/bin/hw/目錄下去執行[email protected]這個二進制可執行文件,這個文件就會執行service.cpp的代碼,調用defaultPassthroughServiceImplementation註冊我們的HIDL服務

在這裏插入圖片描述

再看看log輸出:
在這裏插入圖片描述
在執行[email protected]時就會輸入這句log,代表我們這個HIDL服務已經實現,其實通常的HIDL服務都是通過rc文件來開機啓動的,我這裏爲了方便演示就沒有寫

再執行adb shell ps -A|grep -i --color "hello_hidl"命令看下這個服務狀態
我們發現HIDL服務啓動之後就會一直在後臺,這個其實和AMS,WMS這種服務是類似的,啓動之後在後臺會等待client端訪問
在這裏插入圖片描述

HIDL這個服務已經能夠正常啓動了,接着寫一個測試程序看能否獲取這個服務,並且調用該服務的函數,我在Hello.cpp的addition_hidl函數中添加了一句log:

Return<uint32_t> Hello::addition_hidl(uint32_t a, uint32_t b) {
    // TODO implement
    ALOGD("dongjiao...Hello::addition_hidl a = %d,b = %d",a,b);
    return uint32_t {};
}

測試程序寫在hardware/interfaces/hello_hidl/1.0/default目錄下:
在這裏插入圖片描述
Hello_hidl_test.cpp:

#include <android/hardware/hello_hidl/1.0/IHello.h>
#include <hidl/LegacySupport.h> 
#include <log/log.h>

using android::sp;
using android::hardware::hello_hidl::V1_0::IHello;
using android::hardware::Return;
int main(){
    android::sp<IHello> hw_device = IHello::getService();
    if (hw_device == nullptr) {
              ALOGD("dongjiao...failed to get hello-hidl");
              return -1;
        }
    ALOGD("dongjiao...success to get hello-hidl....");
    Return<uint32_t> total = hw_device->addition_hidl(3,4);
    return 0;
}

測試程序代碼也比較簡單,獲取IHello的服務,然後調用addition_hidl函數

看一下Android.bp:

cc_binary {
    name: "Hello_hidl_test",
    srcs: ["Hello_hidl_test.cpp"],
    shared_libs: [
        "liblog",
        "[email protected]",
        "libhidlbase",
        "libhidltransport",
        "libhwbinder",
        "libutils",
    ],
}

我們再編譯這個測試程序,它會被編譯成一個可執行二進制文件Hello_hidl_test
編譯命令:
mmm hardware/interfaces/hello_hidl/1.0/default/test/
在這裏插入圖片描述
編譯成功了,將這個可執行文件push到手機/system/bin/目錄下

在執行Hello_hidl_test之前別忘了把HIDL服務啓動起來
在這裏插入圖片描述
接着執行Hello_hidl_test
在這裏插入圖片描述
然後看log輸出
在這裏插入圖片描述
可以看到成功了,成功獲取到了HIDL服務並且調用了該服務的addition_hidl函數,將我們的參數傳遞了過去,實際開發中就可以在獲取HIDL服務時打開HAL模塊,然後可以直接調用HAL的函數,上一篇文章其實也寫了測試程序測自定義的HAL模塊,我們只需要將上一篇文章中的測試程序中的代碼copy到HIDL初始化代碼中就能夠調用HAL的那個加法函數了,具體就不測試了

到這裏HIDL服務已經成功實現並且能夠正常使用,HIDL這個框架用起來還是比較簡單的,大部分代碼都是通過工具生成的

下一篇文章我將在native層定義一個JNI服務,然後在JNI服務中調用HIDL服務定義的addition_hidl函數

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