HIDL相关知识介绍

        HIDL 是一种接口描述语言(IDL),用于指定 HAL 及其用户之间的接口。HIDL 允许指定类型和方法调用,并将其收集到interface和package中。更广义地说,HIDL 是一种在可独立编译的代码库之间进行通信的系统。从 Android 10 开始,HIDL 已被弃用,Android 正在迁移到到处使用 AIDL。

    这里说的弃用,指的是官方的弃用,即package不能以android.*为开头,否则编译时将会报错;vendor厂商自定义的hidl目前还可以使用(Android 14上编译正常)。

        HIDL 用于进程间通信(IPC)。使用 HDL 创建的 HAL 被称为binderized  HAL,因为它们可以使用binder进程间通信(IPC)与其他架构层进行通信。binderized 的 HAL 运行在与使用它们的客户端分开的进程中。对于必须链接到进程的库,也可以使用直通模式(Java 中不支持)。

HIDL为分直通式和binder式,其中直通式在同一进程中,binder式运行在不同进程中;

        HIDL 规定了数据结构和方法签名,以接口(类似于类)的形式组织起来,并将其收集到package中。HIDL 的语法对于 C++ 和 Java 程序员来说并不陌生,只是使用了一组不同的关键字。HIDL 还使用 Java 风格的注解。

1. 相关术语

        binderized:表示 HIDL 用于进程间的远程过程调用,通过类似 Binder 的机制实现。

        asynchronous callback,:由 HAL 用户提供、传递给 HAL(使用 HIDL 方法)并由 HAL 调用以随时返回数据的异步接口。

        synchronous callback:从服务器的 HIDL 方法实现向客户端返回数据。不用于返回 void 或单个原始数据值的方法。

        client:调用特定接口方法的进程。一个 HAL 或 Android 框架进程可以是一个接口的客户端,也可以是另一个接口的server。

        extends:表示一个接口向另一个接口添加了方法和/或类型。一个接口只能扩展一个其他接口,可用于同一软件包名称的小版本增量,或用于在旧软件包基础上构建新软件包(如供应商扩展)。  ===> 单继承;

        generates:表示向Client返回值的接口方法。要返回一个非原始值或多个值,需要生成一个同步回调函数

        interface:方法和类型的集合。在 C++ 或 Java 中转化为类。接口中的所有方法调用方向相同:客户端进程调用服务器进程实现的方法

        oneway:应用于 HIDL 方法时,表示该方法不返回值,也不阻塞

        package:共享一个版本的接口和数据类型的集合;

        passthrough:HIDL 的模式,其中server端是一个共享库,由客户端 dlopen 打开。在直通模式下,客户端和服务器是同一个进程,但代码库是分开的。仅用于将传统代码库引入 HIDL 模式。

        server:实现接口方法的进程;

        transport:在服务器和客户端之间传输数据的 HIDL 基础设施。

        version:软件包的版本。由主版本和次版本两个整数组成。次版本的增量可以添加(但不能更改)类型和方法

2. HIDL设计的初衷

        HIDL 的目标是在无需重建 HAL 的情况下替换 Android 框架。HAL 将由供应商或 SOC 制造商构建,并放置在设备上的 /vendor或/odm 分区中,这样,安卓框架就可以在自己的分区中通过 OTA 被替换,而无需重新编译 HAL。

        HIDL 的设计兼顾了以下几个方面:

        互操作性。在可能使用各种架构、工具链和构建配置编译的进程之间创建可靠的互操作接口。HIDL 接口是版本控制的,发布后不能更改

        效率。HIDL 尽量减少复制操作的次数。HIDL 定义的数据以 C++ 标准布局数据结构的形式交付给 C++ 代码,无需拆包即可使用。HIDL 还提供共享内存接口,由于 RPC 本身速度较慢,因此 HIDL 支持两种不使用 RPC 调用传输数据的方法:共享内存和快速消息队列(FMQ)。

        直观。HIDL 仅使用 in 参数进行 RPC,从而避免了棘手的内存所有权问题;无法从方法中有效返回的值将通过回调函数返回无论是将数据传入 HIDL 进行传输,还是从 HIDL 接收数据,都不会改变数据的所有权,数据所有权始终属于调用函数。数据只需在调用函数的持续时间内持续存在,并可在调用函数返回后立即销毁。

3. 直通式HIDL

        要将运行早期 Android 版本的设备更新到 Android O,可以用一个新的 HIDL 接口封装传统HAL,该接口以绑定和直通模式为 HAL 服务。这种封装对 HAL 和 Android 框架都是透明的。

        直通模式仅适用于 C++ 客户端和实现。运行早期 Android 版本的设备没有用 Java 编写的 HAL,因此 Java HAL 本身就是绑定的。

3.1 直通式头文件

        在编译 .hal 文件时,除了用于绑定通信的头文件外, hidl-gen 还会生成一个额外的直通头文件 BsFoo.h ;该头文件定义了 dlopen 编辑的函数。由于直通 HAL 在调用它们的同一进程中运行,因此在大多数情况下,直通方法是通过直接函数调用(同一线程)来调用的。 oneway 方法在自己的线程中运行,因为它们不打算等待 HAL 来处理它们(这意味着:任何在直通模式下使用 oneway 方法的 HAL 都必须是线程安全的)。

        在 IFoo.hal 中, BsFoo.h 封装了 HIDL 生成的方法,以提供附加功能(如让 oneway 事务在另一个线程中运行)。该文件与 BpFoo.h 类似,但不是使用绑定器传递调用 IPC,而是直接调用所需的函数。

也就是说:Bs*.h的文件是直通式的Hal

3.2 用Binder方式包装直通式 HAL 

        可以对支持直通模式的 HAL 实现进行Binder化。给定一个 HAL 接口 a.b.c.d@M.N::IFoo ,就会创建两个包:

        a.b.c.d@M.N::IFoo-impl ;包含 HAL 的实现和公开函数 Foo* HIDL_FETCH_IFoo(const char* name)。在传统设备上,该软件包由dlopen打开,而实现则通过HIDL_FETCH_IFoo实例化。 可以使用hidl-gen、-Lc++-impl 和 -Landroidbp-impl生成基础代码。

        a.b.c.d@M.N::IFoo-service;打开直通 HAL 并将自身注册为绑定服务,从而使同一 HAL 实现既可用作直通服务,也可用作绑定服务。

        给定 IFoo 类型,可以调用 sp<IFoo> IFoo::getService(string name, bool getStub) 访问 IFoo 的实例。如果 getStub 为 true, getService 会尝试仅以直通模式打开 HAL。如果 getStub 为假, getService 会尝试查找绑定服务;如果失败,则会尝试查找直通服务。除非在 defaultPassthroughServiceImplementation 中,否则绝不能使用 getStub 参数(使用 Android O 启动的设备是完全绑定的设备,因此不允许以直通模式打开服务)。

4. Packages及Interface

        HIDL 是围绕Interface构建的,Interface是面向对象语言中用来定义行为的一种抽象类型。每个Interface都是Packages的一部分。

4.1 Packages

        软件包名称可以有子级,如 package.subpackage 。已发布的 HIDL 软件包的根目录是 hardware/interfaces 或 vendor/vendorName。软件包名称在根目录下形成一个或多个子目录;定义软件包的所有文件都在同一目录下。例如,:

package android.hardware.example.extension.light@2.0 

        可以在 hardware/interfaces/example/extension/light/2.0 下找到

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

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

4.2 Interface

        除了 types.hal 之外,其他每个 .hal 文件都定义了一个接口。接口的定义通常如下:

interface IBar extends IFoo { // IFoo is another interface
    // embedded types
    struct MyStruct {/*...*/};

    // interface methods
    create(int32_t id) generates (MyStruct s);
    close();
};

        没有显式 extends 声明的接口隐式地从 android.hidl.base@1.0::IBase 扩展而来(类似于 Java 中的 java.lang.Object )。隐式导入的 IBase 接口声明了几个保留方法,这些方法不应也不能在用户定义的接口中重新声明或以其他方式使用。这些方法包括:

  • ping
  • interfaceChain
  • interfaceDescriptor
  • notifySyspropsChanged
  • linkToDeath
  • unlinkToDeath
  • setHALInstrumentation
  • getDebugInfo
  • debug
  • getHashChain

4.3 Interface界面布局概要

4.3.1  每个文件都可以从软件包根映射及其全称中找到

        将指定包根 -r android.hardware:hardware/interfaces 作为hidl-gen的参数。例如,如果软件包是:

vendor.awesome.foo@1.0::IFoo

        hidl-gen 发送 -r vendor.awesome:some/device/independent/path/interfaces ,那么接口文件应位于 $ANDROID_BUILD_TOP/some/device/independent/path/interfaces/foo/1.0/IFoo.hal

        在项目中,建议名为 awesome 的供应商或 OEM 将其标准接口放在 vendor.awesome 中。选择软件包路径后,不得更改,因为这已嵌入接口的 ABI 中。

4.3.2 软件包路径映射应唯一

        例如,如果有 -rsome.package:$PATH_A 和 -rsome.package:$PATH_B , $PATH_A 必须等于 $PATH_B ,这样接口目录才会一致(这也使接口的版本管理变得更容易)

5. HIDL C++

        Android O 对 Android 操作系统进行了重新架构,在独立于设备的 Android 平台与特定于设备和供应商的代码之间定义了清晰的接口。Android 已经以 HAL 接口的形式定义了许多此类接口,在 hardware/libhardware 中定义为 C 头文件。HIDL 用稳定、版本化的接口取代了这些 HAL 接口,这些接口可以是 C++(如下所述)或 Java 的客户端和服务器端 HIDL 接口。

        本节介绍 HIDL 接口的 C++ 实现,包括 hidl-gen 编译器从 HIDL .hal 文件自动生成的文件、如何打包这些文件以及如何将这些文件与使用它们的 C++ 代码集成的详细信息。

5.1 客户端和服务器实施

        HIDL 接口有客户端和服务器端实现:

        ① HIDL 接口的客户端是通过调用接口方法来使用接口的代码;

        ② 服务器是 HIDL 接口的实现,它接收客户端的调用并返回结果(如有必要);

5.1.1 创建 HAL 客户端

        首先在 Makefile 中加入 HAL 库:

shared_libs: [
    android.hardware.nfc@1.0,
]

        接下来,加入 HAL 头文件:

#include <android/hardware/nfc/1.0/IFoo.h>
…
// in code:
sp<IFoo> client = IFoo::getService();
client->doThing();

5.1.2 创建 HAL 服务器

        要创建 HAL 实现,必须拥有.hal 文件,并且已经在 hidl-gen 上使用 -Lmakefile 或 -Landroidbp 指令为 HAL 生成了 Makefile。

hardware/interfaces/update-makefiles.sh用于原生内部的Hal文件,是一个很好的参考用例;

        创建实现 HAL 所需的文件:

PACKAGE=android.hardware.nfc@1.0
LOC=hardware/interfaces/nfc/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

        要使 HAL 在直通模式下工作,必须在 /(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl(OPTIONAL_IDENTIFIER).so 中驻留 HIDL_FETCH_IModuleName函数,其中的字符串用于标识直通实现。上述命令会自动满足直通模式的要求,并创建 android.hardware.nfc@1.0-impl 目标,但也可以使用任何扩展。例如, android.hardware.nfc@1.0-impl-foo 使用 -foo 来区分自己

        如果一个 HAL 是另一个 HAL 的次版本或扩展版本,则应使用基础 HAL 来命名该二进制文件。例如, android.hardware.graphics.mapper@2.1 实现仍应在名为 android.hardware.graphics.mapper@2.0-impl(OPTIONAL_IDENTIFIER) 的二进制文件中。通常,这里会包括实际的 HAL 版本。通过这样命名二进制文件,2.0 客户端可以直接检索,而 2.1 客户端则可以向上广播实现。

        接下来,填写功能Stubs并设置守护进程。守护程序代码示例(支持直通):

#include <hidl/LegacySupport.h>

int main(int /* argc */, char* /* argv */ []) {
    return defaultPassthroughServiceImplementation<INfc>("nfc");
}

        defaultPassthroughServiceImplementation 将dlopen()调用所提供的-impl库,并将其作为binderized服务提供。程序代码示例:

int main(int /* argc */, char* /* argv */ []) {
    // This function must be called before you join to ensure the proper
    // number of threads are created. The threadpool will never exceed
    // size one because of this call.
    ::android::hardware::configureRpcThreadpool(1 /*threads*/, true /*willJoin*/);

    sp<INfc> nfc = new Nfc();
    const status_t status = nfc->registerAsService();
    if (status != ::android::OK) {
        return 1; // or handle error
    }

    // Adds this thread to the threadpool, resulting in one total
    // thread in the threadpool. We could also do other things, but
    // would have to specify 'false' to willJoin in configureRpcThreadpool.
    ::android::hardware::joinRpcThreadpool();
    return 1; // joinRpcThreadpool should never return
}

        该守护进程通常位于 $PACKAGE + "-service-suffix" (例如 android.hardware.nfc@1.0-service ),但也可能位于任何地方。特定类别 HAL 的 sepolicy 是属性 hal_<module> (例如 hal_nfc) 。

        该属性必须应用于运行特定 HAL 的守护进程(如果同一进程服务于多个 HAL,则可以应用多个属性)。

6. 附:hidl-gen指令

Usage: hidl-gen -o <output path> -L <language> [-O <owner>] [-p <root path>] (-r <interface root>)+ [-R] [-F] [-v] [-d <depfile>] FQNAME...

Process FQNAME, PACKAGE(.SUBPACKAGE)*@[0-9]+.[0-9]+(::TYPE)?, to create output.

	-h: Prints this menu.
	-L <language>: The following options are available:
		check           : Parses the interface to see if valid but doesn't write any files.
		c++             : (internal) (deprecated) Generates C++ interface files for talking to HIDL interfaces.
		c++-headers     : (internal) Generates C++ headers for interface files for talking to HIDL interfaces.
		c++-sources     : (internal) Generates C++ sources for interface files for talking to HIDL interfaces.
		export-header   : Generates a header file from @export enumerations to help maintain legacy code.
		c++-impl        : Generates boilerplate implementation of a hidl interface in C++ (for convenience).
		c++-impl-headers: c++-impl but headers only.
		c++-impl-sources: c++-impl but sources only.
		c++-adapter     : Takes a x.(y+n) interface and mocks an x.y interface.
		c++-adapter-headers: c++-adapter but helper headers only.
		c++-adapter-sources: c++-adapter but helper sources only.
		c++-adapter-main: c++-adapter but the adapter binary source only.
		java            : (internal) Generates Java library for talking to HIDL interfaces in Java.
		java-impl       : Generates boilerplate implementation of a hidl interface in Java (for convenience).
		java-constants  : (internal) Like export-header but for Java (always created by -Lmakefile if @export exists).
		vts             : (internal) Generates vts proto files for use in vtsd.
		makefile        : (removed) Used to generate makefiles for -Ljava and -Ljava-constants.
		androidbp       : (internal) Generates Soong bp files for -Lc++-headers, -Lc++-sources, -Ljava, -Ljava-constants, and -Lc++-adapter.
		androidbp-impl  : Generates boilerplate bp files for implementation created with -Lc++-impl.
		hash            : Prints hashes of interface in `current.txt` format to standard out.
		function-count  : Prints the total number of functions added by the package or interface.
		dependencies    : Prints all depended types.
		inheritance-hierarchy: Prints the hierarchy of inherited types as a JSON object.
		format          : Reformats the .hal files
	-O <owner>: The owner of the module for -Landroidbp(-impl)?.
	-o <output path>: Location to output files.
	-p <root path>: Android build root, defaults to $ANDROID_BUILD_TOP or pwd.
	-R: Do not add default package roots if not specified in -r.
	-F: Require all referenced ASTs to be frozen.
	-r <package:path root>: E.g., android.hardware:hardware/interfaces.
	-v: verbose output.
	-d <depfile>: location of depfile to write to.

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值