美文网首页
一个简单的HIDL开发笔记

一个简单的HIDL开发笔记

作者: 假期开发仔 | 来源:发表于2021-08-04 14:15 被阅读0次

    个人学习总结

    一、创造HIDL实例相关文件

    创建自己的HAL层文件目录

    这里我以我自己的源码目录platform/vendor/mediatek/hardware/interfaces目录为例,在该目录下创建cyhhidl目录;

    cd vendor/mediatek/hardware/interfaces
    mkdir -p cyhhidl/1.0/default
    

    在版本目录(即1.0)中创建接口描述文件ICyhHidl.hal
    文件内容如下:

     package vendor.mediatek.hardware.cyhhidl@1.0;
    
        interface ICyhHidl {
          helloWorld(string name) generates (string result);
      };
    

    内部实现了一个helloWorld方法,传入string类型对象并返回string类型对象。

    使用工具和脚本生成所需文件

    使用hidl-gen生成HAL相关文件,使用脚本更新Makefile文件,自动生成Android.bpAndroid.mk

    根据自身情况设定好对应的值,其中TT是固定的,不用修改

    LOC=vendor/mediate/hardware/interfaces/cyhhidl/1.0/default
    PACKAGE=vendor.mediatek.hardware.cyhhidl@1.0
    SS=vendor.mediatek.hardware:vendor/mediatek/hardware/interfaces
    TT=android.hidl:system/libhidl/transport
    

    hidl-gen工具代码路径在system/tools/hidl,通过mmm编译后会在out 下生成out/host/linux-x86/bin/hidl-gen,之后在命令行中执行(编译hidl-gen开始就在源码根目录,vendor目录下要修改相应的内容)

      ./out/host/linux-x86/bin/hidl-gen -o . -Landroidbp -r$SS -r$TT $PPACKAGE
    

    这样就在1.0目录下生成了Android.bp文件,修改hal文件接口后,需要使用hidl-gen添加对应的哈希:

    ./out/host/linux-x86/bin/hidl-gen -L hash -r$SS -r$TT $PPACKAGE
    

    一般是在vendor/mediatek/hardware/interfaces/current.txt文件内按字母升序添加,接下来生成default目录用于实现服务端的c++代码及bp描述文件,执行以下命令

    ./out/host/linux-x86/bin/hidl-gen -o $LOC -Lc++-impl -r$SS -r$TT $PPACKAGE
    

    这样就在default目录生成了CyhHidl.cppCyhHidl.h文件,接着执行以下命令:

    ./out/host/linux-x86/bin/hidl-gen -o $LOC -Landroidbp-impl -r$SS -r$TT $PPACKAGE
    

    这样就在default目录下生成了Android.bp文件。
    命令行中的-o 需要视情况使用,将会覆盖生成代码,以免造成已修改的服务端代码被覆盖掉。
    至此就生成了HIDL服务所需要的文件:
    1.0/Android.bp1.0/default/CyhHidl.h1.0/default/CyhHidl.cpp,其中1.0/Android.bp内容如下:

    // This file is autogenerated by hidl-gen -Landroidbp.
    
    hidl_interface {
        name: "vendor.mediatek.hardware.cyhhidl@1.0",
        root: "vendor.mediatek.hardware",
        srcs: [
            "ICyhHidl.hal",
        ],
        interfaces: [
            "android.hidl.base@1.0",
        ],
        gen_java: true,
    }
    

    之后我们就要具体实现内部的方法

    二、实现HAL

    CyhHidl.h文件内可以定义实现的实例是哪种模式(直通Passthrough模式绑定Binderized模式,使用直通式HIDL 需要打开屏蔽了的HIDL_FETCH_name方法,并在实现返回。),之后实现对应的.cpp文件内的函数;这里不对CyhHidl.h做改动,使用Binderized模式
    1.0/default/CyhHidl.h

    #ifndef VENDOR_MEDIATEK_HARDWARE_CYHHIDL_V1_0_CYHHIDL_H
    #define VENDOR_MEDIATEK_HARDWARE_CYHHIDL_V1_0_CYHHIDL_H
    
    #include <vendor/mediatek/hardware/cyhhidl/1.0/ICyhHidl.h>
    #include <hidl/MQDescriptor.h>
    #include <hidl/Status.h>
    
    namespace vendor {
    namespace mediatek {
    namespace hardware {
    namespace cyhhidl {
    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 CyhHidl : public ICyhHidl {
        // Methods from ::vendor::mediatek::hardware::cyhhidl::V1_0::ICyhHidl follow.
        Return<void> helloWorld(const hidl_string& name, helloWorld_cb _hidl_cb) override;
    
        // Methods from ::android::hidl::base::V1_0::IBase follow.
    
    };
    
    // FIXME: most likely delete, this is only for passthrough implementations
    // extern "C" ICyhHidl* HIDL_FETCH_ICyhHidl(const char* name);
    
    }  // namespace implementation
    }  // namespace V1_0
    }  // namespace cyhhidl
    }  // namespace hardware
    }  // namespace mediatek
    }  // namespace vendor
    
    #endif  // VENDOR_MEDIATEK_HARDWARE_CYHHIDL_V1_0_CYHHIDL_H
    

    1.0/default/CyhHidl.cpp

    #include "CyhHidl.h"
    
    namespace vendor {
    namespace mediatek {
    namespace hardware {
    namespace cyhhidl {
    namespace V1_0 {
    namespace implementation {
    
    // Methods from ::vendor::mediatek::hardware::cyhhidl::V1_0::ICyhHidl follow.
    Return<void> CyhHidl::helloWorld(const hidl_string& name, helloWorld_cb _hidl_cb) {
        // TODO implement
        char buf[100];
        ::memset(buf, 0x00, 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.
    
    //ICyhHidl* HIDL_FETCH_ICyhHidl(const char* /* name */) {
        //return new CyhHidl();
    //}
    //
    }  // namespace implementation
    }  // namespace V1_0
    }  // namespace cyhhidl
    }  // namespace hardware
    }  // namespace mediatek
    }  // namespace vendor
    
    

    之后通过1.0/default/Android.bp文件看出之后会通过这几个文件生成一个vendor.mediatek.hardware.cyhhide@1.0-impl.so的库,在vendor/lib64/hw/目录下。

    1.0/default/Android.bp

    cc_library_shared {
        name: "vendor.mediatek.hardware.cyhhidl@1.0-impl",
        relative_install_path: "hw",
       
        proprietary: true,
        srcs: [
            "CyhHidl.cpp",
        ],
        shared_libs: [
            "libhidlbase",
            "libhidltransport",
            "libutils",
            "vendor.mediatek.hardware.cyhhidl@1.0",
        ],
    
    }
    

    三、调用HAL流程

    HIDL软件包中自动生成的文件会链接到与软件包同名的单个共享库,该共享库还会导出单个头文件ICyhHidl.h,用于在binder客户端服务端的接口文件,下图是官网对IFoo.hal(对应ICyhHidlTest.hal)编译后生成的文件走向:

    image.png
    1. IFoo.h(对应ICyhHidl.h):描述C++类中的IFoo(ICyhHidl)接口;包含IFoo.hal(对应ICyhHidl.hal)文件中的IFoo(ICyhHidl)接口中所定义的方法和类型,必要时会转换成C++类型。不包含与用于实现此接口的RPC机制(例如HwBinder)相关的详细信息。类的命名空间包含软件包名称和版本号(例::android:hardware::samples::IFoo::V1_0)。客户端和服务端都包含此标头:客户端用它来调用方法,服务器用它来实现这些方法;
    2. IHwFoo.h:头文件,其中包含用于对接口中使用的数据类型进行序列化的函数声明。开发者不得包含其标头(它不包含任何类);
      3.BpFoo.h:从IFoo继承的类,可描述接口的HwBinder代理(客户端)实现。开发者不得直接引用此类;
    3. BnFoo.h:保存对IFoo实现的应用类,可描述接口的HwBinder存根(服务器端)实现,开发者不得直接引用此类;
    4. FooAll.cpp:包含HwBinder代理和HwBinder存根的实现类。当客户端调用接口方法时,代理会自动从客户端封装参数,并将事务发送到绑定内核驱动程序,该内核驱动程序会将事务传送到另一端的存根(该存根随后会调用实际服务器实现)。

    独立于HIDL使用的RPC机制的唯一一个自动生成的文件时IFoo.h,其他所有文件都与HIDL使用的HwBinder RPC机制相关联。因此客户端和服务端不得直接引用除IFoo之外的任何内容,所以需要只包含IFoo.h并链接到生成的共享库。

    开发的实例会用到以下的几个模块

    1. vendor.mediatek.hardware.cyhhidl@1.0-impl.so:CyhHidl模块实现端的代码,编译生成,binder server端;
    2. vendor.mediatek.hardware.cyhhidl@1.0.so:cyhHidl模块调用端的代码,binder clinent端;
    3. cyhHidl_hal_service:通过直通式注册binder service,暴露接口给lclient调用;
    4. vendor.mediatek.hardware.cyhhidl@1.0-service.rc:Android natice进程入口

    四、HIDL Service和Client端测试代码实现

    Service端测试代码service.cpp
    1.0/default/service.cpp

    #define LOG_TAG "vendor.mediatek.hardware.cyhhidl@1.0-service"
    #define LOG_NDEBUG 0
    
    #include <log/log.h>
    #include <android-base/logging.h>
    #include <hidl/LegacySupport.h>
    #include <hidl/HidlTransportSupport.h>
    
    #include "CyhHidl.h"
    using vendor::mediatek::hardware::cyhhidl::V1_0::implementation::CyhHidl;
    using android::hardware::configureRpcThreadpool;
    using android::hardware::joinRpcThreadpool;
    
    int main() {
        configureRpcThreadpool(4, true);
    
        CyhHidl cyhhidl;
        auto status = cyhhidl.registerAsService();
        CHECK_EQ(status, android::OK) << "Failed to register cyhhidl HAL implementation";
    
        ALOGD("register cyhhidl HAL success");
    
        joinRpcThreadpool();
        return 0; // joinRpcThreadpool shouldn't exit
    }
    

    Clint端测试代码client.cpp
    1.0/default/client.cpp

    #define LOG_TAG "vendor.mediatek.hardware.cyhhidl@1.0-client"
    
    #include <vendor/mediatek/hardware/cyhhidl/1.0/ICyhHidl.h>
    #include <hidl/LegacySupport.h>
    #include <stdio.h>
    
    // Generated HIDL files
    using vendor::mediatek::hardware::cyhhidl::V1_0::ICyhHidl;
    using android::sp;
    using android::hardware::hidl_string;
    
    int main() {
        android::sp<ICyhHidl> service = ICyhHidl::getService();
    
        if (service == nullptr) {
            printf("Failed get cyhhidl service!\n");
            return -1;
        }
    
        printf("Success get cyhhidl service!\n");
    
        service->helloWorld("Cyh",[&](hidl_string result){
            printf("%s\n",result.c_str());
        });
    
        return 0;
    }
    

    Android.bp文件内加入编译配置
    1.0/default/Android.bp

    cc_binary {
        name: "vendor.mediatek.hardware.cyhhdil@1.0-service",
        relative_install_path: "hw",
        defaults: ["hidl_defaults"],
        init_rc: ["vendor.mediatek.hardware.cyhhidl@1.0-service.rc"],
        srcs: ["service.cpp","CyhHidl.cpp"],
        proprietary: true,
    
        shared_libs: [
             "libbase",
            "liblog",
            "libhardware",
            "libhidlbase",
            "libhidltransport",
            "libutils",
        "vendor.mediatek.hardware.cyhhidl@1.0",
        ],
        cflags: [
            "-Werror",
            "-Wno-unused-parameter",
        ],
    
       
    }
    
    // 添加客户端测试时使用
    cc_binary {
        name: "vendor.mediatek.hardware.cyhhidl@1.0-client",
        relative_install_path: "hw",
        defaults: ["hidl_defaults"],
        srcs: ["client.cpp"],
    
        shared_libs: [
            "liblog",
            "libbase",
            "libdl",
            "libutils",
            "libhardware",
            "libhidlbase",
            "libhidltransport",
            "vendor.mediatek.hardware.cyhhidl@1.0",
        ],
    
    }
    

    五、启动binder server进程
    创建服务的rc文件 ,设定服务开机自启,vendor.mediatek.hardware.cyhhidl@1.0-service.rc,文件内容如下:

    service hal-cyhhidl-1-0 /vendor/bin/hw/vendor.mediatek.hardware.cyhhidl@1.0-service
    class hal
    user system
    group system
    

    设定服务的selinux权限,在device/mediatek/mt2712/sepolicy/non_plat目录下:
    file_contexts文件上下文添加定义:

      /system/bin/hw/android\.hardware\.cyhhidl@1.\0-service u:object_r:hal_cyhhidl_default_exec:s0
    

    hwservice_contexts文件上下文添加定义

      vendor.mediatek.hardware.cyhhidl::ICyhHidl u:object_r:hal_cyhhidl_hwservice:s0
    

    attributes文件上下文添加属性定义

    # hal cyhhidl
    #attribute hal_cyhhidl;
    #attribute hal_cyhhidl_client;
    #attribute hal_cyhhidl_server;
    

    新增hal_cyhhidl.te权限文件

    # HwBinder IPC from client to server, and callbacks
    binder_call(hal_cyhhidl_client, hal_cyhhidl_server);
    binder_call(hal_cyhhidl_server, hal_cyhhidl_client);
    
    add_hwservice(hal_cyhhidl_server, hal_cyhhidl_hwservice);
    allow hal_cyhhidl_client hal_cyhhidl_hwservice:hwservice_manager find;
    allow shell vendor_file:file { getattr };
    allow shell hal_cyhhidl_hwservice:hwservice { find };
    

    新增hal_cyhhidl_default.te默认权限文件

    type hal_cyhhidl_default, domain,coredomain;
    hal_server_domain(hal_cyhhidl_default, hal_cyhhidl)
    
    type hal_cyhhidl_default_exec, exec_type, file_type;
    init_daemon_domain(hal_cyhhidl_default)
    

    添加hidl配置,在device/mediatek/mt2712/manifest.xml和manifest_ab.xml

    <hal format="hidl">
         <name>vendor.mediatek.hardware.cyhhidl</name>
         <transport>hwbinder</transport>
         <version>1.0</version>
         <interface>
             <name>ICyhHidl</name>
             <instance>default</instance>
         </interface>
    </hal>
    

    device.mk配置添加编译服务端

    PRODUCT_PACKAGES += \
        vendor.mediatek.hardware.cyhhidl@1.0-service
    

    六、运行结果

    开机执行

    adb shell
    ps -A | grep cyhhidl
    

    输入

    su
    ./vendor/bin/hw/vendor.mediatek.hardware.cyhhidl@1.0-client
    

    执行客户端程序,显示如下:

    过程中遇到的问题

    VNDK验证问题

    VNDK只针对hardware下生成的so库,在vendor下开发的HIDL不需要

    编译时在设置SELinux权限时报错

    libsepol.report_failure: neverallow on line 1016 of system/sepolicy/public/domain.te (or line 11338 of policy.conf) violated by allow hal_cyhhidl_default hal_cyhhidl_default_exec:file { execute };
    libsepol.check_assertions: 1 neverallow failures occurred
    

    这是hal_cyhhidl_default.te文件内部的语法错误导致,之后修改后就没有再出现。

    服务编译不过,报出错误error: undefined reference to 'VTT for android::hardware:xxxx

    原因在于我的service.cpp引入了CyhHidl虚函数,但是没有引入对应的资源文件,

    所以之后在Android.bp文件内对应的srcs改为
    srcs: ["service.cpp","CyhHidl.cpp"],相当于把implservice整合,其实就不需要impl了,可以去除;

    之后会有error: unused parameter '_hidl_cb' [-Werror,-Wunused-parameter]错误,在Android.bp内加入

        cflags: [
            "-Werror",
            "-Wno-unused-parameter",
        ],
    

    则成功编译通过

    服务开机没有自启动

    发现生成的文件是在vendor/bin/hw目录下,而rc文件内写成system/bin/hw下,这个生成目录位置要研究一下,修改下rc文件启动的文件目录即可启动service

    修改rc文件后开机没有自启

    最后发现定义属性的时候要在/device/mediatek/mt2712/sepolicy/plat_public/attributes文件下,之前是在/device/mediatek/mt2712/sepolicy/non_plat/attributes,应该HIDL服务在vendorhardware开发的区别,之后有待验证

    再次编译后烧录,之后就可以看到服务正在运行

    相关文章

      网友评论

          本文标题:一个简单的HIDL开发笔记

          本文链接:https://www.haomeiwen.com/subject/iqjuvltx.html