当前位置:首页 > 资讯 > 正文

Android 经典hal开发实战手机开发「Android 经典hal开发实战」

Android 经典hal开发实战手机开发「Android 经典hal开发实战」

背景

hi,粉丝朋友们: 大家好!今天来一个经典hal的实现,首先说一下哈,经典hal在正常在Treble计划后就不再建议使用了,高版本一般都是hidl,aidl。目前新版本一般都是强制使用aidl,那为啥还需要讲解这个经典hal呢?

1、虽然采用了treble计划后,system/vendor隔离后,但是因为经典hal是个so,而且代码一般不需要强依赖so,具有一个非常好的移植性,导致虽然变成了google要求的hidl,其实内部依然实现是经典hal的so

2、经典hal so在trable计划以前已经经历了很多个版本,业务执行的稳定性很好,基本上厂商没啥动力推倒重来,基于新的hidl,aidl再写一遍hal业务

总结:treble计划虽然系统已经大部分都是hidl,aidl实现方式,但是这个只是一个对外接口层面的,内部实现的话其实完全可以继续调用以前写好的经典hal,这个方式保证了稳定性,修改量小

所以基于以上背景,掌握经典hal的相关知识完全是有必要的,不然就可能会导致你根本看不懂很多hal实现

HAL module的架构分析

注意这里只进行依赖libhardware库这种方式实现,不讲解legacy方式

HAL module架构主要分为以下3个结构体:

struct hw_module_t struct hw_module_methods_t struct hw_device_t

3个结构体是写这个标准方式hal的最关键部分,它们定义是在 hardware/libhardware/include/hardware/hardware.h

下面3个进行详细介绍其功能和作用

hw_module_t部分

相关的定义代码如下:

代码很少,注释很多,不过都是英文的,这里给大家总结一下,即常用的成员变量:

1、每一个hardware硬件抽象模块必须定义有一个模块结构体,名字是HAL_MODULE_INFO_SYM,即HMI,这个模块结构体第一个成员变量类型必须是hw_module_t

2、hw_module_t的tag的值必须为HARDWARE_MODULE_TAG,这个就是来标识是硬件抽象的结构体

3、id这个属性代表这个module的唯一性,即每个硬件抽象模块都会有自己的id

4、methods这个代表每个硬件抽象模块对应的方法结构体,类型hw_module_methods_t

常见定义和实现方式如下:

定义结构体:

实例化HAL_MODULE_INFO_SYM:

hw_module_methods_t部分

在结构体 hw_module_methods_t 中只有一个成员,它是一个函数指针,名字是open,它主要作用就是用来打开硬件抽象层中给的硬件设备。因为一个硬件抽象模块module可以包含多个设备device,所以这里需要指定id。

moudlue:代表打开硬件设备设备所属模块 id:代表打开设备的id device:这个双重指针,一般是输出内容到这个参数里面,即返回值,用来描述一个打开的硬件设备

hw_device_t部分

其实这个和前面的hw_module_t高度相识,只不过一个代表整个模块一个代表模块的设备 同样总结一下hw_device_t:

1、每个硬件设备都必须自定义一个硬件设备的结构体,第一个成员变量为hw_device_t

2、tag必须为所在的硬件模块HARDWARE_DEVICE_TAG,代表他是个硬件设备结构体

3、会有一个close的函数指针,主要是关闭一个硬件设备

4、自定义设备结构体,除了第一个成员hw_device_t外,最重要就是会定义其他成员方法指针,这些方法就是用来对外提供操作硬件设备的

常见自定义的hw_device_t如下:

构造结构过程:

上面最重要就是把hw_device_t构造出,而且给成员变量test赋值了具体方法,后面获取了这个hw_device_t就可以调用真正的hal实现方法了

HAL module的使用步骤

上面只是一个个的定义好硬件抽象模块的步骤,接下来是要要怎么使用上面的hardware接口进行调用到相关的hal方法呢? 1、通过hw_get_module(char* id, struct hw_module_t ** module) 方法获取一个硬件抽象模块的指针

2、通过模块调用module->common.methods->open()方法获取一个硬件抽象即hw_device_t的指针

3、使用硬件抽象设备的hw_device_t指针调用具体的hal实现方法,比如上面 mytest_dev->addTest

相关调用hal的代码如下:

hardware.c源码分析

上面使用的hw_get_module这些方法到底在哪里实现的呢? 其实这些代码的实现都是在hardware.c文件中 下面从hw_get_module方法作为切入点全面展开分析一下相关的 hardware/libhardware/hardware.c

hw_module_exists会根据传递进来的name,subname,然后去拼出一个so的文件名字,看看这个so文件是否存在,存在这返回找到

上面可以看出hw_module_exists会去几个路径找,优先级是oem > vendor> system

如果prop指定的都没有找到最后只能用最后的default看看是否存在,如果这个default so都没有,那么就会报错了,即无法获取到对于的硬件抽象模块,说明硬件抽象这部分功能就无法使用了。

一般情况下都最少可以找到一个default的so,那么找到后会执行上面的load方法,把这个so进行相关load

这个load方法是最关键的,来看看系统到底是怎么实现的:

补充介绍一下两个函数: dlopen()是一个强大的库函数。该函数将打开一个新库,并把它装入内存。该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的。 dlsym是一个计算机函数,功能是根据动态链接库操作句柄与符号,返回符号对应的地址,不但可以获取函数地址,也可以获取变量地址。

上面的load方法就是我们说的hardware动态性的关键,它是在运行时候才真正依赖具体硬件抽象so,编译期间只需要依赖公共的hardware相关公共类既可以,不需要so,所以这里就给硬件抽象提供商带来很大灵活性,哪怕硬件厂商不提供也一般会有一个default的so保证不会产生什么严重的崩溃和强依赖问题,大大减低了aosp的代码对于硬件抽象的各个厂商的耦合性。

这里看看手机上的相关hal so:

这里在看看哪个prop是ms8998

本文章对应视频手把手教你学framework:

https://mp.weixin.qq.com/s/LbVLnu1udqExHVKxd74ILg

私聊作者+v(androidframework007) 

点击这里 https://mp.weixin.qq.com/s/Qv8zjgQ0CkalKmvi8tMGaw

视频:https://www.bilibili.com/video/BV1Jg4y1C7fw/