什么是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域不一样

整体设计



可以看到最左侧在源码进行编译的时候,上下层其实是编译成两个不同的镜像的:上层为system.img,底层为vendor.img。 如果直接在两个镜像的“域”中进行传参和调用无疑会出现无数的权限问题和空指针。HIDL就是为了解决上层去调用HAL层的一种技术思想。他对HAL层中对于底层device 的调用方法封装成调用接口,并把调用的部分作为系统服务进行启动。从上层的HIDLClient去访问server进行调用底层,这样的过程耦合度非常之低,并且方便上下层开发人员进行接口对接,并且在日后的演进过程中会极大地减少调试的时间成本和可以避免的bug量。

为什么需要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 android.hardware.hello_hidl@1.0;

interface IHello {

    addition_hidl(uint32_t a,uint32_t b) generates (uint32_t total);

};

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

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

 PACKAGE=android.hardware.hello_hidl@1.0
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 files
using 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);的注释去掉

// FIXME: your file license if you have one

#include "Hello.h"
#include <utils/Log.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
ALOGE("hello_hidl service is init success....a :%d,b:%d",a,b);
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: "android.hardware.hello_hidl@1.0-impl",
relative_install_path: "hw",
proprietary: true,
srcs: [
"Hello.cpp",
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"android.hardware.hello_hidl@1.0",
"liblog",
"libutils",
],
}

这个Android.bp会将Hello这个HIDL服务编译成一个android.hardware.hello_hidl@1.0-impl.so,它还依赖一个android.hardware.hello_hidl@1.0.so,这个so哪来的呢?

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

// This file is autogenerated by hidl-gen -Landroidbp.
hidl_interface {
name: "android.hardware.hello_hidl@1.0",
root: "android.hardware",
vndk: {
enabled: true,
},
srcs: [
"IHello.hal",
],
interfaces: [
"android.hidl.base@1.0",
],
gen_java: true,
}

这个Android.bp会将hardware/interfaces/hello_hidl/1.0这个HIDL编译成一个android.hardware.hello_hidl@1.0.so,到这里我们发现service.cpp没有用到,所以我们还需要修改default目录下的Android.bp:

// FIXME: your file license if you have one

cc_library_shared {
name: "android.hardware.hello_hidl@1.0-impl",
relative_install_path: "hw",
proprietary: true,
srcs: [
"Hello.cpp",
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"liblog",
"libhardware",
"android.hardware.hello_hidl@1.0",
"liblog",
"libutils",
],
}
cc_binary {
name: "android.hardware.hello_hidl@1.0-service",
defaults: ["hidl_defaults"],
relative_install_path: "hw",
vendor: true,
srcs: ["service.cpp"],
shared_libs: [
"android.hardware.hello_hidl@1.0",
"libhardware",
"libhidlbase",
"libhidltransport",
"libutils",
"liblog",
], }

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

android.hardware.hello_hidl@1.0-impl.soandroid.hardware.hello_hidl@1.0.soandroid.hardware.hello_hidl@1.0-service.so

还有一点需要注意的是,这个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/android.hardware.hello_hidl@1.0-impl.so vendor/lib64/hw/

adb push system/lib64/android.hardware.hello_hidl@1.0.so vendor/lib64/

adb push vendor/bin/hw/android.hardware.hello_hidl@1.0-service vendor/bin/hw/

接着我们到手机vendor/bin/hw/目录下去执行android.hardware.hello_hidl@1.0-service这个二进制可执行文件,这个文件就会执行service.cpp的代码,调用defaultPassthroughServiceImplementation注册我们的HIDL服务

再看看log输出:



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

再执行adb shell ps -A|grep -i --color "hello_hidl"命令看下这个服务状态



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",
"android.hardware.hello_hidl@1.0",
"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这个框架用起来还是比较简单的,大部分代码都是通过工具生成的

AndroidQ 打通应用层到HAL层---(HIDL服务实现)的更多相关文章

  1. AndroidQ 打通应用层到HAL层(转)

    1. 参考https://blog.csdn.net/qq_34211365/category_9903135.html 直通式,绑定式,从应用端调到hal接口,亲自实现能够更加理解

  2. AndroidO Treble架构下Hal进程启动及HIDL服务注册过程【转】

    本文转载自:https://blog.csdn.net/yangwen123/article/details/79854267 通过前面对Treble架构的介绍,我们知道,Android Framew ...

  3. Android native进程间通信实例-binder篇之——HAL层访问JAVA层的服务

    有一天在群里聊天的时候,有人提出一个问题,怎样才能做到HAL层访问JAVA层的接口?刚好我不会,所以做了一点研究. 之前的文章末尾部分说过了service call 可以用来调试系统的binder服务 ...

  4. Android Hal层简要分析

    Android Hal层简要分析 Android Hal层(即 Hardware Abstraction Layer)是Google开发的Android系统里上层应用对底层硬件操作屏蔽的一个软件层次, ...

  5. 如何在 kernel 和 hal 层读取同一个标志

    很多时候我们需要从 HAL 层(Hardware Abstract Layer)传一个标志给 kernel 层.一般这种传递是不能直接通过定义全局变量来实现的. 此时可以通过读写文件来实现该标志. 譬 ...

  6. 高通HAL层之Sensor HAL

    高通的HAL层其实分为两种,一种是直接从kernel这边报数据上来的,由sensor HAL层来监听,另一种是走ADSP的模式,HAL层是通过qmi的形式进行监听的: 走ADSP架构的可以看下面的博客 ...

  7. 【Android】Sensor框架HAL层解读

    Android sensor构建 Android4.1 系统内置对传感器的支持达13种,他们分别是:加速度传感器(accelerometer).磁力传感器(magnetic field).方向传感器( ...

  8. 〖Android〗OK6410a的Android HAL层代码编写笔记

    一.编写LED灯的Linux驱动程序代码 之所以使用存在HAL层,是为了保护对硬件驱动过程的逻辑与原理: 所以,残留在Linux驱动层的代码,只保留了基本的读写操作,而不含有关键的逻辑思维: 1. l ...

  9. ZT Android4.2关于bluetooth在HAL层的分析(1)

    我的电子杂烩饭 http://blog.sina.com.cn/wuchuchu2012 [订阅][手机订阅] 首页 博文目录 图片 关于我 正文 字体大小:大 中 小 Android4.2关于blu ...

  10. Android HAL层与Linux Kernel层驱动开发简介

    近日稍微对Android中的驱动开发做了一些简要的了解. HAL:Hardware Abstract Layer 硬件抽象层,由于Linux Kernel需要遵循GPL开源协议,硬件厂商为了保护自己硬 ...

随机推荐

  1. 如何通过minIO在后端实现断点续传

    首先是黑马的媒资管理模块流程图:前端负责计算媒资文件的MD5值,同时对媒资文件进行分块. 后端需要以下几个接口: 1.检查分块是否存在(传入参数为视频唯一标识信息与块信息):检查当前分块是否已经上传至 ...

  2. DP进阶合集

    (ps:本集合为Star_F总结的dp进阶知识,持续更新~. 转载本文章需要联系我,否则视为侵权!!) 前置知识:线性dp,背包,树形dp,区间dp 内容预览: 状压dp 数位dp dp优化(前缀和, ...

  3. 云计算:Docker-compose快速部署前后端项目

    | 更好的观看效果请前往,原文博客地址:https://www.zeker.top/posts/338829e1/ 介绍 Docker Compose 是官方编排的项目之一,负责快速的部署分布式应用. ...

  4. .Net 6.0 Web API 项目生成镜像并上传到私有仓库 Harbor

    〇.前言 本文首先简单介绍了 Dockerfile 内容和常用命令: 然后是在 Windows 环境 Docker desktop 的安装和配置: 最后创建了 Web API 示例项目,并简单说明了从 ...

  5. 压力测试工具httperf使用方法

    目录 压力测试工具httperf使用方法 通过tar zxvf解压httperf-0.9.0.tar.gz 进入目录 安装c++编译环境 开始编译 进入编译后的bin目录 开始测试 压力测试工具htt ...

  6. 读论文《基于 GA - BP 的汽车行李箱盖内板冲压成形工艺优化》 —— 如何使用AI技术优化模具产业中工件冲压工艺

    最近到了模具公司工作,本来以为身边同事对模具生产和工件生产的流程(大致流程)会比较了解,结果一问才知道基本都是一问三不知,大家都在模具公司工作但是貌似很多人干的和模具生产和工件制造的工作关联性并不强, ...

  7. 深度学习框架:为啥不同的框架,不同的运行设备(GPU/CPU/NPU),运算出的结果性能会有一定百分数的差别呢

    经常会遇到有人在网上说,TensorFlow的计算结果比pytorch的高上几个百分点,也有人说RTX3090的计算结果没有A100的好,还有人说NPU的计算结果比GPU的高,而且这种说法在业内也是极 ...

  8. 【转载】 docker挂载volume的用户权限问题,理解docker容器的uid

    =================================================================== 在刚开始使用docker volume挂载数据卷的时候,经常出现 ...

  9. 【转载】回复“大修意见”(Major Revision)的模板 —— 审稿意见回复模板

    原文地址: https://zhuanlan.zhihu.com/p/80214252 ================================================== 上周有个小 ...

  10. 为python安装扩展模块时报错——error: invalid command 'bdist_wheel'

    具体过程: devil@hp:~/lab$ ./bazel-bin/python/pip_package/build_pip_package /tmp/dmlab_pkg2022年 10月 03日 星 ...