Android HIDL理解(基于Android O)

 

1 概述

         HIDL是Hardware Interface Definition Language的简称。在Android Project Treble被提出,在android O中被全面的推送。

 

2 HIDL

2.1 hidl设计目的

设计 HIDL 这个机制的目的,主要目的是把框架(framework)与 HAL 进行隔离,使得框架部分可以直接被覆盖、更新,而不需要重新对 HAL 进行编译。HAL 的部分将会放在设备的 /vendor 分区中,并且是由设备供应商(vendors)或 SOC 制造商来构建。这使得框架部分可以通过 OTA 方式更新,同时不需要重新编译 HAL。

HIDL 实际上是用于进行进程间通信(Inter-process Communication,IPC)的。进程间的通信可以称为 Binder 化(Binderized)。对于必须连接到进程的库,也可以使用 passthough 模式(但在Java中不支持)。

HIDL 接口具有client端和server端的实现:

          client端是指通过HIDL调用方法的一方

          server端是指实现HIDL的接口,接受client的调用并返回数据的一方

在从 libhardware HAL 转换为 HIDL HAL 的过程后,HAL 的实现成为server端,而调用 HAL 的进程则成为client。默认实现可提供Passthrough和binderized式 HAL:

binderized  mode:

以HIDL表示的 HAL。这些 HAL 取代了早期 Android 版本中使用的传统 HAL 和旧版 HAL。在Binder HAL 中,Android  framework和 HAL 之间通过 Binder 进程间通信 (IPC) 调用进行通信。所有在推出时即搭载了 Android 8.0 或后续版本的设备都必须只支持binder HAL。

Passthrough  Mode:

以 HIDL 封装的传统 HAL 或旧版 HAL。这些 HAL 封装了现有的 HAL,可在binder 和 Same-Process(Passthrough)模式下使用。升级到 Android 8.0 的设备可以使用Passthrough  HAL。

演化过程,如下所示:

binder化后的通讯过程:

 

2.2 hidl语法

HIDL 语言与 C 语言是相似的(但没有使用 C 的预处理器),以下没有描述的标点符号(除了明显使用 | 与 = 的)是语法的一部分:

符号

说明

/** */

文档的注释。

/* */

多行注释

//

单行注释

[empty]

表示为空

可选项

该序列包含 0 个或者多个如前述使用分隔符隔开的项

,

分割序列元素

;

标记每个元素的结束位置

constexpr

C 风格的常量表达式

import_name

一个包或者接口的名称,其标准在 HIDL Versioning 中有详细阐述

 

示例:

 

下面列出的是 HIDL 相关的术语:

术语

翻译

说明

binderized

Binder 化

标明 HIDL 被用于进程间的远程方法调用,它是通过一个类似 Binder 的机制来实现的。

callback, asynchronous

异步回调

由 HAL 用户所提供的接口,通过 HIDL 的方法传递给 HAL,并由 HAL 来调用,以随时返回所需数据

callback, synchronous

同步回调

从服务端的 HIDL 方法实现向客户端返回数据,未使用的方法返回 void 或一个原始类型值。

client

客户端

调用一个特定接口的方法的进程。一个 HAL 或者 Framework 进程可以是一个接口的客户端,也可以是另一个接口的服务端。

extends

扩展

表示了一个将方法与(或)类型添加到另一个接口的接口。一个接口只能扩展另外的一个接口。它可以用于对于同一个包名的从版本升级,或者也可用来在一个旧的包的基础之上建立一个新的包(比如供应商的扩展)。

generates

生成

表示一个返回值给客户端的接口方法。为了返回一个非原始类型值,或者更多的值给客户端,则需要生成一个同步回调函数。

interface

接口

这是方法与类型的集合,在 C++ 与 Java 中转换为类。在接口中的所有方法都被调用在同一方向:客户端进程调用由服务端进程实现的方法。

oneway

单向

这个概念应用到 HIDL 方法时,表示其无返回值,且不会阻塞。

package

共享同一版本的接口与数据类型的集合。

passthrough

透明传输

服务端是一个共享库时的 HIDL 模式,通过 dlopen 被客户端打开。在这个 passthrough 模式下,客户端与服务端是相同的进程,但是是不同的代码库。仅仅用于将旧的代码库引用到 HIDL 模型中。

transport

传送带

HIDL 在客户端与服务端之间移动数据的基本结构。

server

服务端

实现了接口方法的进程。另请参阅 passthrough 的解释。

version

版本

包的版本。包含两个整数,表示主版本与从版本。从版本的升级可以增加(但不会改变原来的)类型或者方法。

 

2.3 Packages

         HIDL是围绕接口构建的,接口是面向对象语言中用于定义行为的抽象类型。 每个接口都是包的一部分。

         package的名称可以包含子包比如package.subpackage。公共HIDL包的根目录在hardware/interfaces 或 vendor/vendorname。包名称在根目录下形成一个或多个子目录。

定义包的所有文件都在同一目录中。 例如,可以在hardware / interfaces / example / extension / light / 2.0下找到包android.hardware.example.extension.light@2.0。

下表列出了包前缀和位置:

包目录包含扩展名为.hal的文件。 每个文件都必须包含一个package声明,用于命名该文件所属的包和版本。 文件types.hal(如果存在)不定义接口,而是定义包中每个接口可访问的数据类型。

 

 

3 示例

首先进入android更目录,创建示例目录:

在1.0目录中添加.hal文件,内容如下:

很简单就是获取vampire的爱好,返回一个string字符串内容就是我们需要的,接口定义完毕。

下面调用update-makefiles.sh脚本自动生成Android.bp

执行结束后会自动增加Android.bp文件:

Android.mk用于生成java,暂时不管,先看一下Android.bp的内容:

可以看到通过IVampir.hal后面会生成多个文件:

VampireAll.cpp/IVampire.h/IHwVampire.h/BnHwVampire.h/BpHwVampire.h/BsVampire.h

这些文件的作用是什么呢,android的官方文档给出了解释:

 

文件

说明

IFoo.h

描述 C++ 类中的纯 IFoo 接口;它包含 IFoo.hal 文件中的 IFoo 接口中所定义的方法和类型,必要时会转换为 C++ 类型。不包含与用于实现此接口的 RPC 机制(例如 HwBinder)相关的详细信息。类的命名空间包含软件包名称和版本号,例如 ::android::hardware::samples::IFoo::V1_0。客户端和服务器都包含此标头:客户端用它来调用方法,服务器用它来实现这些方法。

IHwFoo.h

包含用于对接口中使用的数据类型进行序列化的函数的声明。开发者不得直接包含其标头

BpFoo.h

从 IFoo 继承的类,可描述接口的 HwBinder 代理(客户端)实现。开发者不得直接引用此类。

BnFoo.h

保存对 IFoo 实现的引用的类,可描述接口的 HwBinder 存根(服务器端)实现。开发者不得直接引用此类。

fooAll.cpp

包含 HwBinder 代理和 HwBinder 存根的实现的类。当客户端调用接口方法时,代理会自动从客户端封送参数,并将事务发送到绑定内核驱动程序,该内核驱动程序会将事务传送到另一端的存根

可以看出这些文件包含了binder话后对于binder通讯的一些封装,作为client和service都需要引用它,client是为了调用而service则是为了实现功能。

再看一下这个文件最终的归宿:

可以看到最终所有文件都会被打包到android.hardware.vampire@1.0.so这个动态库中,而我们client的调用与service的实现都会使用这个动态库,到这hidl描述的interface就实现了,我们只需要mmm hardware/interfaces/vampire/就可以生成需要的interface 库文件。

 

         既然hidl接口共享库完成了,下面我们来实现以下这个接口,也就是service的实现,首先利用android提供的工具生成IVampire接口实现代码:

执行结束后会新增如下代码:

然后我们进入Vampire.cpp去实习以下接口函数:

在需要实现的接口里面会提醒TODO implement,我们建立一个字符串便调用cb,注意我们打开了HIDL_FETCH表示是passthrough模式。

然后我们再看一下Android.bp文件:

可以看到Vampire.cpp会生成android.hardware.vampire@1.0-impl.so库文件,它是service的实现共享库,下面我们就需要创建一个service进程用于client的访问。

创建service.cpp:

主函数会获取IVampire的实现代码,创建service并注册到系统中。我们需要将service生成一个可执行文件,为了演示方便我们将其加到default的Android.bp中:

为了实现自动启动还需要添加android.hardware.vampire@1.0-service.rc文件:

 

到这service端就准备OK了,下面就是client的调用了,创建client.cpp文件:

你可能注意到我们在.hal定义的返回值被替换为一个回调函数了,我们实现以下,通过getService获取服务,然后调用hobby来获取,我们将client也编译为可执行文件:

 

下面只要轻松的mm或者mmm以下就可以获取需要的各种库文件与可执行文件了。

看一下当前目录下的文件:

将client和service放在一起只是为了实现方便对比,实际过程中可以放置在不同的地方,

发现没有IVampire.h等文件啊,不要急执行mm或者mmm后会在out/soong/.intermediates/hardware/interfaces/vampire/1.0中生成这些中间文件。

生成的库文件与可执行文件如下:

各个文件的相互关系和调用流程如下:

4 测试验证

         测试第一步将生成的文件push到手机的对应位置中,为了方便我们手动启动service,不使用rc文件:

然后执行client:

logcat一下:

说是下manifest.xml中未添加vampire,这个文件带代码的device/qcom/project中,编译后8.0在vendor目录下,9.0在vendor/etc/vintf中,好的添加权限:

添加后push到手机,在执行:

成功返回。

 

 

 

5总结

         简单点理解hidl的目的是为了用户方便快速的开发hal的库文件或者说service,而不需要客户去关心如何实现binder,通过hidl-gen工具利用.hal文件将用户定义的接口需要binderized 的部分自动封装生成代码,客户只需要知道如何实现接口即service的实现,如何调用接口即client的使用即可。

 

 

 

参考:

https://www.jianshu.com/p/ca6823b897b5

https://blog.csdn.net/gh201030460222/article/details/80551897

https://source.android.google.cn/devices/architecture/hidl

https://blog.csdn.net/junwua/article/details/80594202

 

更多相关文章

  1. Android(安卓)调用系统dialog
  2. android list排序
  3. android应用程序抓包
  4. Progressbar的使用
  5. Android(安卓)让你的SeekBar 也支持长按事件
  6. Android(安卓)Studio最常用快捷键
  7. linux 编译 android-apk 安装到window下的模拟器(步骤)
  8. [Android]网络传输使用BufferedOutputStream写文件导致文件变大
  9. android asmack调用MultiUserChat.getHostedRooms方法出现空指针

随机推荐

  1. 老调重弹篇:有关BC/C++语言程序编程学习的
  2. Linux软件包管理
  3. K8S中部署KAFKA集群
  4. 如何写出让大厂面试官满意的字符串复制函
  5. 华为防火墙L2TP ***的配置
  6. 顺序栈(C语言,静态栈)
  7. Oracle Linux 7.9+Oracle 12c+ASM安装文
  8. 总结了几个Java锁的面试题,看你是否能融会
  9. 再议电商业务的复杂性
  10. markdown与Emmet插件的使用方式