添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

本文基于 AOSP Android10 r41 源码环境

1. Android O 后的 HAL —— Treble 计划

在 Android O 以前,framework 与 hal 是紧耦合的存在于 system.img 中,因此进行版本升级时需要: OEM 厂商适配 framework ,SoC 厂商适配 hal, 之后将修改打包到system.img,生成 OTA 升级包,推送到手机进行 OTA 升级。

​在 Android O 以后,framework 与 hal 进行了解耦, framework 存在于system.img,hal 存在于 vendor.img ,进行版本升级时,分为两次升级:

  • framework 升级: OEM 厂商适配 framework,将修改打包到system.img, 生成OTA 升级包,推送到手机进行OTA 升级(framework 发生改变,hal 层未变)。
  • hal 升级:SoC 厂商适配 hal, 将修改打包到 vendor.img, 生成 OTA 升级包,推送到手机进行 OTA 升级(framework发生改变,hal 层发生改变)。
  • 要实现 Android O 以后的升级方式,需要保证 System 分区与 Vendor 分区之间的接口稳定,Android O 中提供了多种技术来维护这个接口的稳定性,其中最主要的是:

  • VNDK:System 分区中的库,这些库 Vendor 分区可能会使用到,这些库由 google 维护和保持其对外的稳定性。
  • HIDL:Vendor 分区通过 HIDL 方式来对外提供使用接口,需要 SOC 厂商维护其对外接口的稳定性。
  • 接下来我们就来看看 HIDL 在 HAL 层中的使用。

    2. Hal 层的实现

    源码可以在 github.com/yuandaimaah… 这里下载到。

    首先我们需要将 Linux 驱动开发入门 中介绍的 hello 驱动添加到内核中,接着在看下面的操作:

    2.1 Hal 协议实现

    Hal 层的实现一般放在 vendor 目录下,我们在 vendor 目录下创建如下的目录:

    mkdir -p jelly/hardware/interface/hello_hidl/1.0
    

    接着在 vendor/jelly/hardware/interface/hello_hidl/1.0 目录下创建 Hal 文件;

    IHello.hal :

    //定义包名,最后跟一个版本号
    package jelly.hardware.hello_hidl@1.0;
    //定义 hidl 服务对外提供的接口
    interface IHello {
        //for test,generates 后面跟的是返回类型
        addition_hidl(uint32_t a,uint32_t b) generates (uint32_t total);
        //写 hello 驱动
        write(string name) generates (uint32_t result);
        //读 hello 驱动
        read() generates (string name);
    

    这里的 IHello.hal 定义了我们的服务对外提供了哪些函数。可以认为这就是我们服务的对外协议。协议一般定义好就不会再修改,以保持对外的稳定性。

    关于 hal 的写法,可以参考官方的文档,另外也可以参考 hardware 目录下系统自带的 hal 的写法。

    2.2 hal 文件生成 C++ 源文件

    接着我们使用 hidl-gen 命令将我们写的 hal 文件转换为 C++ 文件:

    source build/envsetup.sh
    PACKAGE=jelly.hardware.hello_hidl@1.0
    LOC=vendor/jelly/hardware/interfaces/hello_hidl/1.0/default
    hidl-gen -o $LOC -Lc++-impl -rjelly.hardware:vendor/jelly/hardware/interfaces $PACKAGE
    

    接着就会生成一些 C++ 代码:

    vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下会生成 Hello.cpp 和 Hello.h

    out/soong/.intermediates/vendor/jelly/hardware/interfaces/hello_hidl/1.0/jelly.hardware.hello_hidl@1.0_genc++/gen/jelly/hardware/hello_hidl/1.0 中生成了如下代码:

    out/soong/.intermediates/vendor/jelly/hardware/interfaces/hello_hidl/1.0/jelly.hardware.hello_hidl@1.0_genc++_headers/gen/jelly/hardware/hello_hidl/1.0 中生成了如下代码:

    接着修改 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下生成的 Hello.cpp:

    // FIXME: your file license if you have one
    #include "Hello.h"
    #include <log/log.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdio.h>
    namespace jelly {
    namespace hardware {
    namespace hello_hidl {
    namespace V1_0 {
    namespace implementation {
    // Methods from ::jelly::hardware::hello_hidl::V1_0::IHello follow.
    Return<uint32_t> Hello::addition_hidl(uint32_t a, uint32_t b) {
        ALOGE("hello_hidl service is init success....a :%d,b:%d",a,b);
        return uint32_t {};
        return uint32_t {};
    Return<uint32_t> Hello::write(const hidl_string& name) {
        int fd = open("/dev/hello", O_RDWR);
        if (fd == -1)
    		printf("can not open file /dev/hello\n");
    		return  uint32_t { 0 };
        ::write(fd, name.c_str(), 100);
        close(fd);
        return uint32_t { 1 };
    //通过 read_cb function 回调函数,回传数据
    Return<void> Hello::read(read_cb _hidl_cb) {
        char buf[100];
        int fd = open("/dev/hello", O_RDWR);
        if (fd == -1)
    		printf("can not open file /dev/hello\n");
    		return Void();
        ::read(fd, buf, 100);
        hidl_string result(buf);
        _hidl_cb(result);
        close(fd);
        return Void();
    }  // namespace implementation
    }  // namespace V1_0
    }  // namespace hello_hidl
    }  // namespace hardware
    }  // namespace jelly
    

    这里主要是对我们的协议进行实现,实现了对上一节实现的设备文件 /dev/hello 的读写。至此我们的 hidl 服务就定义好了

    2.3 服务端实现

    接着我们需要写一个 Server 端来向 HwServiceManager 注册我们的服务。在 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下添加 service.cpp:

    #include <hidl/HidlTransportSupport.h>
    #include <utils/Looper.h>
    #include <utils/StrongPointer.h>
    #include <log/log.h>
    #include "Hello.h"
    using android::hardware::configureRpcThreadpool;
    using android::hardware::joinRpcThreadpool;
    using jelly::hardware::hello_hidl::V1_0::IHello;
    using jelly::hardware::hello_hidl::V1_0::implementation::Hello;
    int main() {
        ALOGD("hello-hidl is starting...");
        configureRpcThreadpool(4, true /* callerWillJoin */);
        android::sp<IHello> service = new Hello();
        android::status_t ret = service->registerAsService();
        if (ret != android::NO_ERROR) {
        joinRpcThreadpool();
        return 0;
        //Passthrough模式
        //return defaultPassthroughServiceImplementation<IHello>(4);
    

    我们的服务端需要在开机时启动,创建 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default/jelly.hardware.hello_hidl@1.0-service.rc 文件:

    service vendor_hello_hidl_service /vendor/bin/hw/jelly.hardware.hello_hidl@1.0-service
    	class hal
    	user system
    	group system
    

    接着我们需要添加 VINTF 对象,对于注册到 hwservicemanager 的服务都需要添加一个 VINTF 对象。对于编码来说 VINTF 对象就是一个 xml 文件,创建 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default/jelly.hardware.hello_hidl@1.0-service.xml 文件:

    <manifest version="1.0" type="device">
      <hal format="hidl">
            <name>jelly.hardware.hello_hidl</name>
            <transport>hwbinder</transport>
            <version>1.0</version>
            <interface>
                <name>IHello</name>
                <instance>default</instance>
            </interface>
        </hal>
    </manifest>
    

    2.4 生成 Android.bp

    接着我们来生成 Android.bp:

    hidl-gen -o $LOC -Landroidbp-impl -rjelly.hardware:vendor/jelly/hardware/interfaces $PACKAGE
    

    这个命令会在 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下生成一个 Android.bp,我们在生成的基础上稍作修改如下:

    // FIXME: your file license if you have one
    cc_library_shared {
        name: "jelly.hardware.hello_hidl@1.0-impl",
        relative_install_path: "hw",
        proprietary: true,
        srcs: [
            "Hello.cpp",
        shared_libs: [
            "libhidlbase",
            "libhidltransport",
            "libutils",
            "jelly.hardware.hello_hidl@1.0",
            "liblog",
    cc_binary {
        name: "jelly.hardware.hello_hidl@1.0-service",
        init_rc: ["jelly.hardware.hello_hidl@1.0-service.rc"],
        vintf_fragments: ["jelly.hardware.hello_hidl@1.0-service.xml"],
        defaults: ["hidl_defaults"],
        relative_install_path: "hw",
        vendor: true,
        srcs: ["service.cpp", "Hello.cpp"],
        shared_libs: [
            "jelly.hardware.hello_hidl@1.0",
            "libhardware",
            "libhidlbase",
            "libhidltransport",
            "libutils",
            "liblog",
    

    生成的库里面有一个依赖 jelly.hardware.hello_hidl@1.0,接着我们来生成这个库对应的 Android.bp:

    hardware/interfaces 目录下将 update-makefiles.sh 拷贝到 vendor/jelly/hardware/interfaces/ 目录下,并修改如下:

    #!/bin/bash
    source $ANDROID_BUILD_TOP/system/tools/hidl/update-makefiles-helper.sh
    do_makefiles_update \
      "jelly.hardware:vendor/jelly/hardware/interfaces"
    

    接着执行:

    ./vendor/jelly/hardware/interfaces/update-makefiles.sh
    

    就会生成 vendor/jelly/hardware/interfaces/Android.bp 文件:

    hidl_interface {
        name: "jelly.hardware.hello_hidl@1.0",
        root: "jelly.hardware",
        srcs: [
            "IHello.hal",
        interfaces: [
            "android.hidl.base@1.0",
        gen_java: false,
    

    其中的 hidl_interface 是 hidl 独有的,当编译源码时,它会将 out/soong/.intermediates/vendor/jelly/hardware/interfaces/hello_hidl/1.0/jelly.hardware.hello_hidl@1.0_genc++/gen/jelly/hardware/hello_hidl/1.0out/soong/.intermediates/vendor/jelly/hardware/interfaces/hello_hidl/1.0/jelly.hardware.hello_hidl@1.0_genc++_headers/gen/jelly/hardware/hello_hidl/1.0 目录下的源码编译为 jelly.hardware.hello_hidl@1.0.so 文件,并预制到手机的 /vendor/lib/vendor/lib64/ 目录下。

    为了使编译通过,新建 vendor/jelly/hardware/interfaces/Android.bp 文件:

    hidl_package_root {
        name: "jelly.hardware",
        path: "vendor/jelly/hardware/interfaces",
    

    这个 Android.bp 的作用是告诉编译系统包名与路径的映射关系。

    接着新建 vendor/jelly/hardware/interfaces/current.txt 文件,current.txt 记录了所有 hal 接口的 hash 值,接口有变化时,同时需要更新 current.txt 中的 hash 值,这是我们先随便设置一个 hash 值:

    123456 jelly.hardware.hello_hidl@1.0::IHello
    

    再执行一遍 update-makefiles.sh,这个时候就会发现提示 hash 值不正确了,同时会给出正确的 hash 值,我们把正确的 hash 值替换到 current.txt 即可。

    2.5 客户端编写

    vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下创建如下的文件和文件夹:

    其中 hello_hidl_test.cpp:

    #include <jelly/hardware/hello_hidl/1.0/IHello.h>
    #include <hidl/LegacySupport.h>
    #define LOG_TAG "hello_hidl"
    #include <log/log.h>
    using android::sp;
    using jelly::hardware::hello_hidl::V1_0::IHello;
    using android::hardware::Return;
    using android::hardware::hidl_string;
    int main(){
        android::sp<IHello> hw_device = IHello::getService();
        if (hw_device == nullptr) {
                  ALOGD("failed to get hello-hidl");
                  return -1;
        ALOGD("success to get hello-hidl....");
        Return<uint32_t> total = hw_device->addition_hidl(3,4);
        hw_device->write("hello");
        hw_device->read([&](hidl_string result){
            ALOGD("%s\n", result.c_str());
        return 0;
    

    Android.bp:

    cc_binary {
        name: "hello_hidl_test",
        srcs: ["hello_hidl_test.cpp"],
        vendor: true,
        shared_libs: [
            "liblog",
            "jelly.hardware.hello_hidl@1.0",
            "libhidlbase",
            "libhidltransport",
            "libhwbinder",
            "libutils",
    

    2.6 selinux 配置

    device/Jelly/Rice14/sepolicy 目录下添加:

    device.te 中添加如下内容:

    type hello_hidl_dev_t, dev_type;
    

    hwservice.te:

    type hello_hidl_hwservice, hwservice_manager_type;
    

    hello_hidl.te:

    type hello_hidl, domain;
    type hello_hidl_exec, exec_type, vendor_file_type, file_type;
    init_daemon_domain(hello_hidl);
    add_hwservice(hello_hidl, hello_hidl_hwservice)
    hwbinder_use(hello_hidl)
    allow hello_hidl hidl_base_hwservice:hwservice_manager { add };
    allow hello_hidl hello_hidl_dev_t:chr_file { open read write };
    binder_call(hello_hidl,hwservicemanager)
    get_prop(hello_hidl,hwservicemanager_prop)
    

    hwservice_contexts:

    jelly.hardware.hello_hidl::IHello      u:object_r:hello_hidl_hwservice:s0
    

    hello_hidl_test.te

    type  hello_hidl_test, domain;
    type  hello_hidl_test_exec, exec_type, vendor_file_type, file_type;
    domain_auto_trans(shell, hello_hidl_test_exec, hello_hidl_test);
    get_prop(hello_hidl_test, hwservicemanager_prop)
    allow hello_hidl_test hello_hidl_hwservice:hwservice_manager find;
    hwbinder_use(hello_hidl_test);
    

    在 file_contexts 中添加:

    /dev/hello                      u:object_r:hello_hidl_dev_t:s0
    /vendor/bin/hw/jelly\.hardware\.hello_hidl@1\.0-service    u:object_r:hello_hidl_exec:s0
    

    注意,file_contexts 最后必须留一行空行,不然会编译失败

    2.7 编译执行

    接着在 device/Jelly/Rice14/Rice14.mk 中添加如下内容:

    BOARD_SEPOLICY_DIRS += \ device/Jelly/Rice14/sepolicy PRODUCT_PACKAGES += \ jelly.hardware.hello_hidl@1.0-service \ hello_hidl_test \ jelly.hardware.hello_hidl@1.0-impl \

    然后整编系统:

    source 
    lunch 1
    make -j16
    

    最后测试:

    # 执行客户端程序
    hello_hidl_test &
    # 查看 log
    # logcat | grep hello 
    05-03 14:20:19.816  1555  1555 D         : hello-hidl is starting...
    05-03 14:20:19.816  1555  1555 I ServiceManagement: Registered jelly.hardware.hello_hidl@1.0::IHello/default (start delay of 36ms)
    05-03 14:20:19.816  1555  1555 I ServiceManagement: Removing namespace from process name jelly.hardware.hello_hidl@1.0-service to hello_hidl@1.0-service.
    05-03 14:20:38.843  3469  3469 D hello_hidl: success to get hello-hidl....
    05-03 14:20:38.843  1555  1563 E hello_hidl: hello_hidl service is init success....a :3,b:4
    05-03 14:20:38.844  1555  1563 D hello_hidl: service is writing 
    05-03 14:20:40.846  1555  1563 D hello_hidl: service is reading 
    05-03 14:20:40.847  3469  3469 D hello_hidl: hello
    

    可以看到我们的客户端已经访问到服务端了。

    以上是通过 hwbinder 跨进程通信的方式,来实现的。对于效率要求较高的模块,我们还是希望通过 dlopen 直接加载的方式来使用 hal 层。Android 的 Hidl 框架也是提供了支持的。这种方式称为直通模式(passthrough),这部分内容会在下一篇教程中讲解。

  • Android系统开发入门-11.添加hidl服务
  • Android系统开发入门-12.hidl服务回调
  • Android HIDL模型下HAL Service添加SELinux规则实战
  • ROC-RK3399-PC Pro Android10 源码
  • 一个简单的HIDL开发笔记
  • 简单HIDL HAL的实现
  • hidl 服务启动流程
  • SElinux权限配置
  • AndroidO Treble架构下Hal进程启动及HIDL服务注册过程
  • Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要
  • 系统角度解读Android P新特性
  • Android : 供应商原生开发套件 (VNDK)
  • android 自定义驱动(第三篇:HIDL服务端)
  • 分类:
    Android
    标签: