AndroidQ 打通应用层到HAL层---(HIDL服务实现)

AndroidQ 打通应用层到HAL层---(HIDL服务实现),第1张

概述AndroidQ打通应用层到HAL层—(HAL模块实现)这篇文章中我们已经实现了自己的HAL,本篇我们实现一个HIDL服务,通过这个服务来调用HAL模块的函数什么是HIDLHIDL全称为HALinterfacedefinitionlanguage(发音为“hide-l”)是用于指定HAL和其用户之间的接口的一种接口描述语言

AndroidQ 打通应用层到HAL层—(HAL模块实现)这篇文章中我们已经实现了自己的HAL,本篇我们实现一个HIDL服务,通过这个服务来调用HAL模块的函数

@L_403_1@什么是HIDL

HIDL 全称为HAL interface deFinition language(发音为“hIDe-l”)是用于指定 HAL 和其用户之间的接口的一种接口描述语言 (IDL),AndroID O开始引入了HIDL这个概念,HIDL和应用层AIDL差不多,AIDL常用于连接App和Framework,HIDL则是用来连接Framework和HAL,AIDL使用Binder通信,HIDL则使用HwBinder通信,他们都是通过Binder驱动完成通信,只不过两个Binder域不一样

为什么需要HIDL

目前AndroID系统生态是几乎每年Google都会出一个AndroID大版本,而普通手机用户一部手机一般要用两三年,所以你会发现尽管AndroID系统已经升级到了10,马上11出来了,然后还是有很多用户依然使用的是AndroID 5,6,7等版本,对普通用户来说如果不更换手机就很难跟上AndroID版本,这是因为OEM厂商在同一设备上进行系统升级需要花费时间金钱成本很高,导致他们不愿意升级,成本高的原因是AndroID O之前AndroID Framework的升级需要OEM将HAL也进行对应升级,Framework和HAL是一起被编译成system.img,它们存在高耦合,针对这种情况Google在AndroID O中引入了Treble计划,Treble的目的就是解耦Framework和HAL,就是通过HIDL来实现,Framework不再直接调用HAL,而是通过HIDL来间接使用HAL模块,每个HAL模块都可以对应一个HIDL服务,Framework层通过HwBinder创建HIDL服务,通过HIDL服务来获取HAL相关模块继而打开HAL下的设备,而最终HAL也从system.img中分离,被编进一个单独的分区vendor.img,从而简化了AndroID系统升级的影响与难度

HIDL的使用

HIDL可以分为:HIDL C++(C++实现)、HIDL Java(Java 实现),并且还主要分为直通式和绑定式,本篇文章使用的C++和直通式的HIDL,HIDL用起来非常简单,AOSP的harDWare/interfaces/目录下有很多的HIDL,我们仿照其他HIDL创建自己的HIDL目录:harDWare/interfaces/hello_hIDl/1.0

并在此目录下创建一个IHello.hal文件:

package [email protected];interface IHello {    addition_hIDl(uint32_t a,uint32_t b) generates (uint32_t total);    };

这个文件定义了一个addition_hIDl函数,这个函数用来调用HAL的加法函数

然后就可以使用AndroID提供的工具hIDl-gen来生成HIDL框架,执行如下命令:

 [email protected] LOC=harDWare/interfaces/hello_hIDl/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/hello_hIDl/1.0下多了一个default目录,进入default目录,里面有三个文件AndroID.bp,Hello.cpp,Hello.h


之后再在执行./harDWare/interfaces/update-makefiles.sh这个命令,update-makefiles.sh这个脚本目的是为HIDL生成对应AndroID.bp文件

最后目录结构为:

接着我们还需要在default目录下增加一个空文件service.cpp,用作注册HIDL服务,我们采用直通式的HIDL,所以service.cpp的内容为:

#include <androID/harDWare/hello_hIDl/1.0/IHello.h>#include <hIDl/LegacySupport.h>// Generated HIDL filesusing androID::harDWare::hello_hIDl::V1_0::IHello;using androID::harDWare::defaultPassthroughServiceImplementation;int main() {    return defaultPassthroughServiceImplementation<IHello>();}

defaultPassthroughServiceImplementation函数最终会向HwServiceManager注册HIDL服务

接着我们来看看之前生成的文件,首先看Hello.h

// FIXME: your file license if you have one#pragma once#include <androID/harDWare/hello_hIDl/1.0/IHello.h>#include <hIDl/MQDescriptor.h>#include <hIDl/Status.h>namespace androID {namespace harDWare {namespace hello_hIDl {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 Hello : public IHello {    // Methods from ::androID::harDWare::hello_hIDl::V1_0::IHello follow.    Return<uint32_t> addition_hIDl(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" IHello* HIDL_FETCH_IHello(const char* name);}  // namespace implementation}  // namespace V1_0}  // namespace hello_hIDl}  // namespace harDWare}  // namespace androID

系统自动生成了Hello结构体(当然也可以自己改为class),继承IHello接口,addition_hIDl函数就需要在Hello.cpp中去实现了,因为我们采用直通式HIDL,所以需要将// extern “C” IHello* HIDL_FETCH_IHello(const char* name);的注释去掉

接着来看看Hello.cpp:

// FIXME: your file license if you have one#include "Hello.h"namespace androID {namespace harDWare {namespace hello_hIDl {namespace V1_0 {namespace implementation {// Methods from ::androID::harDWare::hello_hIDl::V1_0::IHello follow.Return<uint32_t> Hello::addition_hIDl(uint32_t a, uint32_t b) {    // Todo implement    return uint32_t {};}// Methods from ::androID::hIDl::base::V1_0::IBase follow.IHello* HIDL_FETCH_IHello(const char* /* name */) {    return new Hello();}}  // namespace implementation}  // namespace V1_0}  // namespace hello_hIDl}  // namespace harDWare}  // namespace androID

同样需要去掉HIDL_FETCH_IHello函数的注释,采用直通式HIDL时,通过前面service.cpp中的defaultPassthroughServiceImplementation函数注册HIDL服务时,内部原理就是通过“HIDL_FETCH_”字串拼接defaultPassthroughServiceImplementation传递的IHello,找到HIDL_FETCH_IHello函数并获取IHello对象,我们可以看到HIDL_FETCH_IHello初始代码就是创建了一个Hello对象

在接着看default目录下的AndroID.bp:

cc_library_shared {    name: "[email protected]",    relative_install_path: "hw",    proprIEtary: true,    srcs: [        "Hello.cpp",    ],    shared_libs: [        "libhIDlbase",        "libhIDltransport",        "libutils",        "[email protected]",    ],}

这个AndroID.bp会将Hello这个HIDL服务编译成一个[email protected],它还依赖一个[email protected],这个so哪来的呢?

再接着看1.0目录下的AndroID.bp:

// This file is autogenerated by hIDl-gen -LandroIDbp.hIDl_interface {    name: "[email protected]",    root: "androID.harDWare",    vndk: {        enabled: true,    },    srcs: [        "IHello.hal",    ],    interfaces: [        "[email protected]",    ],    gen_java: true,}

这个AndroID.bp会将harDWare/interfaces/hello_hIDl/1.0这个HIDL编译成一个[email protected],到这里我们发现service.cpp没有用到,所以我们还需要修改default目录下的AndroID.bp:

// FIXME: your file license if you have onecc_library_shared {    name: "[email protected]",    relative_install_path: "hw",    proprIEtary: true,    srcs: [        "Hello.cpp",    ],    shared_libs: [        "libhIDlbase",        "libhIDltransport",        "libutils",        "liblog",        "libharDWare",        "[email protected]",    ],}cc_binary {    name: "[email protected]",    defaults: ["hIDl_defaults"],    relative_install_path: "hw",    vendor: true,    srcs: ["service.cpp"],    shared_libs: [        "[email protected]",        "libharDWare",        "libhIDlbase",        "libhIDltransport",        "libutils",        "liblog",    ],}

新增加对service.cpp的编译,我们将service.cpp编译成一个二进制可执行文件[email protected],用来启动HIDL服务,好了,最终我们这个HIDL会编译出来如下三个so:
[email protected]
[email protected]
[email protected]

还有一点需要注意的是,这个HIDL想要被Framework获取使用还需要在manifest.xml中注册,
manifest.xml在手机vendor/etc/vintf/manifest.xml下,我们将这个文件pull出来然后添加如下代码:

   <hal format="hIDl">        <name>androID.harDWare.hello_hIDl</name>        <transport>hwbinder</transport>        <version>1.0</version>        <interface>            <name>IHello</name>            <instance>default</instance>        </interface>        <fqname>@1.0::IHello/default</fqname>    </hal>

然后在Hello.cpp中添加一行log,之后进行编译

IHello* HIDL_FETCH_IHello(const char* /* name */) {    ALOGE("hello_hIDl service is init success....");        return new Hello();}

执行mmm harDWare/interfaces/hello_hIDl/1.0/


编译成功后我们将生成的三个so分别push到手机vendor/lib64/hw/,vendor/lib64/,vendor/bin/hw/目录下

adb push vendor/lib64/hw/[email protected] vendor/lib64/hw/

adb push system/lib64/[email protected] vendor/lib64/

adb push vendor/bin/hw/[email protected] vendor/bin/hw/

接着我们到手机vendor/bin/hw/目录下去执行[email protected]这个二进制可执行文件,这个文件就会执行service.cpp的代码,调用defaultPassthroughServiceImplementation注册我们的HIDL服务

再看看log输出:


在执行[email protected]时就会输入这句log,代表我们这个HIDL服务已经实现,其实通常的HIDL服务都是通过rc文件来开机启动的,我这里为了方便演示就没有写

再执行adb shell ps -A|grep -i --color "hello_hIDl"命令看下这个服务状态
我们发现HIDL服务启动之后就会一直在后台,这个其实和AMS,WMS这种服务是类似的,启动之后在后台会等待clIEnt端访问

@H_404_143@

HIDL这个服务已经能够正常启动了,接着写一个测试程序看能否获取这个服务,并且调用该服务的函数,我在Hello.cpp的addition_hIDl函数中添加了一句log:

Return<uint32_t> Hello::addition_hIDl(uint32_t a, uint32_t b) {    // Todo implement    ALOGD("dongjiao...Hello::addition_hIDl a = %d,b = %d",a,b);    return uint32_t {};}

测试程序写在harDWare/interfaces/hello_hIDl/1.0/default目录下:


Hello_hIDl_test.cpp:

#include <androID/harDWare/hello_hIDl/1.0/IHello.h>#include <hIDl/LegacySupport.h> #include <log/log.h>using androID::sp;using androID::harDWare::hello_hIDl::V1_0::IHello;using androID::harDWare::Return;int main(){    androID::sp<IHello> hw_device = IHello::getService();    if (hw_device == nullptr) {              ALOGD("dongjiao...Failed to get hello-hIDl");              return -1;        }    ALOGD("dongjiao...success to get hello-hIDl....");    Return<uint32_t> total = hw_device->addition_hIDl(3,4);    return 0;}

测试程序代码也比较简单,获取IHello的服务,然后调用addition_hIDl函数

看一下AndroID.bp:

cc_binary {    name: "Hello_hIDl_test",    srcs: ["Hello_hIDl_test.cpp"],    shared_libs: [        "liblog",        "[email protected]",        "libhIDlbase",        "libhIDltransport",        "libhwbinder",        "libutils",    ],}

我们再编译这个测试程序,它会被编译成一个可执行二进制文件Hello_hIDl_test
编译命令:
mmm harDWare/interfaces/hello_hIDl/1.0/default/test/


编译成功了,将这个可执行文件push到手机/system/bin/目录下

在执行Hello_hIDl_test之前别忘了把HIDL服务启动起来


接着执行Hello_hIDl_test


然后看log输出


可以看到成功了,成功获取到了HIDL服务并且调用了该服务的addition_hIDl函数,将我们的参数传递了过去,实际开发中就可以在获取HIDL服务时打开HAL模块,然后可以直接调用HAL的函数,上一篇文章其实也写了测试程序测自定义的HAL模块,我们只需要将上一篇文章中的测试程序中的代码copy到HIDL初始化代码中就能够调用HAL的那个加法函数了,具体就不测试了

到这里HIDL服务已经成功实现并且能够正常使用,HIDL这个框架用起来还是比较简单的,大部分代码都是通过工具生成的

下一篇文章我将在native层定义一个JNI服务,然后在JNI服务中调用HIDL服务定义的addition_hIDl函数

总结

以上是内存溢出为你收集整理的AndroidQ 打通应用层到HAL层---(HIDL服务实现)全部内容,希望文章能够帮你解决AndroidQ 打通应用层到HAL层---(HIDL服务实现)所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://www.outofmemory.cn/web/1062103.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-25
下一篇 2022-05-25

发表评论

登录后才能评论

评论列表(0条)

保存