[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 /Android.bp:

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

This corresponds to the "-rvendor.zsui.hardware:" 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 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 

#define LOG_TAG "Example-hal"

#include 
#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 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 '-' 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端调用的方式;

文笔有限,若有谬误,还请指出;

感谢!

 

你可能感兴趣的:(Android)