美文网首页
Android HIDL服务实现-(Binderized Mod

Android HIDL服务实现-(Binderized Mod

作者: 猫咪不吃鱼 | 来源:发表于2022-04-05 14:55 被阅读0次

    一、前言

    正如 Android HIDL 概述 一文中简单的对 HIDL 的演进和新架构下 Framework 与 Hal 层之间的通信做了介绍。但是笔者的目的是想完整的实现从上层 APP 到 hal 之间通信过程,由此可以更加深刻的理解这种机制。

    二、Binderized Mode (绑定式)简介

    从上文介绍,我们知道 绑定模式 是 google 为了向前兼容而定义的一种类型,且 Android 8.0 及后续版本的设备都必须只支持这种模式。这种模式下 Framework 与 Hal 分别位于不同的进程中,其实从具体实现来讲这种模式也更应该被称为 Binder 化的直通式。本文将通过这种方式实现一个 HIDL 服务。

    三、环境/工具准备

    • Ubuntu 20.04 TLS
    • Android 源码:Android 9.0,编译烧录详见 Android源码编译烧录
    • hidl-gen 工具:Android 系统自带,需要配置一下环境变量

    四、HIDL 实现

    本文目的是实现一个具有 加减乘除 运算的 HIDL 服务,命名为 银河一号(GalaxyOne)。HIDL用起来非常简单,在系统源码中的 hardware/interfaces 目录下有很多的 HIDL,我们仿照其他 HIDL 来创建自己的目录:hardware/interfaces/galaxy_one/1.0

    4.1 创建 IGalaxyOne.hal 文件

    hardware/interfaces/galaxy_one/1.0/IGalaxyOne.hal

    这里定义了四种基本的运算:加、减、乘、除,这是上层调用 HAL 的入口,内容如下:

    package android.hardware.galaxy_one@1.0;
    
    interface IGalaxyOne{
    
        //加法
        add(uint32_t a,uint32_t b) generates (uint32_t result);
        //减法
        sub(uint32_t a,uint32_t b) generates (uint32_t result);
        //乘法
        mul(uint32_t a,uint32_t b) generates (uint32_t result);
        //除法
        div(uint32_t a,uint32_t b) generates (uint32_t result);
        
    };
    

    4.2 hidl-gen 生成 HIDL 框架

    在使用 hidl-gen 之前需要先做两件事:
    1、hidl-gen 由 Android 提供,使用之前需要先配置一下系统路径,如我这里所做的:

    # vim ~/.bashrc
    export PATH=/home/zsk/AOSP/out/soong/host/linux-x86/bin:$PATH
    

    2、Ubuntu 新的终端窗口必须先设定一些 Android 环境变量:

    source build/envsetup.sh
    lunch aosp_sailfish-userdebug  // lunch mode 根据需求修改
    make hidl-gen
    

    配置完成之后在 源码根目录 下执行如下命令:

      PACKAGE=android.hardware.galaxy_one@1.0
      LOC=hardware/interfaces/galaxy_one/1.0/default/
      hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
      hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
    

    命令执行成功之后会发现在 hardware/interfaces/galaxy_one/1.0 目录下多了一个 default 目录,进入之后发现有如下文件:

    之后执行 update-makefiles.sh 脚本来为 HIDL 生成对应的 Android.bp 文件,此脚本位于 hardware/interfaces 目录下,同样可在源码根目录下执行:

    ./hardware/interfaces/update-makefiles.sh
    

    接下来我们需要添加两个空文件:

    touch hardware/interfaces/galaxy_one/1.0/default/android.hardware.galaxy_one@1.0-service.rc
    touch hardware/interfaces/galaxy_one/1.0/default/service.cpp
    
    

    完成之后,整个工程结构如下所示:


    4.3 调用流程

    上述过程已经将 HIDL 服务所需要的全本文件配置完成,虽然其中很多文件是空的,或者没有具体实现,我们现在先放在一边,先来对整体的调用流程及各个文件的功效略作说明。

    Binder 化直通式
    • Application:指上层应用
    • JNI:指 framework 层,getService 获取 hal 层 service
    • android.hardware.galaxy_one@1.0.so:由 IGalaxyOne.hal 生成的接口库,由 hardware/interfaces/galaxy_one/1.0/Android.bp 通过 IGalaxyOne.hal 生成,这样只要这个接口库不变,那么 framework 的更新和 hal 层就隔绝开了
    • android.hardware.galaxy_one@1.0-service.rc:设备开机时通过 rc 文件启动此服务
    • galaxy_hal_service:service的名,可通过国 start galaxy_hal_service 启动服务
    • android.hardware.galaxy_one@1.0-impl.so:实现库,上层应用的最终调用

    关于 Application、JNI 这两层内容会在稍后用两个篇幅去分析,此处暂不理会。现在我们就着这个调用过程将需要的内容补充完成。

    4.3.1 接口库生成

    android.hardware.galaxy_one@1.0.so,由 hardware/interfaces/galaxy_one/1.0/Android.bp 通过 IGalaxyOne.hal 生成,Android.bp 文件是在上面一些列命令执行之后生成,而接口库是当我们最终执行编译模块时生成,可以说这个过程不需要我们手动参与,Android.bp 内容如下:

    // This file is autogenerated by hidl-gen -Landroidbp.
    
    hidl_interface {
        name: "android.hardware.galaxy_one@1.0",   //此处设置接口库的名字
        root: "android.hardware",
        vndk: {
            enabled: true,
        },
        srcs: [
            "IGalaxyOne.hal",
        ],
        interfaces: [
            "android.hidl.base@1.0",
        ],
        gen_java: true,
    }
    
    
    4.3.2 实现库生成

    android.hardware.galaxy_one@1.0-impl.so,由 hardware/interfaces/galaxy_one/1.0/default/Android.bp 通过 GalaxyOne.cpp 生成,注意这个 Android.bp 文件是位于 default 目录下,同样的在最后模块编译时生成,原始内容如下:

    cc_library_shared {
        name: "android.hardware.galaxy_one@1.0-impl",
        relative_install_path: "hw",
        proprietary: true,
        srcs: [
            "GalaxyOne.cpp",
        ],
        shared_libs: [    //这里可以添加我们需要的库
            "liblog",     
            "libhidlbase",
            "libhidltransport",
            "libhwbinder",
            "libutils",
            "android.hardware.galaxy_one@1.0",
        ],
    }
    
    4.3.3 GalaxyOne.cpp 实现

    4.3.2 中,实现库是由 GalaxyOne.cpp 编译而成,现在我们来将此文件补充完成:

    GalaxyOne.h:
    Binder化直通式,同样需要将 HIDL_FETCH_XXX 打开,至于原因我们在后面会提及

    #ifndef ANDROID_HARDWARE_GALAXY_ONE_V1_0_GALAXYONE_H
    #define ANDROID_HARDWARE_GALAXY_ONE_V1_0_GALAXYONE_H
    
    #include <android/hardware/galaxy_one/1.0/IGalaxyOne.h>
    #include <hidl/MQDescriptor.h>
    #include <hidl/Status.h>
    #include <log/log.h>
    
    namespace android {
    namespace hardware {
    namespace galaxy_one {
    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 GalaxyOne : public IGalaxyOne {
        // Methods from ::android::hardware::galaxy_one::V1_0::IGalaxyOne follow.
        Return<uint32_t> add(uint32_t a, uint32_t b) override;
        Return<uint32_t> sub(uint32_t a, uint32_t b) override;
        Return<uint32_t> mul(uint32_t a, uint32_t b) override;
        Return<uint32_t> div(uint32_t a, uint32_t b) override;
    
        // Methods from ::android::hidl::base::V1_0::IBase follow.
    
    };
    
    // FIXME: most likely delete, this is only for passthrough implementations
     extern "C" IGalaxyOne* HIDL_FETCH_IGalaxyOne(const char* name);
    
    }  // namespace implementation
    }  // namespace V1_0
    }  // namespace galaxy_one
    }  // namespace hardware
    }  // namespace android
    
    #endif  // ANDROID_HARDWARE_GALAXY_ONE_V1_0_GALAXYONE_H
    
    

    GalaxyOne.cpp:

    #include "GalaxyOne.h"
    
    namespace android {
    namespace hardware {
    namespace galaxy_one {
    namespace V1_0 {
    namespace implementation {
    
    // Methods from ::android::hardware::galaxy_one::V1_0::IGalaxyOne follow.
    Return<uint32_t> GalaxyOne::add(uint32_t a, uint32_t b) {
        uint32_t result = a + b;
        ALOGE("GalaxyOne::add  a = %d,b = %d,result = %d",a,b,result);
        return result;
    }
    
    Return<uint32_t> GalaxyOne::sub(uint32_t a, uint32_t b) {
        uint32_t result = a - b;
        ALOGE("GalaxyOne::sub  a = %d,b = %d,result = %d",a,b,result);
        return result;
    }
    
    Return<uint32_t> GalaxyOne::mul(uint32_t a, uint32_t b) {
        uint32_t result = a * b;
        ALOGE("GalaxyOne::mul  a = %d,b = %d,result = %d",a,b,result);
        return result;
    }
    
    Return<uint32_t> GalaxyOne::div(uint32_t a, uint32_t b) {
        uint32_t result = a / b;
        ALOGE("GalaxyOne::div  a = %d,b = %d,result = %d",a,b,result);
        return result;
    }
    
    // Methods from ::android::hidl::base::V1_0::IBase follow.
    IGalaxyOne* HIDL_FETCH_IGalaxyOne(const char* /* name */) {
        ALOG("galaxy_one service init success....");
        return new GalaxyOne();
    }
    
    }  // namespace implementation
    }  // namespace V1_0
    }  // namespace galaxy_one
    }  // namespace hardware
    }  // namespace android
    

    4.3.3 模块编译

    现在除了需要的 rc 文件没有补充、galaxy-hal-service 服务没有生成外其余均已配置好了,现在进行编译生成对应的库。进入根目录下执行如下命令:(注意是在刚刚执行过的 source build/envsetup.shlunch 的窗口下编译,若是新窗口则需要重新执行这两条命令)

    mmm  hardware/interfaces/galaxy_one/1.0
    

    此时应该可以在 out/tartget/product/XXX/vendor/lib64/hwout/tartget/product/XXX/system/lib64/hw 目录下找到 android.hardware.galaxy_one@1.0.soandroid.hardware.galaxy_one@1.0-impl.so 两个动态库

    4.3.4 serice 生成

    上面过程将需要的动态库生成完毕,接下来我们需要生成对应的 service 可执行文件,这个过程一共分为三步:

    1、修改 /default 下的 Android.bp 文件内容

    cc_library {
        name: "android.hardware.galaxy_one@1.0-service",
        defaults: ["hidl_defaults"],
        relative_install_path: "hw",
        vendor: true,
        srcs: [
            "service.cpp"
        ],
        init_rc: ["android.hardware.galaxy_one@1.0-service.rc"],
        shared_libs: [
            "liblog",
            "libhidlbase",
            "libhidltransport",
            "libhwbinder",
            "libutils",
            "android.hardware.galaxy_one@1.0",
        ],
    }
    

    2、补充 service.cpp 内容

    内容很简单,defaultPassthroughServiceImplementation 帮我们自动注册服务:

    #define LOG_TAG "android.hardware.galaxy_one@1.0-service"
     
    #include <android/hardware/galaxy_one/1.0/IGalaxyOne.h>
    #include <hidl/LegacySupport.h>
    #include "GalaxyOne.h"
     
    // Generated HIDL files
    using android::hardware::galaxy_one::V1_0::IGalaxyOne;
    using android::hardware::galaxy_one::V1_0::implementation::GalaxyOne;
     
    using android::hardware::defaultPassthroughServiceImplementation;
    using android::hardware::configureRpcThreadpool;
    using android::hardware::joinRpcThreadpool;
     
    int main() {
        return defaultPassthroughServiceImplementation<IGalaxyOne>();
    } 
    

    3、补充 rc 文件

    注意这里的 galaxy-hal-service 相当于这个服务的别名,系统就是根据这个文件在启动的同时也将这个 service 启动,因此在下面我们手动启动测试时没有什么作用,不过这里先补充完整。

    service galaxy-hal-service /vendor/bin/hw/android.hardware.galaxy_one@1.0-service
        class hal
        user system
        group system
    

    同样执行 mmm hardware/interfaces/galaxy_one/1.0 命令,完成之后就会得到如下二进制可执行文件:

    out/target/product/sailfish/vendor/bin/hw/android.hardware.galaxy_one@1.0-service
    

    4.4 client 端

    经过一系列过程之后,我们得到了三个产物
    1、android.hardware.galaxy_one@1.0.so
    2、android.hardware.galaxy_one@1.0-impl.so
    3、android.hardware.galaxy_one@1.0-service

    现在需要模拟一个客户端来测试调用,因此在 default 目录下新建 test 目录,并新建 client.cpp、Android.bp 文件,具体结构如下:

    client.cpp 内容如下:

    #include <android/hardware/galaxy_one/1.0/IGalaxyOne.h>
    #include <hidl/Status.h>
    #include <log/log.h>
    
    using android::sp;
    using android::hardware::galaxy_one::V1_0::IGalaxyOne;
    using android::hardware::Return;
    
    int main(){
        android::sp<IGalaxyOne> service = IGalaxyOne::getService();
        if (service == nullptr) {
            ALOGD("faile to get galaxy_one service......");
            return -1;
        }
        ALOGE("success to get galaxy_one service.....");
    
        uint32_t addResult = service->add(3,4);
        ALOGE("galaxy_one service add: result = %d",(int)addResult);
    
        uint32_t subResult = service->sub(8,3);
        ALOGE("galaxy_one service sub: result = %d",(int)subResult);
    
        uint32_t mulResult = service->mul(3,8);
        ALOGE("galaxy_one service mul: result = %d",(int)mulResult);
    
        uint32_t divResult = service->div(8,2);
        ALOGE("galaxy_one service div: result = %d",(int)divResult);
    
        return 0;
    }
    

    Android.bp 内容如下:

    cc_binary {
        name: "galaxy_test",    //表示生成的 client 名称
        srcs: [
            "client.cpp"
        ],
        shared_libs: [
            "liblog",
            "android.hardware.galaxy_one@1.0",
            "libhidlbase",
            "libhidltransport",
            "libhwbinder",
            "libutils",
        ],
    }
    

    mmm hardware/interfaces/galaxy_one/1.0 命令编译之后,可以在 out/target/product/XXX/system/bin 目录下找到 galaxy_test

    五、验证服务

    5.1 push 设备

    现在我们一共得到 4 个产物,使用 adb 命令将其 push 到手机对应目录下:

    1、android.hardware.galaxy_one@1.0-impl.so ===> /vendor/lib64/hw
    2、android.hardware.galaxy_one@1.0.so ===> vendor/lib64
    3、android.hardware.galaxy_one@1.0-service ===> /vendor/bin/hw
    4、galaxy_test ===> /system/bin

    5.2 修改设备 manifest.xml

    HIDL 想要被 framework 获取使用还需要在 manifest.xml 中注册,此在手机 vendor/etc/vintf 下,我们将这个文件 pull 出来添加如下代码之后再 push 原来位置:

        <hal format="hidl">
            <name>android.hardware.galaxy_one</name>
            <transport>hwbinder</transport>
            <version>1.0</version>
            <interface>
                <name>IGalaxyOne</name>
                <instance>default</instance>
            </interface>
            <fqname>@1.0::IGalaxyOne/default</fqname>
        </hal>
    

    5.3 运行 service

    这里我们手动启动,用于测试

    ./vendor/bin/hw/android.hardware.galaxy_one@1.0-service
    

    5.4 运行 client

    ./system/bin/galaxy_test
    

    运行成功之后可查看日志,有如下内容则表示服务建立成功:


    相关文章

      网友评论

          本文标题:Android HIDL服务实现-(Binderized Mod

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