[Android][frameworks][HIDL]使用HIDL新建虛擬HAL以實現system_server與native進程雙向通信(一)——服務端

前言

需求是這樣的,system_server有一個LocalService,需要向一個具有root權限的native進程進行消息傳遞(下發請求指令,獲取狀態信息等)

 

評估

首先附上官方介紹:https://source.android.com/devices/architecture/hidl

此功能最早使用socket實現(參考lmkd的實現),但是由於如下幾點原因,因此決定使用HIDL重構:

1、socket傳輸效率低,開銷大,通信過程中至少有兩次拷貝過程;

2、由於1的原因,從system_server調用方法主動獲取native進程狀態時效性比binder通信差;

3、由於我們使用的SoC平臺普遍性能較差,因此爲了保證整機性能,需要儘可能減少system_server的壓力;

4、符合Android設計規範,實現更優雅……(汗)

同時考慮到該需求有如下幾點特點:

1、通信兩側語言不同(JAVA - C/C++),雖然可以手動添加一層JNI來實現語言對等,但是能偷懶就偷懶唄;

2、改動需要符合CTS/VTS認證,不能出現neverallow的sepolicy,並且有利於後期推送OS升級的FOTA;

3、native部分儘可能模塊化,甚至可以閉源;

因此使用binderized HIDL看上去是一個比較好的選擇;

 

實現步驟

1、添加本地.hal文件:

mkdir -p hardware/interfaces/example/1.0/
touch hardware/interfaces/example/1.0/IExample.hal
emacs -nw hardware/interfaces/example/1.0/IExample.hal

由於是測試用,就寫個簡單的Hello World:

package [email protected];

interface IExample {
    helloWorld(string in) generates (string out);
};

2、使用hidl-gen命令自動生成相關文件:

首先生成.hal文件對應的Android.bp:

hidl-gen -Landroidbp -rvendor.zsui.hardware:hardware/interfaces/ -randroid.hidl:system/libhidl/transport [email protected]

然後根據.hal文件生成實現類與頭文件:

hidl-gen -o hardware/interfaces/example/1.0/default/ -Landroidbp-impl -rvendor.zsui.hardware:hardware/interfaces/ -randroid.hidl:system/libhidl/transport [email protected]

最後生成實現類的Android.bp:


hidl-gen -o hardware/interfaces/example/1.0/default/ -Lc++-impl -rvendor.zsui.hardware:hardware/interfaces/ -randroid.hidl:system/libhidl/transport [email protected]

若無報錯,最後目錄結構應該是這樣的:

hardware/interfaces/example/
└── 1.0
    ├── Android.bp
    ├── default
    │   ├── Android.bp
    │   ├── Example.cpp
    │   └── Example.h
    └── IExample.hal
​

簡單解讀一下:

1.0目錄下的Android.bp用於解釋HIDL接口相關信息;

default目錄下的Android.bp用於描述HIDL接口實際實現的相關信息;

不太嚴謹地用AIDL類比一下,1.0目錄下的.hal文件可以類比爲.aidl文件,而default目錄下的.cpp和.h文件就是這個接口的具體實現;

此例中的兩個目標模板就創建完成了,並且生成了對應的Android.bp,照理話說此時兩個模塊已經可以進行編譯了,但是隻要你試一下,就會發現如下報錯:

$ make [email protected]
error: hardware/interfaces/example/1.0/Android.bp:9:15: module "[email protected]_interface": interfaces: Cannot find package root specification for package root 'vendor.zsui.hardware' needed for module '[email protected]'. Either this is a mispelling of the package root, or a new hidl_package_root module needs to be added. For example, you can fix this error by adding the following to <some path>/Android.bp:

hidl_package_root {
    name: "vendor.zsui.hardware",
    path: "<some path>",
}

This corresponds to the "-rvendor.zsui.hardware:<some path>" option that would be passed into hidl-gen.
ninja: build stopped: subcommand failed.
19:34:12 soong bootstrap failed with: exit status 1

這個是因爲之前的參數-rvendor.zsui.hardware:hardware/interfaces/指定hardware/interfaces/爲包名vendor.zsui.hardware的package root,但是該路徑下並沒有定義vendor.zsui.hardware包名的package root;

解決方案有三個,鑑於Google架構設計考慮,建議採用c:

a. 修改包名爲hardware/interfaces/Android.bp中定義的hidl_package_root的name,即"android.hardware";

b. 在hardware/interfaces/Android.bp中添加name爲的hidl_package_root;

hidl_package_root {
    name: "vendor.zsui.hardware",
    path: "vendor/zsui/proprietary/hardware/interfaces/",
}

c. 使用其他目錄結構,並手動聲明hidl_package_root,例如:

mkdir -p vendor/zsui/proprietary/hardware/interfaces/
mv hardware/interfaces/example/ vendor/zsui/proprietary/hardware/interfaces/
touch vendor/zsui/proprietary/hardware/interfaces/Android.bp
emacs -nw vendor/zsui/proprietary/hardware/interfaces/Android.bp

在Android.bp中添加:

hidl_package_root {
    name: "vendor.zsui.hardware",
    path: "vendor/zsui/proprietary/hardware/interfaces/",
}

然後編譯就可以成功了;

只是裏面沒有實現,也不會有任何服務跑起來,只是兩個.so庫而已;

out/target/product/xxx/system/lib(64)/[email protected]
out/target/product/xxx/vendor/lib(64)/hw/[email protected]

3、實現HIDL

上一步已經說了,default目錄下就是我們接口的具體實現,因此我們只需要把實現代碼完成:

完善Example.cpp:

#include "Example.h"

namespace vendor {
namespace zsui {
namespace hardware {
namespace example {
namespace V1_0 {
namespace implementation {

// Methods from ::vendor::zsui::hardware::example::V1_0::IExample follow.
Return<void> Example::helloWorld(const hidl_string& name, helloWorld_cb _hidl_cb) {
    // TODO implement
    char buf[100];
    memset(buf, 0, 100);
    snprintf(buf, 100, "Hello World, %s", name.c_str());
    hidl_string result(buf);

    _hidl_cb(result);
    return Void();
}


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

//IExample* HIDL_FETCH_IExample(const char* /* name */) {
    //return new Example();
//}
//
}  // namespace implementation
}  // namespace V1_0
}  // namespace example
}  // namespace hardware
}  // namespace zsui
}  // namespace vendor

示例用,就返回了個字符串拼接;

4、binder化HIDL

現在實現和接口都有了,也都可以編譯了,問題就剩下:誰是服務端,誰是客戶端了;

由於使用binderized HIDL,因此我們可以直接基於impl創建服務端:

a. 創建兩個文件

touch vendor/zsui/proprietary/hardware/interfaces/example/1.0/default/service.cpp
touch vendor/zsui/proprietary/hardware/interfaces/example/1.0/default/[email protected]

其中service.cpp爲服務入口,[email protected]爲啓動相關注冊信息;

b. 在service.cpp中添加入口函數並註冊服務:

#include <vendor/zsui/hardware/example/1.0/IExample.h>

#define LOG_TAG "Example-hal"

#include <hidl/LegacySupport.h>
#include "Example.h"

using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;

using vendor::zsui::hardware::example::V1_0::IExample;
using vendor::zsui::hardware::example::V1_0::implementation::Example;

int main() {
    configureRpcThreadpool(2, true /*callerWillJoin*/);
    android::sp<IExample> service = new Example();
    if(service->registerAsService() != android::OK){
        ALOGE("hal registration FAILED");
    } else {
        ALOGV("hal ready");
        joinRpcThreadpool();
    }
    ALOGW("hal exiting");
    return 0;
}

c. 由於我們需要編譯出可執行的二進制文件,而不是一個.so庫,因此需要修改Android.bp:(注意//Modify與//Add字段)

cc_binary {     //Modify
    // FIXME: this should only be -impl for a passthrough hal.
    // In most cases, to convert this to a binderized implementation, you should:
    // - change '-impl' to '-service' here and make it a cc_binary instead of a
    //   cc_library_shared.
    // - add a *.rc file for this module.
    // - delete HIDL_FETCH_I* functions.
    // - call configureRpcThreadpool and registerAsService on the instance.
    // You may also want to append '-impl/-service' with a specific identifier like
    // '-vendor' or '-<hardware identifier>' etc to distinguish it.
    name: "[email protected]",       //Modify
    relative_install_path: "hw",
    // FIXME: this should be 'vendor: true' for modules that will eventually be
    // on AOSP.
    proprietary: true,
    defaults: ["hidl_defaults"],    //Add
    srcs: [
        "Example.cpp",
        "service.cpp",      //Add
    ],
    shared_libs: [
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "liblog",       //Add
        "[email protected]",
    ],
    
    init_rc: ["[email protected]"],       //Add
}

d. 添加對應服務啓動信息到[email protected]

service example-hal-1-0 /vendor/bin/hw/[email protected]
    class hal
    user root
    group system
    writepid /dev/cpuset/system-background/tasks

至此,編譯下[email protected],應該是可以編譯過的;

服務端的創建差不多就到這裏了,下一篇會着重講解sepolicy的設置、以及JAVA端調用的方式;

文筆有限,若有謬誤,還請指出;

感謝!

 

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