目录
1. HIDL 概念
1.1. Hidl 的简单介绍
HIDL的全称是HAL interface definition language(硬件抽象层接口定义语言),是Android
Framework 与Android HAL之间的接口。HIDL 旨在用于进程间通信 (IPC),进程之间的通信
采用 Binder 机制。
1.2. Hidl 的设计目的
HIDL 的目标是,可以在无需重新构建 HAL 的情况下替换框架。HAL 将由供应商或 SOC
制造商构建,并放置在设备的 /vendor 分区中,这样一来,就可以在框架自己的分区中通过
OTA 替换框架,而无需重新编译 HAL。
HIDL 设计在以下方面之间保持了平衡:
• 互操作性。在可以使用各种架构、工具链和编译配置来编译的进程之间创建可互操作的
可靠接口。HIDL 接口是分版本的,发布后不得再进行更改。
• 效率。HIDL 会尝试尽可能减少复制操作的次数。HIDL 定义的数据以 C++ 标准布局数
据结构传递至 C++ 代码,无需解压,可直接使用。此外,HIDL 还提供共享内存接
口;由于 RPC 本身有点慢,因此 HIDL 支持两种无需使用 RPC 调用的数据传输方
法:共享内存和快速消息队列 (FMQ)。
• 直观。通过仅针对 RPC 使用 in 参数,HIDL 避开了内存所有权这一棘手问题:无法通
过相应方法高效返回的值将通过回调函数返回。无论是将数据传递到 HIDL 中以进行传
输,还是从 HIDL 接收数据,都不会改变数据的所有权,也就是说,数据所有权始终属
于调用函数。数据仅需要在函数被调用期间保留,可在被调用的函数返回数据后立即清
除。
1.3. Hidl 与 Aidl 的对比
2 . HIDL 类型
2.1 Passthrough
兼容之前的 HAL 使用方式(在同一个进程)。要将运行早期版本的 Android 的设备更新为
使用 Android O,您可以将惯用的(和旧版)HAL 封装在一个新 HIDL 接口中,该接口将在绑
定式模式和同进程(直通)模式提供 HAL。这种封装对于 HAL 和 Android 框架来说都是透明
的。
直通模式仅适用于 C++ 客户端和实现。运行早期版本的 Android 的设备没有用 Java 编
写的 HAL,因此 Java HAL 自然而然经过 Binder 化。
2.2 Binderized
使用 Binder 方式进行 IPC(在不同进程)。在使用 HIDL 的时候需要有两个软件包,一个
是 FQName-impl,一个是 FQName-service。FQName-impl 一般是 HAL 实现的部分或者是链接
HAL 的部分,FQName-service 就是 service 端。
3. HIDL 服务的实现
3.1 hidl_gen 工具路径
代码目录:system/tools/hidl
在使用的时候可以直接使用 out/host/linux-x86/bin/hidl-gen 或者使用:
source build/envsetup.sh
lunch m7332-userdebug
lunch 之后可以直接使用 hidl-gen,因为这个时候已经将 bin 的目录添加到了环境变量中了
可以使用 hidl-gen -h 查看
3.2 update-makefiles.sh
通过/hardware/interfaces/update-makefiles.sh 可以创建编译 HIDL 文件的 Android.bp
假设我们创建一个 helloworld 的模块,在 hardware/interfaces 下创建 helloworld/1.0/IHelloWorld.hal:
通过 update-makefiles.sh 就可以在对应 package 的目录下创建 Android.bp:
chenzhaohao@ubuntu:~/9632-1/public-mtk9632/hardware/interfaces$ ./update-makefiles.sh
…….
Updating android.hardware.helloworld@1.0
…….
name:FQName 的全名
root:定义好的 package root name
interfaces:编译过程中依赖的接口名称,如 c 中的 shared library
gen_java:是否编译为 Java 使用的接口
当然,还有其他的参数,例如 gen_java_constants 设为 true 的时候会生成为 Java 使用的 Constants 类。
可以根据实际情况修改该 Android.bp
3.3 update-files.sh
IHelloWorld.hal 和对应的 Android.bp 创建好后,就可以根据需要实现 FQName-impl 和 FQName-service 所
需要的文件,而这些文件也是通过 hidl-gen 创建的,就是下面这个脚本。
update-files.sh 文件路径 移动到源码根目录下
执行
chenzhaohao@ubuntu:~/9632-1/public-mtk9632/$ ./update-files.sh
执行完之后自动生成了 default 目录
创建后的 Android.bp 如下:
可以根据特殊的需要进行修改,例如,vendor 需要设为 true,这样编译出来的 so 位于 vendor 下面,而不是 system。也可以使用同样的方式为 FQName-service 创建对应的规则。
3.4 HIDL 生成的文件
在编译 HIDL 文件,会在 out/soong/.interfaces/PACKAGE/MOUDLE/VERSION/下生成对应的文件。例如helloworld 是在 hardware/interfaces 下创建,所以生成的文件路径为:
out/soong/.intermediates/hardware/interfaces/helloworld/1.0
其中:
android.hardware.helloworld@1.0 就是模块对应的库文件;
android.hardware.helloworld@1.0_genc++ 为生成对应的 C++临时文件,在使用的时候都是链接到这里;
android.hardware.helloworld@1.0_genc++/gen/android/hardware/helloworld/1.0/HelloWorldAll.cpp
android.hardware.helloworld@1.0_genc++_headers 为生成的 C++ 所需的头文件;
android.hardware.helloworld-V1.0-java 为 java 代码所使用的 java 库文件;
android.hardware.helloworld-V1.0-java_gen_java 为 java 代码所使用的 java 文件
3.5 service 和 impl 关系
当 IHelloworld.hal 创建完成就可以创建对应的 HIDL 实现代码(impl 和 service),而 hidl-gen 也提供了默认提供了生成的方式。
Impl:
service.端的 service.cpp 需要手动去编写,可以拷贝系统已存在的过来进行修改,比较简单
3.6 添加 rc 文件
在实现了 serivce 和 impl 代码后需要添加 rc 文件,文件名为 android.hardware.helloworld@1.0-
service.rc:
3.7 启动 service
在 xml 中配置完成之后就直接 start;另外需要设置 selinux 中添加 te 文件,设置 domain 信息。对于selinux 配置,这里暂不分析
3.8 实现 client 端
客户端的文件直接使用 java 文件引用编译生成的 java 静态库 android.hardware.helloworld-V1.0-java然后 IHelloWorld.getService(true); 来获取 HIDL 服务。这样就直接跳过 java – jni ---c++
接着可以直接在 HelloWorld.cpp 中引用 hal 层编译生成的 so 文件来操作硬件外设。
4 . 新旧软件架构对比
4.1 Android 8.0 之前架构图
以 Android 原生自带的 Led 硬件访问服务为例子
这里的难度是在 binder 通讯那块,如果使用的是 Android 原生的就 不需要重新写。但是想要开发自己的中间件,就必须实现要去实现自己的 binder 驱动通讯。
4.2 binder 驱动通讯的实现
采用旧架构进行中间件的设计和开发架构图如下,这里忽略 app 到中间件的的调用流程,这个流程和HIDL 的一样。流程直接从 native 开始往下走。
从上面的架构流程图可以看出,使用老技术开发操作硬件外设是非常的复杂和繁琐。涉及到的文件和知识点众多,无论是前期开发或者是后期的维护难度是非常大。因此,在 Android 8.0 之后的版本,采用了 HIDL进行软件架构重构,弃用之前的方法。HIDL 加快了相应速度,提高访问效率,而且代码简洁,接口逻辑清晰,开发和维护起来方便。
4.3 使用 HIDL 设计软件架构
有了前面 3 章节介绍 Hidl 的知识点之后,我们就可以设计出基于 HIDL 的软件架构。
通过新软件架构图和传统的软件架构图可以知道 HIDL 那层代替了原来的