文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

[Android][frameworks][HIDL]使用HIDL新建虚拟HAL以实现system_server与native进程双向通信(一)——服务端

2022-06-06 13:02

关注

前言

需求是这样的,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 vendor.zsui.hardware.example@1.0;
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 vendor.zsui.hardware.example@1.0

然后根据.hal文件生成实现类与头文件:


hidl-gen -o hardware/interfaces/example/1.0/default/ -Landroidbp-impl -rvendor.zsui.hardware:hardware/interfaces/ -randroid.hidl:system/libhidl/transport vendor.zsui.hardware.example@1.0

最后生成实现类的Android.bp:



hidl-gen -o hardware/interfaces/example/1.0/default/ -Lc++-impl -rvendor.zsui.hardware:hardware/interfaces/ -randroid.hidl:system/libhidl/transport vendor.zsui.hardware.example@1.0

若无报错,最后目录结构应该是这样的:


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 vendor.zsui.hardware.example@1.0
error: hardware/interfaces/example/1.0/Android.bp:9:15: module "vendor.zsui.hardware.example@1.0_interface": interfaces: Cannot find package root specification for package root 'vendor.zsui.hardware' needed for module 'vendor.zsui.hardware.example@1.0'. 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)/vendor.zsui.hardware.example@1.0.so
out/target/product/xxx/vendor/lib(64)/hw/vendor.zsui.hardware.example@1.0-impl.so
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* ) {
    //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/vendor.zsui.hardware.example@1.0-service.rc

其中service.cpp为服务入口,vendor.zsui.hardware.example@1.0-service.rc为启动相关注册信息;

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 );
    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: "vendor.zsui.hardware.example@1.0-service",       //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
        "vendor.zsui.hardware.example@1.0",
    ],
    init_rc: ["vendor.zsui.hardware.example@1.0-service.rc"],       //Add
}

d. 添加对应服务启动信息到vendor.zsui.hardware.example@1.0-service.rc:


service example-hal-1-0 /vendor/bin/hw/vendor.zsui.hardware.example@1.0-service
    class hal
    user root
    group system
    writepid /dev/cpuset/system-background/tasks

至此,编译下vendor.zsui.hardware.example@1.0-service,应该是可以编译过的;

服务端的创建差不多就到这里了,下一篇会着重讲解sepolicy的设置、以及JAVA端调用的方式;

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

感谢!


作者:Ryan ZHENG


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯