Android HIDL源码分析

时间:2024-03-23 10:00:47

Android HIDL 基础知识,源码分析。

概念

在 Android O 8.0 后引入的 Treble 项目,目的是将 Framework 和 HAL 层分开;Google 重点关注 Framework 及以上部分,HAL 及以下交给各厂商实现, HAL 层及厂商实现都会放到新的 /vendor 分区中;这样 Google 在后续 OTA 系统升级时,可以单独升级系统部分,而不需要修改厂商实现部分。因此重新定义了 Framework 和 HAL 层的接口,即 HIDL ;以及新增了接口层的测试 VTS: Vendor Test Suite ,确保厂商实现部分接口设计符合规范并能向前兼容。简而言之,Google 这么做主要目的是:不管厂商如何实现,修改了哪些东西,需要确保 Android 原生系统都能在这台设备上升级并使用。当然这种结构也方便手机厂商做 system 升级,如果当前版本已经是 Android O ,各个分区已经分配好,可以做到仅仅更新 AOSP 部分到 P ,而其他 vendor 可以保持不变,提供了一种可能性和便利性,实际上厂商是否愿意这么做呢?

Android HIDL源码分析

HIDL: HAL Interface Definition Language , HIDL 发音为 hide-l ,指定了 HAL 层和 Framework 层通信的接口,目的是 Framework和 HAL 层能相互通信;这样用户能够替换 Android 框架,而无需重新编译 HAL 。

HAL 类型

HAL 有两种类型:

  • Binder HAL
    绑定式 HAL ,通过 Binder 机制实现跨进程通信。
  • Passthrough HAL
    直通式 HAL ,通过 dlopen 方式加载库,也就是在同一进程直接调用。这里需要注意,默认情况下通常会使用 *Binder 化直通式 HAL *,也就是说最后仍然是跨进程 Binder 通信。

Android HIDL源码分析

Binder HAL

Android O 开始,Framework 和 HAL 跨进程也是使用 Binder 互相通信,这种通信方式极大地增加了老版本中的 Binder 流量,因此新增了 Binder 域,官网推荐三个域的使用范围:

  • /dev/binder
    用于 Framework 和 APP 之间的 IPC ,使用 AIDL 接口。
  • /dev/hwbinder
    用于 Framework 和 Vendor 进程之间的 IPC ,使用 HIDL 接口。
    用于供应商进程之间的 IPC ,使用 HIDL 接口。
  • /dev/vndbinder
    供应商/供应商进程之间的 IPC ,使用 AIDL 接口。

Binder 化直通式 HAL

它实际是直通模式的 HAL ,通过 Binder 化来实现。比如 HAL 接口 [email protected]::IFoo,系统会创建两个软件包:

  • [email protected]::IFoo-impl
    包含 HAL 的实现,并暴露函数 IFoo* HIDL_FETCH_IFoo(const char* name)。在旧版设备上,此软件包经过 dlopen 处理,且实现使用 HIDL_FETCH_IFoo 进行了实例化。也可以使用 hidl-gen 和 -Lc++-impl 以及 -Landroidbp-impl 来生成基础代码。
  • [email protected]::IFoo-service
    打开直通式 HAL,并将其自身注册为 Binder 化服务,从而使同一 HAL 实现能够同时以直通模式和 Binder 化模式使用。
    如果有一个 IFoo ,可以调用 sp<IFoo> IFoo::getService(string name, bool getStub) ,以获取对 IFoo 实例的访问权限。如果 getStub 为 True,则 getService 会尝试仅在直通模式下打开 HAL。如果 getStub 为 False,则 getService 会尝试找到 Binder 化服务;如果未找到,则它会尝试找到直通式服务。除了在 defaultPassthroughServiceImplementation 中,其余情况一律不得使用 getStub 参数。(搭载 Android O 的设备是完全 Binder 化的设备,因此不得在直通模式下打开服务。)

网络配置工具

Android 中包含标准的 Linux 网络实用程序: ifconfig, ip, ip6tables 等,但是这些程序都在 system/bin 目录下, 在 Android O 之后,避免系统更新导致这些程序使用时不匹配,这些程序都被集成到 netutils-wrapper-1.0 工具中:

1
2
3
4
5
6
7
ELUGA_Ray_710:/system/bin # ls *wrapper-* -l
lrwxr-xr-x 1 root shell    20 20*:56 ip-wrapper-1.0 -> netutils-wrapper-1.0
lrwxr-xr-x 1 root shell    20 20*:56 ip6tables-wrapper-1.0 -> netutils-wrapper-1.0
lrwxr-xr-x 1 root shell    20 20*:56 iptables-wrapper-1.0 -> netutils-wrapper-1.0
lrwxr-xr-x 1 root shell    20 20*:56 ndc-wrapper-1.0 -> netutils-wrapper-1.0
lrwxr-xr-x 1 root shell    20 20*:56 tc-wrapper-1.0 -> netutils-wrapper-1.0
-rwxr-xr-x 1 root shell 65248 20*:56 netutils-wrapper-1.0

Android O 之后,vendor 中不能直接调用 /system/bin/netutils-wrapper-1.0 ,否则会出错;也不能直接调用 /system/bin/ip <FOO> <BAR> ,而只能使用封装程序 /system/bin/ip-wrapper-1.0 <FOO> <BAR> ;如果 vendor 进程调用这些命令,需要在 SELinux policy 中添加:

1
domain_auto_trans(VENDOR-DOMAIN-NAME, netutils_wrapper_exec, netutils_wrapper)

转换工具

Android 源码中提供了工具将老旧版本的 hal 模块转换为 HIDL HAL 格式,工具: c2hal: system/tools/hidl/c2hal ,使用示例:

1
c2hal -r android.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport -p [email protected] hardware/libhardware/include/hardware/nfc.h

转换完成后,可以进一步手动修正部分小错误。

术语

  • Binder 化
    表示 HIDL 用于进程之间的远程过程调用,并通过类似 Binder 的机制来实现。
  • 异步回调
    由 HAL 用户提供、传递给 HAL(通过 HIDL 方法)并由 HAL 调用以随时返回数据的接口。
  • 同步回调
    将数据从服务器的 HIDL 方法实现返回到客户端;不用于返回无效值或单个原始值的方法。
  • 客户端
    调用特定接口的方法的进程。 HAL 进程或框架进程可以是一个接口的客户端和另一个接口的服务器。
  • 扩展
    也可以理解为继承;表示向另一接口添加方法和/或类型的接口,一个接口只能扩展另一个接口。可用于具有相同软件包名称的 Minor 版本递增,也可用于在旧软件包的基础上构建的新软件包。
  • 生成
    表示将值返回给客户端的接口方法。要返回一个非原始值或多个值,则会生成同步回调函数。
  • 接口
    方法和类型的集合。会转换为 C++ 或 Java 中的类。接口中的所有方法均按同一方向调用:客户端进程会调用由服务器进程实现的方法。
  • 单向
    应用到 HIDL 方法时,表示该方法既不返回任何值也不会造成阻塞。
  • 直通式
    HIDL 的一种模式,使用这种模式时,服务器是共享库,由客户端进行 dlopen 处理。在直通模式下,客户端和服务器是相同的进程,但代码库不同。此模式仅用于将旧版代码库并入 HIDL 模型。
  • 服务器
    实现接口的方法的进程。
  • 传输
    在服务器和客户端之间移动数据的 HIDL 基础架构。
  • 版本
    软件包的版本。由两个整数组成: Major 版本和 Minor 版本。 Minor 版本递增可以添加(但不会更改)类型和方法。
  • 应用二进制接口 `ABI
    应用编程接口 + 所需的任何二进制链接。
  • 完全限定名称 fqName
    用于区分 hidl 类型的名称。例如: [email protected]::IFoo 。
  • 软件包
    共用一个版本的接口和数据类型的集合。包含 HIDL 接口和类型的软件包。例如: [email protected] 。
  • 软件包根目录
    包含 HIDL 接口的根目录软件包。例如: HIDL 接口 android.hardware 在软件包根目录 [email protected] 中。
  • 软件包根目录路径
    软件包根目录映射到的 Android 源代码树中的位置。

基础语法

规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
ROOT =
    PACKAGE IMPORTS PREAMBLE { ITEM ITEM ... }  // not for types.hal
    PREAMBLE = interface identifier EXTENDS
  | PACKAGE IMPORTS ITEM ITEM...  // only for types.hal; no method definitions

ITEM =
    ANNOTATIONS? oneway? identifier(FIELD, FIELD ...) GENERATES?;
  |  struct identifier { SFIELD; SFIELD; ...};  // Note - no forward declarations
  |  union identifier { UFIELD; UFIELD; ...};
  |  enum identifier: TYPE { ENUM_ENTRY, ENUM_ENTRY ... }; // TYPE = enum or scalar
  |  typedef TYPE identifier;

VERSION = integer.integer;

PACKAGE = package android.hardware.identifier[.identifier[...]]@VERSION;

PREAMBLE = interface identifier EXTENDS

EXTENDS = <empty> | extends import_name  // must be interface, not package

GENERATES = generates (FIELD, FIELD ...)

// allows the Binder interface to be used as a type
// (similar to typedef'ing the final identifier)
IMPORTS =
   [empty]
  |  IMPORTS import import_name;

TYPE =
  uint8_t | int8_t | uint16_t | int16_t | uint32_t | int32_t | uint64_t | int64_t |
 float | double | bool | string
|  identifier  // must be defined as a typedef, struct, union, enum or import
               // including those defined later in the file
|  memory
|  pointer
|  vec<TYPE>
|  bitfield<TYPE>  // TYPE is user-defined enum
|  fmq_sync<TYPE>
|  fmq_unsync<TYPE>
|  TYPE[SIZE]

FIELD =
   TYPE identifier

UFIELD =
   TYPE identifier
  |  struct identifier { FIELD; FIELD; ...} identifier;
  |  union identifier { FIELD; FIELD; ...} identifier;

SFIELD =
   TYPE identifier
  |  struct identifier { FIELD; FIELD; ...};
  |  union identifier { FIELD; FIELD; ...};
  |  struct identifier { FIELD; FIELD; ...} identifier;
  |  union identifier { FIELD; FIELD; ...} identifier;

SIZE =  // Must be greater than zero
     constexpr

ANNOTATIONS =
     [empty]
  |  ANNOTATIONS ANNOTATION

ANNOTATION =
  |  @identifier
  |  @identifier(VALUE)
  |  @identifier(ANNO_ENTRY, ANNO_ENTRY  ...)

ANNO_ENTRY =
     identifier=VALUE

VALUE =
     "any text including \" and other escapes"
  |  constexpr
  |  {VALUE, VALUE ...}  // only in annotations

ENUM_ENTRY =
     identifier
  |  identifier = constexpr
  • 可以使用 /** */, /* */, // 来注释
  • [empty] 表示该字词可能为空
  • ? 跟在文本或字词后,表示它是可选的
  • ... 表示包含零个或多个项、用指定的分隔符号分隔的序列。HIDL 中不含可变参数
  • 逗号 , 用于分隔序列元素
  • 分号 ; 用于终止各个元素,包括最后的元素
  • 大写字母是非终止符
  • italics 是一个令牌系列,例如 integer 或 identifier(标准 C 解析规则)
  • constexpr 是 C 样式的常量表达式(如 1 + 1 和 1L << 3)
  • import_name 是软件包或接口名称,按 HIDL 版本编号中所述的方式加以限定
  • 小写 words 是文本令牌

软件包

软件包名称可以具有子级,例如 package.subpackage 。软件包前缀和对应的位置如下:

软件包前缀 位置
android.hardware.* hardware/interfaces/*
android.frameworks.* frameworks/hardware/interfaces/*
android.system.* system/hardware/interfaces/*
android.hidl.* system/libhidl/transport/*

例如 package [email protected] 软件包:
可以在 hardware/interfaces/example/extension/light/2.0 下找到。软件包目录中包含扩展名为 .hal 的文件,每个文件均必须包含一个指定文件所属的软件包和版本的 package 语句;文件 types.hal(如果存在)并不定义接口,而是定义软件包中每个接口可以访问的数据类型。

版本编号

软件包分版本,用两个整数表示: major.minor :

  • Major 版本不向后兼容
    递增 Major 版本号将会使 Minor 版本号重置为 0 。
  • Minor 版本向后兼容
    如果递增 Minor 版本号,则意味着较新版本完全向后兼容之前的版本。可以添加新的数据结构和方法,但不能更改现有的数据结构或方法签名。

可同时在一台设备上提供 HAL 的多个 Major 或 Minor 版本。不过 Minor 版本应优先于 Major 版本,因为与之前的 Minor 版本接口一起使用的客户端代码也适用于同一接口的后续 Minor 版本。

支持的注解

主要用于 VTS 测试

  • @callflow
    next=
  • @entry
  • @exit

hal 定义

1
2
3
4
5
6
7
8
interface IBar extends IFoo { // IFoo is another interface
    // embedded types
    struct MyStruct {/*...*/};

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

不含显式 extends 声明的接口会从 [email protected]::IBase (类似于 Java 中的 java.lang.Object )隐式扩展。隐式导入的 IBase 接口声明了多种不应也不能在用户定义的接口中重新声明或以其他方式使用的预留方法,也就是说 hal 中不存在重载和重写!

接口只能扩展一个其他接口(不支持多重继承),具有非零 Minor 版本号的软件包中的每个接口必须扩展一个以前版本的软件包中的接口:

  • 存在 1.2 版本的软件包 original 中的接口 IFoo
  • 小版本 Minor 升级到 1.3 版本,软件包 original 中的接口 IFoo 必须继承 1.2 版本的 IFoo
  • 假设大版本 Major 升级到 4.0 版本,而软件包 derivative 中的接口 IBar 是继承于 1.2 版本的软件包 original 中的接口 IFoo
  • 小版本 Minor 升级到 4.1 版本, IBar 必须扩展 4.0 版本的 IBar ;而不能继承 1.3 版本的 IFoo ,因为它是与 1.2 版本的 IFoo 绑定的
  • 大版本 Major 再次升级到 5.0 版本,此时 IBar 可以直接 1.3 版本的 `IFoo

接口扩展并不意味着生成的代码中存在代码库依赖关系或跨 HAL 包含关系,接口扩展只是在 HIDL 级别导入数据结构和方法定义。HAL 中的每个方法必须在相应 HAL 中重新实现。

import 导入

import 语句是用于访问其他软件包中的软件包接口和类型的 HIDL 机制,存在两种导入情况:

  • import 语句位于 types.hal 文件中时,导入的内容对整个软件包可见,属于软件包级导入
  • improt 语句位于 custom.hal 接口文件中时,导入的内容只对当前接口文件可见,属于接口级导入

import 语句后,根据导入值分三种情况:

  • 完整软件包导入
    导入值是一个软件包名称和版本,则系统会将整个软件包导入,即可访问整个软件包的接口或类型。
  • 接口导入
    导入值是一个接口文件,则导入该接口以及 types.hal 两个文件。
  • 类型导入
    导入值是 types.hal 中定义的某个类型,则仅将该类型导入,而 types.hal 中的其他类型不导入。
1
2
3
import [email protected];            // 导入整个软件包
import [email protected]::IQuux; // 导入接口文件和 types.hal
import [email protected]::types; // 仅仅导入 types.hal 中定义的 types 类型

接口哈希 Hash 和版本控制文件 current.txt

HIDL 接口哈希,是一种旨在防止意外更改接口并确保接口更改经过全面审查的机制。因为 HIDL 接口带有版本编号,也就是说接口一经发布便不得再更改。

软件包根目录必须有版本控制文件 current.txt ;如果创建一个软件包路径,如 -r vendor.awesome:vendor/awesome/interfaces ),则还应创建文件 $ANDROID_BUILD_TOP/vendor/awesome/interfaces/current.txt 。
current.txt 必须包含接口哈希及接口全限定名称,比如 system/libhidl/transport/current.txt 文件内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Do not change this file except to add new interfaces. Changing
# pre-existing interfaces will fail VTS and break framework-only OTAs

# HALs released in Android O

fc6c***da68 [email protected]::IAllocator
bdda***0fd9 [email protected]::IBase
500e***7567 [email protected]::types
4d04***0d3f [email protected]::IServiceManager
5055***50fb [email protected]::IServiceNotification
2b88***27c8 [email protected]::IMapper
4632***a16b [email protected]::IMemory
7c9f***4b7c [email protected]::ITokenManager

# HALs released in Android O-MR1

0b94***4ef6 [email protected]::IServiceManager

current.txt 中的内容按照 Android 大版本来分隔,比如上半部分为 Android O ,下半部分为 Android O-MR1 ,厂商自定义的接口文件也应该遵循这个格式。

可以手动将哈希添加到 current.txt 文件中,也可以使用 hidl-gen 加上参数 -Lhash 选项添加。
如下为针对类型文件、接口、整个软件包生成哈希:

1
2
3
4
5
6
7
8
$ hidl-gen -L hash -r vendor.awesome:vendor/awesome/hardware/interfaces -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport [email protected]::types
9626fd18...f9d298a6 [email protected]::types
$ hidl-gen -L hash -r vendor.awesome:vendor/awesome/hardware/interfaces -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport [email protected]::INfc
07ac2dc9...11e3cf57 [email protected]::INfc
$ hidl-gen -L hash -r vendor.awesome:vendor/awesome/hardware/interfaces -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport [email protected]
9626fd18...f9d298a6 [email protected]::types
07ac2dc9...11e3cf57 [email protected]::INfc
f2fe5442...72655de6 [email protected]::INfcClientCallback

如果是新添加的 hal 软件包,则将生成哈希及对应接口文件,一并写入 current.txt 文件中:

1
$ hidl-gen -L hash -r vendor.awesome:vendor/awesome/hardware/interfaces -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport [email protected] >> vendor/awesome/hardware/interfaces/current.txt

接口文件的哈希,可以通过调用 IBase::getHashChain 来查看。 hidl-gen 编译接口时,会检查 HAL 软件包根目录中的 current.txt 文件,以查看 HAL 是否已被更改:

  • 如果没有找到对应的哈希值,则继续编译,并认为接口没有发布
  • 如果找到了对应哈希值,则做相应检查:
    • 如果接口与哈希值匹配,则继续编译
    • 如果接口与哈希值不匹配,则意味着接口被更改了,停止编译

所以,如果接口文件处于调试阶段,需要等调试完成后再发布到 current.txt 中;而接口文件一旦发布,将无法更改;如果想更改接口,必须升级版本号,也就是重新定义接口。比如 camera 相关接口的修改,必须升级版本号:

1
2
3
4
5
6
7
8
// hardware/interfaces/current.txt
c170***2d62f [email protected]::ICameraDevice
78e9***44076 [email protected]::ICameraDeviceCallback
28f0***44566 [email protected]::ICameraDevicePreviewCallback
4db4***352a3 [email protected]::types
b32f***55918 [email protected]::ICameraDevice
63bf***b3b21 [email protected]3.2::ICameraDeviceCallback
0fa3***7208e [email protected]::ICameraDeviceSession

数据传递

数据在传递过程中:

  • .hal 文件接口中定义的方法默认为阻塞模式,如果需要采用非阻塞式则在方法前面使用关键字 oneway
  • 方法调用和回调只能接受 in 参数,并且不支持 out, inout 参数
  • 方法在数据传递时超过 4KB 以上的数据便被认为是过度调用;同时跨进程调用是基于 Binder 机制,所以总的数据传输不能超过 1MB

对于跨进程通信, HIDL 只使用参数回调函数,避免了内存所有权的棘手问题,特别是不能有效通过方法返回的值可以直接通过回调函数返回。这样既不将数据传递到 HIDL ,也不从 HIDL 接收数据,改变数据的所有权。数据只需要在被调用函数的持续时间内存在,并且可以在被调用函数返回后立即销毁。

HIDL 如果不使用 Binder RPC ,可以通过两种方法来转移数据:

  • 共享内存
    分配的内存通过映射来共享。
  • 快速消息队列 FMQ
    HIDL 提供了一种可实现无等待消息传递的模板化消息队列类型。

快速消息队列 FMQ

HIDL 的远程过程调用 RPC 基础架构使用 Binder 机制,这意味着调用涉及开销、需要内核操作,并且可以触发调度程序操作。不过对于必须在开销较小且无内核参与的进程之间传输数据的情况, HIDL 提供了快速消息队列 FMQ 系统。
FMQ 会创建具有所需属性的消息队列, MQDescriptorSync, MQDescriptorUnsync 对象可通过 HIDL RPC 调用发送,并可供接收进程用于访问消息队列。

它在直通式或绑定式模式下不使用内核或调度程序,从而创建可以借助内置 HIDL 类型 MQDescriptorSync 或 MQDescriptorUnsync 的参数通过 RPC 传递的对象。

MessageQueue 支持的队列类型 flavor 有两种:

  • 未同步队列 flavor: kSynchronizedReadWrite
    可以溢出,并且可以有多个读取器;每个读取器都必须及时读取数据,否则数据将会丢失。未同步队列只有一个写入器,但可以有任意多个读取器。此类队列有一个写入位置;不过,每个读取器都会跟踪各自的独立读取位置。执行写入操作一定会成功(不会检查是否出现溢出情况),但前提是写入的内容不超出配置的队列容量(如果写入的内容超出队列容量,则操作会立即失败)。由于各个读取器的读取位置可能不同,因此每当新的写入操作需要空间时,系统都允许数据离开队列,而无需等待每个读取器读取每条数据。
  • 已同步队列 flavor: kUnsynchronizedWrite
    不能溢出,并且只能有一个读取器。已同步队列有一个写入器和一个读取器,其中写入器有一个写入位置,读取器有一个读取位置。写入的数据量不可能超出队列可提供的空间;读取的数据量不可能超出队列当前存在的数据量。

这两种队列都不能下溢(从空队列进行读取将会失败),并且只能有一个写入器。

FMQ 相关源码路径如下,其中 MessageQueue 主要是模板,所以定义和代码实现都在该 .h 文件中:

1
2
system/libfmq/include/fmq/MessageQueue.h
system/libhidl/base/include/hidl/MQDescriptor.h

队列类型是在 MQDescriptor.h 中定义的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// MQDescriptor.h
enum MQFlavor : uint32_t {
  /*
   * represents the wait-free synchronized flavor of the
   * FMQ. It is intended to be have a single reader and single writer.
   * Attempts to overflow/underflow returns a failure.
   */
  kSynchronizedReadWrite = 0x01,
  /*
   * represents the flavor of FMQ where writes always succeed. 
   * This flavor allows one writer and many readers. A read operation
   * can detect an overwrite and reset the read counter.
   */
  kUnsynchronizedWrite = 0x02
};

创建快速队列示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <fmq/MessageQueue.h>
using android::hardware::kSynchronizedReadWrite;
using android::hardware::kUnsynchronizedWrite;
using android::hardware::MQDescriptorSync;
using android::hardware::MQDescriptorUnsync;
using android::hardware::MessageQueue;
....

// For a synchronized non-blocking FMQ
mFmqSynchronized =
  new (std::nothrow) MessageQueue<uint16_t, kSynchronizedReadWrite>
      (kNumElementsInQueue);

// For an unsynchronized FMQ that supports blocking
mFmqUnsynchronizedBlocking =
  new (std::nothrow) MessageQueue<uint16_t, kUnsynchronizedWrite>
      (kNumElementsInQueue, true /* enable blocking operations */);
  • MessageQueue<T, flavor>(numElements) 初始化程序负责创建并初始化支持消息队列功能的对象
  • MessageQueue<T, flavor>(numElements, configureEventFlagWord) 初始化程序负责创建并初始化支持消息队列功能和阻塞的对象
  • flavor 可以是 kSynchronizedReadWrite 同步队列或 kUnsynchronizedWrite 未同步队列
  • uint16_t 可以是任意不涉及嵌套式缓冲区(无 string 或 vec 类型)、句柄或接口的 HIDL 定义的类型
  • kNumElementsInQueue 表示队列的大小(以条目数表示);它用于确定将为队列分配的共享内存缓冲区的大小

内存共享

HIDL 共享内存 MemoryBlock 是构建在 hidl_memory , HIDL @1.0::IAllocator 和 HIDL @1.0::IMapper 之上的抽象层,专为有多个内存块共用单个内存堆的 HIDL 服务而设计。
也就是说, HIDL 共享内存块只是提供了一种使用思路,而不是像 FMQ 提供了具体实现;使用 MemoryBlock 可显著减少 mmap/munmap 数量和用户空间细分错误,从而提升性能。架构图如下,核心思想是多个内存块共用单个内存堆 hidl_memory :

Android HIDL源码分析

基本数据类型

HIDL 基本数据类型及对应处理,都是在 system/libhidl/base 目录中实现的,基本数据类型的定义文件为 HidlSupport.h ,目录结构为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
├── Android.bp
├── HidlInternal.cpp
├── HidlSupport.cpp
├── include
│   └── hidl
│       ├── HidlInternal.h
│       ├── HidlSupport.h
│       ├── MQDescriptor.h
│       ├── Status.h
│       ├── SynchronizedQueue.h
│       └── TaskRunner.h
├── Status.cpp
└── TaskRunner.cpp

2 directories, 11 files

hidl_death_recipient

服务死亡通知,如果客户端注册该通知,当服务端断开时,会发出通知。

1
2
3
4
struct hidl_death_recipient : public virtual RefBase {
    virtual void serviceDied(uint64_t cookie,
       const ::android::wp<::android::hidl::base::V1_0::IBase>& who) = 0;
};

hidl_handle

句柄基本类型,是对 native_handle_t 句柄的封装:

1
2
3
4
5
6
7
8
9
struct hidl_handle {
    ...
    // explicit conversion
    const native_handle_t *getNativeHandle() const;
private:
    ...
    details::hidl_pointer<const native_handle_t> 
        mHandle __attribute__ ((aligned(8)));
};

调用传递 hidl_handle 对象(复合类型的*或一部分)的 HIDL 接口方法时,其中包含的文件描述符的所有权如下所述:

  • 将 hidl_handle 对象作为参数传递的调用程序会保留对其封装的 native_handle_t 中包含的文件描述符的所有权;该调用程序在完成对这些文件描述符的操作后,必须将这些文件描述符关闭
  • 通过将 hidl_handle 对象传递到 _cb 函数来返回该对象的进程会保留对该对象封装的 native_handle_t 中包含的文件描述符的所有权;该进程在完成对这些文件描述符的操作后,必须将这些文件描述符关闭
  • 接收 hidl_handle 的传输拥有对相应对象封装的 native_handle_t 中的文件描述符的所有权;接收器可在事务回调期间按原样使用这些文件描述符,但如果想在回调完成后继续使用这些文件描述符,则必须克隆原生句柄。事务完成时, transport 将自动对文件描述符执行 close 操作

HIDL 不支持在 Java 中使用句柄。

hidl_string

HIDL 字符串;通过 HIDL 接口将字符串传递到 Java 或从 Java 传递字符串将会导致字符集转换,而此项转换可能无法精确保留原始编码。

1
2
3
4
5
6
7
8
9
10
11
struct hidl_string {
    ...
    const char *c_str() const;
    size_t size() const;
    bool empty() const;
    ...
private:
    details::hidl_pointer<const char> mBuffer;
    uint32_t mSize;  // NOT including the terminating '\0'.
    ...
};

hidl_memory

memory 类型用于表示 HIDL 中未映射的共享内存。

1
2
3
4
5
6
7
struct hidl_memory {
    ...
private:
    hidl_handle mHandle __attribute__ ((aligned(8)));
    uint64_t mSize __attribute__ ((aligned(8)));
    hidl_string mName __attribute__ ((aligned(8)));
};

hidl_vec

vec<T> 模板用于表示包含 T 实例且大小可变的缓冲区。 T 可以是任何由 HIDL 提供的或由用户定义的类型,句柄除外。( vec<T> 的 vec<> 将指向 vec<T> 结构体数组,而不是指向内部 T 缓冲区数组。) T 可以是以下项之一:

  • 基本类型(例如 uint32_t )
  • 字符串
  • 用户定义的枚举
  • 用户定义的结构体
  • 接口,或 interface 关键字( vec<IFoo>,vec<interface> 仅在作为*参数时受支持)
  • 句柄
  • bitfield<U>
  • vec<U> ,其中 U 可以是此列表中的任何一项,接口除外(例如, vec<vec<IFoo>> 不受支持)
  • U[](有大小的 U 数组),其中 U 可以是此列表中的任何一项,接口除外

hidl_array

表示多维数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template<typename T, size_t SIZE1, size_t... SIZES>
struct hidl_array {
    ...
private:
    T mBuffer[elementCount()];
};

// An array of T's. Assumes that T::operator=(const T &) is defined.
template<typename T, size_t SIZE1>
struct hidl_array<T, SIZE1> {
    ...
private:
    T mBuffer[SIZE1];
};

hidl_version

HIDL 版本号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct hidl_version {
    ...
    constexpr uint16_t get_major() const { return mMajor; }
    constexpr uint16_t get_minor() const { return mMinor; }

private:
    uint16_t mMajor;
    uint16_t mMinor;
};

inline android::hardware::hidl_version make_hidl_version(
        uint16_t major, uint16_t minor) {
    return hidl_version(major,minor);
}

.hal 文件自动生成代码

基本步骤

比如 .hal 文件准备添加到 hardware/interface 目录下:

  • 新建包名对应的文件夹,比如 myintere
  • 在包文件夹下新建主次版本对应的文件夹,比如 1.0
  • 在版本目录下定义并编写 .hal 文件
  • 执行 .hal 文件所在位置的 update-makefiles.sh 脚本(实际还是调用的 hidl-gen ),自动生成 Android.bp/mk 文件
    因为是在 hardware/interface 目录下添加的,则执行: ./hardware/interfaces/update-makefiles.sh ,自动生成对应 Android.bp 。
1
2
3
4
5
6
// 1. 新建目录
[email protected]:~/**/hardware/interfaces/tests/$ mkdir -p myintere/1.0
// 2. 新建 .hal 文件
[email protected]:~/**/hardware/interfaces/tests/myintere/1.0$ touch IMyCallback.hal  IMyIntere.hal
// 3. 根目录下执行 update-makefiles.sh 脚本
[email protected]:~/**/$ ./hardware/interfaces/update-makefiles.sh

hidl-gen 规则

hidl-gen 转换规则以及生成哪些文件都是在自动生成的 Android.bp 文件中定义的; hidl, aidl 文件自动生成 java, cpp, h 文件工具源码都是在 system/tools/ 目录下:

1
2
3
system/tools/
├── aidl
└── hidl

绑定式模式使用 hidl-gen 编译器并以 IFoo.hal 接口文件作为输入,它具有以下自动生成的文件:

Android HIDL源码分析

由编译器生成的文件:

  • 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 存根 Stub (服务器端)实现。开发者不得直接引用此类。
  • FooAll.cpp
    包含 HwBinder 代理和 HwBinder 存根 Stub 的实现的类。当客户端调用接口方法时,代理会自动从客户端封送参数,并将事务发送到绑定内核驱动程序,该内核驱动程序会将事务传送到另一端的存根 Stub(该存根随后会调用实际的服务器实现)。

生成的这些文件结构类似于由 aidl-cpp 生成的文件。独立于 HIDL 使用的 RPC 机制的自动生成的文件是 IFoo.h ,其他所有文件都与 HIDL 使用的 HwBinder RPC 机制相关联。因此客户端和服务器实现不得直接引用除 IFoo 之外的任何内容。为了满足这项要求,只包含 IFoo.h 并链接到生成的共享库。

在直通式模式中,在编译 IFoo.hal 文件时,标头文件除了用于 Binder 通信的标头之外, hidl-gen 还会生成一个额外的直通标头文件 BsFoo.h ;此标头定义了会被执行 dlopen 操作的函数。由于直通式 HAL 在它们被调用的同一进程中运行,因此在大多数情况下,直通方法由直接函数调用(同一线程)来调用。 oneway 方法在各自的线程中运行,因为它们不需要等待 HAL 来处理它们(这意味着,在直通模式下使用 oneway 方法的所有 HAL 对于线程必须是安全的)。 BsFoo.h 文件类似于 BpFoo.h ,不过所需函数是直接调用的,并未使用 Binder 传递调用 IPC 。未来 HAL 的实现可能提供多种实现结果,例如 FooFast HAL 和 FooAccurate HAL 。在这种情况下,系统会针对每个额外的实现结果创建一个文件(例如 PTFooFast.cpp 和 PTFooAccurate.cpp )。

hidl-gen 语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
usage: hidl-gen [-p <root path>] -o <output path> -L <language> (-r <interface root>)+ [-t] fqname+
    -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
       java            : (internal) Generates Java library for talking to HIDL interfaces in Java.
       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        : (internal) Generates makefiles for -Ljava and -Ljava-constants.
       androidbp       : (internal) Generates Soong bp files for -Lc++-headers and -Lc++-sources.
       androidbp-impl  : Generates boilerplate bp files for implementation created with -Lc++-impl.
       hash            : Prints hashes of interface in `current.txt` format to standard out.
    -o <output path>: Location to output files.
    -p <root path>: Android build root, defaults to $ANDROID_BUILD_TOP or pwd.
    -r <package:path root>: E.g., android.hardware:hardware/interfaces.
    -t: generate build scripts (Android.bp) for tests.
  • fqName : 完全限定名
  • -L :指定生成的语言包
  • -r : hal 文件路径

文件路径映射

每个 hal 文件都可以通过软件包根目录映射及其完全限定名称找到,软件包根目录以参数 -r android.hardware:hardware/interfaces 的形式指定给 hidl-gen 。例如:

1
hidl-gen -r vendor.awesome:some/device/independent/path/interfaces [email protected]::IFoo
  • 根目录参数为 vendor.awesome:some/device/independent/path/interfaces
    表示 vendor.awesome 对应的目录路径为:
    $ANDROID_BUILD_TOP/some/device/independent/path/interfaces 。
  • 软件包为 [email protected]::IFoo
    表示接口文件应该位于 vendor.awesome 目录路径下的 foo 接口的 1.0 版本下的 IFoo.hal 文件,即:
    $ANDROID_BUILD_TOP/some/device/independent/path/interfaces/foo/1.0/IFoo.hal 。

软件包路径映射不得重复,比如如果同时存在 -rsome.package:$PATH_A 和 -rsome.package:$PATH_B ,则 $PATH_A 必须等于 $PATH_B 才能实现一致的接口目录(这也能让接口版本控制起来更简单)。

代码文件生成

  • 定义 hal 文件组

    1
    2
    3
    4
    5
    6
    7
    
    filegroup {
        name: "[email protected]_hal",
        srcs: [
            "ICameraProvider.hal",
            "ICameraProviderCallback.hal",
        ],
    }
    
  • 生成头文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    // hardware/interfaces/camera/provider/2.4/Android.bp
    genrule {
        name: "android.hardware.camera.[email protected]_genc++_headers",
        tools: ["hidl-gen"],
        cmd: "$(location hidl-gen) -o $(genDir) -Lc++-headers -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport [email protected]",
        srcs: [
            ":[email protected]_hal",    // hal 文件组
        ],
        out: [
            "android/hardware/camera/provider/2.4/ICameraProvider.h",
            "android/hardware/camera/provider/2.4/IHwCameraProvider.h",
            "android/hardware/camera/provider/2.4/BnHwCameraProvider.h",
            "android/hardware/camera/provider/2.4/BpHwCameraProvider.h",
            "android/hardware/camera/provider/2.4/BsCameraProvider.h",
            "android/hardware/camera/provider/2.4/ICameraProviderCallback.h",
            "android/hardware/camera/provider/2.4/IHwCameraProviderCallback.h",
            "android/hardware/camera/provider/2.4/BnHwCameraProviderCallback.h",
            "android/hardware/camera/provider/2.4/BpHwCameraProviderCallback.h",
            "android/hardware/camera/provider/2.4/BsCameraProviderCallback.h",
        ],
    }
    
  • 生成 CPP 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    // hardware/interfaces/camera/provider/2.4/Android.bp
    genrule {
        name: "[email protected]_genc++",
        tools: ["hidl-gen"],
        cmd: "$(location hidl-gen) -o $(genDir) -Lc++-sources -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport [email protected]",
        srcs: [
            ":[email protected]_hal",    // hal 文件组
        ],
        out: [
            "android/hardware/camera/provider/2.4/CameraProviderAll.cpp",
            "android/hardware/camera/provider/2.4/CameraProviderCallbackAll.cpp",
        ],
    }
    

HIDL 服务新增函数

HIDL 服务在代码自动生成,每个服务对应的头文件中,比如 IFoo.h 都会自动添加如下几个函数:

1
2
3
4
5
6
7
8
9
// 获取服务
static ::android::sp<IFoo> getService(
    const std::string &serviceName="default", bool getStub=false);
// 注册服务
::android::status_t registerAsService(
    const std::string &serviceName="default");
// 服务注册成功后的通知
static bool registerForNotifications( const std::string &serviceName,
        const ::android::sp<IServiceNotification> &notification);
  • getService :获取服务
  • registerAsService :注册服务
  • registerForNotifications :注册通知监听事件,服务注册成功后会发出通知

服务注册与获取

服务端注册 Binder 服务

HIDL 接口使用 IInterface::registerAsService 来注册 Binder 服务,注册的名称不需要与接口或软件包名称相关。如果没有指定名称,则默认为 default ; HIDL 接口调用 android::hardware::IInterface::getInterfaceVersion 可以查看当前接口的版本。

1
2
3
4
5
sp<IFoo> myFoo = new Foo();
status_t status = myFoo->registerAsService();               // 默认值为 "default"
sp<IFoo> anotherFoo = new Foo();
status_t anotherStatus = 
    anotherFoo->registerAsService("another_foo_service");   // 显示指定服务名称

Binder 化直通式

首先要理解 Binder 化直通式 HAL ,指的是 HAL 服务端注册的方式:以直通的方式加载服务端,并向 hwservicemanager 注册该服务;通过函数 defaultPassthroughServiceImplementation<IFoo> 来注册:

1
2
defaultPassthroughServiceImplementation<ICameraProvider>(
    "legacy/0", /*maxThreads*/ 6);

下面是源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// LegacySupport.h
namespace android {
namespace hardware {

/**
 * Registers passthrough service implementation.
 */
template<class Interface>
__attribute__((warn_unused_result))
status_t registerPassthroughServiceImplementation(
        std::string name = "default") {
    // 直通式加载服务端
    sp<Interface> service = 
        Interface::getService(name, true /* getStub */);

    if (service == nullptr) {
        ALOGE(...);
        return EXIT_FAILURE;
    }

    LOG_FATAL_IF(...);

    // 向 hwservicemanager 注册服务
    status_t status = service->registerAsService(name);

    if (status == OK) {
        ALOGI(...);
    } else {
        ALOGE(...);
    }

    return status;
}


template<class Interface>
__attribute__((warn_unused_result))
status_t defaultPassthroughServiceImplementation(std::string name,
                                            size_t maxThreads = 1) {
    configureRpcThreadpool(maxThreads, true);
    status_t result = 
        registerPassthroughServiceImplementation<Interface>(name);

    if (result != OK) {
        return result;
    }

    joinRpcThreadpool();
    return 0;
}
template<class Interface>
__attribute__((warn_unused_result))
status_t defaultPassthroughServiceImplementation(size_t maxThreads = 1) {
    return defaultPassthroughServiceImplementation<Interface>(
        "default", maxThreads);
}

registerPassthroughServiceImplementation 函数中,在 getService 时,参数 getStub 为 ture ,即通过直通式加载服务端;拿到直通式服务端 Interface 后,又通过 registerAsService 向 hwservicemanager 注册该服务。
即加载服务端的当前进程,作为服务进程;客户端从 hwservicemanager 可以查到服务端,并通过 Binder 和服务进程通信。

客户端获取服务

HIDL 接口因为有版本区分,所以每个接口文件都可以被认为是单独的、唯一的。因此 IFooService 版本 1.1 和 IFooService 版本 2.2 都可以注册为 foo_service ,并且两个接口上的 getService("foo_service") 都可获取该接口的已注册服务。因此在大多数情况***册或发现服务均无需提供名称参数(也就是说名称为 default )。

1
2
3
4
5
6
7
8
// C++
sp<V1_1::IFooService> service = V1_1::IFooService::getService();
sp<V1_1::IFooService> alternateService = 
    V1_1::IFooService::getService("another_foo_service");
// Java
V1_1.IFooService service = V1_1.IFooService.getService(true /* retry */);
V1_1.IFooService alternateService = 
    V1_1.IFooService.getService("another", true /* retry */);

客户端通过 getService 来获取服务端,而 getService 是每个 .hal 文件在自动生成源码时,都会自动添加的函数,函数原型为(这里以 IServiceManager.hal 为例):
static ::android::sp<IServiceManager> getService(const std::string &serviceName="default", bool getStub=false);
除了 <> 里的接口文件类型不一样,所有的 .hal 文件都会生成同样的代码;而它的实现则是在对应的 .cpp 文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// ServiceManagerAll.cpp
::android::sp<IServiceManager> IServiceManager::getService(
    const std::string &serviceName, const bool getStub) {
    using ::android::hardware::defaultServiceManager;
    using ::android::hardware::getPassthroughServiceManager;
    ...

    sp<IServiceManager> iface = nullptr;
    // 获取绑定式 IServiceManager
    const sp<IServiceManager> sm = defaultServiceManager();
    if (sm == nullptr) {
        ALOGE("getService: defaultServiceManager() is null");
        return nullptr;
    }
    ...
    for (int tries = 0; !getStub && 
            (vintfHwbinder || (vintfLegacy && tries == 0)); tries++) {
        ...
        // 通过绑定式 IServiceManager 获取服务端
        Return<sp<::android::hidl::base::V1_0::IBase>> ret =
                sm->get(IServiceManager::descriptor, serviceName);
        ...
        sp<::android::hidl::base::V1_0::IBase> base = ret;
        ...
        // 强制转换为服务端对应类型,这里以 IServiceManager 为例
        Return<sp<IServiceManager>> castRet = 
            IServiceManager::castFrom(base, true /* emitError */);
        ...
        iface = castRet;
        ...
        return iface;
    }
    
    if (getStub || vintfPassthru || vintfLegacy) {
        // 获取直通式 IServiceManager
        const sp<IServiceManager> pm = getPassthroughServiceManager();
        if (pm != nullptr) {
            // 通过直通式 IServiceManager 获取服务端
            Return<sp<::android::hidl::base::V1_0::IBase>> ret =
                    pm->get(IServiceManager::descriptor, serviceName);
            if (ret.isOk()) {
                sp<::android::hidl::base::V1_0::IBase> baseInterface = ret;
                if (baseInterface != nullptr) {
                    // 强制转换为服务端对应类型,这里以 IServiceManager 为例
                    iface = IServiceManager::castFrom(baseInterface);
                    // 直通模式中如果 getStub 为 false,返回的是 BsServiceManager
                    if (!getStub || trebleTestingOverride) {
                        iface = new BsServiceManager(iface);
                    }
                }
            }
        }
    }
    return iface;
}

getService 主要是根据参数 getStub 的值来决定采用哪种方式获取服务端:

  • 为 false 时
    通过 defaultServiceManager 获取服务端,即绑定式
  • 为 true 时
    通过 getPassthroughServiceManager 获取服务端,即直通式

这两个函数下面会详细介绍。

服务死亡通知

客户端需要注册服务终止通知接收器,当服务终止时,客户端收到通知;接收器需要继承 hidl_death_recipient 子类,并实现对应的方法。

1
2
3
4
5
6
7
8
9
class IMyDeathReceiver : hidl_death_recipient {
  virtual void serviceDied(uint64_t cookie,
                           wp<IBase>& service) override {
    log("RIP service %d!", cookie);  // Cookie should be 42
  }
};
....
IMyDeathReceiver deathReceiver = new IMyDeathReceiver();
m_importantService->linkToDeath(deathReceiver, 42);

源码结构

源码目录速查表

实现源码路径为:

1
2
3
system/libhidl
system/libhwbinder
system/hwservicemanager

libhidl

主要包含三个动态库: libhidlbase, libhidltransport, libhidlmemory ;其中 libhidlbase 主要是 hidl 的基本类型相关; libhidlmemory 是封装了 memory 通过 IMapper 来映射;libhidltransport 包含直通式 IServiceManager.hal 的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
libhidl/
├── base
│   ├── Android.bp
│   ├── HidlInternal.cpp
│   ├── HidlSupport.cpp
│   ├── include
│   ├── Status.cpp
│   └── TaskRunner.cpp
├── libhidlmemory
│   ├── Android.bp
│   ├── include
│   └── mapping.cpp
├── transport
│   ├── allocator
│   ├── Android.bp
│   ├── base
│   ├── current.txt
│   ├── HidlBinderSupport.cpp
│   ├── HidlTransportSupport.cpp
│   ├── HidlTransportUtils.cpp
│   ├── include
│   ├── manager
│   ├── memory
│   ├── ServiceManagement.cpp
│   ├── Static.cpp
│   └── token
└── ...

libhwbinder

对应生成 libhwbinder 库,是 .hal 文件 Binder 通信相关库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
libhwbinder/
├── Android.bp
├── Binder.cpp
├── BpHwBinder.cpp
├── BufferedTextOutput.cpp
├── Debug.cpp
├── IInterface.cpp
├── include
│   └── hwbinder
├── IPCThreadState.cpp
├── MODULE_LICENSE_APACHE2
├── NOTICE
├── OWNERS
├── Parcel.cpp
├── PREUPLOAD.cfg
├── ProcessState.cpp
├── Static.cpp
├── TextOutput.cpp
└── vts
    ├── OWNERS
    └── performance

hwservicemanager

生成可执行文件 hwservicemanager ,是 HIDL 绑定式服务的大管家。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
hwservicemanager/
├── AccessControl.cpp
├── AccessControl.h
├── Android.bp
├── HidlService.cpp
├── HidlService.h
├── hwservicemanager.rc
├── hwservicemanagerTest.cpp
├── MODULE_LICENSE_APACHE2
├── NOTICE
├── OWNERS
├── service.cpp
├── ServiceManager.cpp
├── ServiceManager.h
├── TokenManager.cpp
├── TokenManager.h
├── Vintf.cpp
└── Vintf.h

libhidl 目录

libhidlbase 库

libhidlbase 库对应的源码列表如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
base/
├── Android.bp
├── HidlInternal.cpp
├── HidlSupport.cpp
├── include
│   └── hidl
│       ├── HidlInternal.h
│       ├── HidlSupport.h
│       ├── MQDescriptor.h
│       ├── Status.h
│       ├── SynchronizedQueue.h
│       └── TaskRunner.h
├── Status.cpp
└── TaskRunner.cpp
  • HidlInternal
    hidl 内部使用的一些类、字符串定义等等。 hal 客户端/服务端都不会使用。
  • HidlSupport
    hidl 支持的基本数据类型(不包含 C++, Java 类型)。
  • MQDescripto
    快速消息队列 fmq 中相关类型。
  • Status
    表示 hidl 通信的状态和返回值,比如成功、失败、异常等等。
  • SynchronizedQueue
    同步队列。
  • TaskRunner
    后台无限循环的任务,使用 SynchronizedQueue 队列保存任务。

libhidlmemory 库

libhidlmemory 库对应的源码列表如下:

1
2
3
4
5
6
libhidlmemory/
├── Android.bp
├── include
│   └── hidlmemory
│       └── mapping.h
└── mapping.cpp

就一个有效文件 mapping ,对应头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
// mapping.h
namespace android {
namespace hardware {

/**
 * Returns the IMemory instance corresponding to a hidl_memory object.
 * If the shared memory cannot be fetched, this returns nullptr.
 */
sp<android::hidl::memory::V1_0::IMemory> mapMemory(
    const hidl_memory &memory);

}  // namespace hardware
}  // namespace android

只包含一个功能函数:将 hidl_memory 内存映射后,返回对应的 IMemory 。 libhidlmemory 库是对 system/libhidl/transport/memory 的封装,而 transport/memory 则是具体的实现。

android.hidl.* 软件包

system/libhidl/transport/* 目录下包含 5 个软件包,这些软件包以 android.hidl.* 开头,它们是 hidl 的基础软件包:

  • base
    定义了 IBase.hal ,它的功能类似 Java Object ,也就是说 Ibase.hal 是所有 hal 文件的父类。每个 hal 文件在自动生成源码时,都会自动添加 IBase 中的函数及其默认实现。
  • allocator
    定义了内存分配接口 IAllocator.hal ,内存分配的具体实现为 AshmemAllocator 。
  • memory
    定义了内存映射接口 IMapper.hal 以及内存接口 IMemory.hal ;而内存映射的具体实现为 AshmemMapper ,内存块 IMemory 的具体实现为 AshmemMemory 。
  • manager
    定义了 IServiceManager.hal 相关功能接口;它有两个实现:直通式是在 system/libhidl/ServiceManagement.cpp 中实现的,对应 libhidltransport 库;绑定式是在 system/hwservicemanager/ServiceManager.cpp 中实现的,对应 hwservicemanager 可执行文件。
  • token
    定义了接口 ITokenManager.hal ,它可以将 hidl 接口转换为 token 方便跨进程传输;该接口是在 hwservicemanager 中实现的。

hidl 相关的内存分配和映射,都是使用的匿名共享内存机制 Ashmem ; IServiceManager 服务管理分为直通式和绑定式,是在不同文件中实现的。

IBase 文件路径为 system/libhidl/transport/base/1.0/IBase.hal ,是所有 hal 的基础接口,类似 Java 中的 Object 类;因为 hidl 中不存在重写和重载,所以自定义的 hal 文件中函数名不能和下面的重复:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package [email protected];

interface IBase {

    // 测试服务是否正在运行
    ping();

    /*
     * Provides run-time type information for this object.
     * For example, for the following interface definition:
     *     package [email protected];
     *     interface IParent {};
     *     interface IChild extends IParent {};
     * return:interfaceChain on an IChild object must yield the following
     *     ["[email protected]::IChild",
     *      "[email protected]::IParent"
     *      "[email protected]::IBase"]
     */
    interfaceChain() generates (vec<string> descriptors);

    /*
     * Provides run-time type information for this object.
     * For example, for the following interface definition:
     *     package [email protected];
     *     interface IParent {};
     *     interface IChild extends IParent {};
     * Calling interfaceDescriptor on an IChild object must yield
     *     "[email protected]::IChild"
     *
     * @return descriptor a descriptor of the run-time type of the
     *         object (the first element of the vector returned by
     *         interfaceChain())
     */
    interfaceDescriptor() generates (string descriptor);

    oneway notifySyspropsChanged();
    linkToDeath(death_recipient recipient, uint64_t cookie) 
        generates (bool success);
    unlinkToDeath(death_recipient recipient) 
        generates (bool success);
    oneway setHALInstrumentation();
    getDebugInfo() generates (DebugInfo info);
    debug(handle fd, vec<string> options);

    /*
     * For example, for the following interface definition:
     *     package [email protected];
     *     interface IParent {};
     *     interface IChild extends IParent {};
     * return:interfaceChain on an IChild object must yield the following
     *     [(hash of IChild.hal),
     *      (hash of IParent.hal)
     *      (hash of IBase.hal)].
     *
     * SHA-256 is used as the hashing algorithm. Each hash has 32 bytes
     * according to SHA-256 standard.
     *
     * @return hashchain a vector of SHA-1 digests
     */
    getHashChain() generates (vec<uint8_t[32]> hashchain);
};

libhidltransport 库

libhidltransport 库中的头文件 #include <hidl/ServiceManagement.h> ,包含了几组重要函数,用来区分当前是客户端采用绑定式还是直通式来获取服务端:

1
2
3
4
5
6
7
// ServiceManagement.h
// These functions are for internal use by hidl. If you want to get ahold
// of an interface, the best way to do this is by calling IFoo::getService()
sp<::android::hidl::manager::V1_0::IServiceManager> defaultServiceManager();
sp<::android::hidl::manager::V1_1::IServiceManager> defaultServiceManager1_1();
sp<::android::hidl::manager::V1_0::IServiceManager> getPassthroughServiceManager();
sp<::android::hidl::manager::V1_1::IServiceManager> getPassthroughServiceManager1_1();
  • defaultServiceManager :绑定式服务管理 IServiceManager
  • getPassthroughServiceManager :直通式服务管理 IServiceManager

它们都是在 ServiceManagement.cpp 中实现的,先看绑定式源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// ServiceManagement.cpp
sp<IServiceManager1_0> defaultServiceManager() {
    return defaultServiceManager1_1();
}
sp<IServiceManager1_1> defaultServiceManager1_1() {
    {
        AutoMutex _l(details::gDefaultServiceManagerLock);
        if (details::gDefaultServiceManager != NULL) {
            return details::gDefaultServiceManager;
        }

        if (access("/dev/hwbinder", F_OK|R_OK|W_OK) != 0) {
            // HwBinder not available on this device or not accessible to
            // this process.
            return nullptr;
        }

        waitForHwServiceManager();

        while (details::gDefaultServiceManager == NULL) {
            details::gDefaultServiceManager =
                fromBinder<IServiceManager1_1, BpHwServiceManager, 
                        BnHwServiceManager>(
                    ProcessState::self()->getContextObject(NULL));
            if (details::gDefaultServiceManager == NULL) {
                LOG(ERROR) << "...";
                sleep(1);
            }
        }
    }

    return details::gDefaultServiceManager;
}

它的核心代码是 fromBinder 这个模板函数,这里需要注意 ProcessState::self()->getContextObject(NULL) 这句代码的含义是:获取 handle 为 0 的 IBinder ,而 handle 为 0 表示是 hwservicemanager 守护进程,后续在 hwservicemanager 进程中做详细介绍。这里 ProcessState, IPCThreadState 等,虽然都是在 libhwbinder 库中,实际上和 Framework Binder 中代码很多都是相同,实现的功能也大致相同。
fromBinder 是在 HidlBinderSupport.h 头文件中定义的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// HidlBinderSupport.h
template <typename IType, typename ProxyType, typename StubType>
sp<IType> fromBinder(const sp<IBinder>& binderIface) {
    using ::android::hidl::base::V1_0::IBase;
    using ::android::hidl::base::V1_0::BnHwBase;

    if (binderIface.get() == nullptr) {
        return nullptr;
    }
    if (binderIface->localBinder() == nullptr) {
        return new ProxyType(binderIface);
    }
    sp<IBase> base = static_cast<BnHwBase*>(binderIface.get())->getImpl();
    if (details::canCastInterface(base.get(), IType::descriptor)) {
        StubType* stub = static_cast<StubType*>(binderIface.get());
        return stub->getImpl();
    } else {
        return nullptr;
    }
}

模板代码表示,如果是远程访问,则新建代理对象即 BpHwServiceManager ,即客户端持有服务端的代理;如果是本地调用,则直接转换为 BnHwServiceManager ,换句话说这里客户端就是服务端自己。
当是远程访问时,实际的 IServiceManager 是由 hwservicemanager 进程中 ServiceManager 实现的。

再看直通式源码:

1
2
3
4
5
6
7
8
9
// ServiceManagement.cpp
sp<IServiceManager1_0> getPassthroughServiceManager() {
    return getPassthroughServiceManager1_1();
}
sp<IServiceManager1_1> getPassthroughServiceManager1_1() {
    static sp<PassthroughServiceManager> manager(
        new PassthroughServiceManager());
    return manager;
}

直通模式获取服务端时,直接返回的 PassthroughServiceManager 对象,通过它获取服务端 get 方法源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
struct PassthroughServiceManager : IServiceManager1_1 {

    static void openLibs(
        const std::string& fqName,
        std::function<bool /* continue */
            (void* /* handle */, const std::string& /* lib */, 
                const std::string& /* sym */)> eachLib) {
        //fqName looks like [email protected]::IFoo
        size_t idx = fqName.find("::");
        ...
        std::string packageAndVersion = fqName.substr(0, idx);
        std::string ifaceName = fqName.substr(idx + strlen("::"));
        const std::string prefix = packageAndVersion + "-impl";
        // hardcode 服务端必须包含一个 HIDL_FETCH_ 开头的函数
        const std::string sym = "HIDL_FETCH_" + ifaceName;
        const int dlMode = RTLD_LAZY;
        void *handle = nullptr;
        ...
        std::vector<std::string> paths = 
            {HAL_LIBRARY_PATH_ODM, HAL_LIBRARY_PATH_VENDOR,
             HAL_LIBRARY_PATH_VNDK_SP, HAL_LIBRARY_PATH_SYSTEM};
        ...
        for (const std::string& path : paths) {
            std::vector<std::string> libs = search(path, prefix, ".so");

            for (const std::string &lib : libs) {
                const std::string fullPath = path + lib;

                // 找到库文件,打开后返回句柄
                if (path != HAL_LIBRARY_PATH_SYSTEM) {
                    handle = android_load_sphal_library(
                        fullPath.c_str(), dlMode);
                } else {
                    handle = dlopen(fullPath.c_str(), dlMode);
                }
                // 没有找到则继续循环查找
                if (handle == nullptr) {
                    const char* error = dlerror();
                    LOG(ERROR)...;
                    continue;
                }
                // 回调传入的函数
                if (!eachLib(handle, lib, sym)) {
                    return;
                }
            }
        }
    }

    Return<sp<IBase>> get(const hidl_string& fqName,
                          const hidl_string& name) override {
        sp<IBase> ret = nullptr;

        openLibs(fqName, [&](void* handle, 
            const std::string &lib, const std::string &sym) {
            IBase* (*generator)(const char* name);
            *(void **)(&generator) = dlsym(handle, sym.c_str());
            if(!generator) {
                const char* error = dlerror();
                LOG(ERROR)...;
                dlclose(handle);
                return true;
            }

            ret = (*generator)(name.c_str());

            if (ret == nullptr) {
                dlclose(handle);
                return true; 
            }

            registerReference(fqName, name);
            return false;
        });

        return ret;
    }
    ...
}

openLibs 首先根据全限定名解析出版本号,以及 .hal 接口名称,再查找其实现库即 IFoo-impl.so 库,查找路径为 HAL_LIBRARY_PATH_SYSTEM 等等,它们的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// HidlInternal.h
#define HAL_LIBRARY_PATH_SYSTEM_64BIT "/system/lib64/hw/"
#define HAL_LIBRARY_PATH_VNDK_SP_64BIT "/system/lib64/vndk-sp/hw/"
#define HAL_LIBRARY_PATH_VENDOR_64BIT "/vendor/lib64/hw/"
#define HAL_LIBRARY_PATH_ODM_64BIT    "/odm/lib64/hw/"
#define HAL_LIBRARY_PATH_SYSTEM_32BIT "/system/lib/hw/"
#define HAL_LIBRARY_PATH_VNDK_SP_32BIT "/system/lib/vndk-sp/hw/"
#define HAL_LIBRARY_PATH_VENDOR_32BIT "/vendor/lib/hw/"
#define HAL_LIBRARY_PATH_ODM_32BIT    "/odm/lib/hw/"

#if defined(__LP64__)
#define HAL_LIBRARY_PATH_SYSTEM HAL_LIBRARY_PATH_SYSTEM_64BIT
#define HAL_LIBRARY_PATH_VNDK_SP HAL_LIBRARY_PATH_VNDK_SP_64BIT
#define HAL_LIBRARY_PATH_VENDOR HAL_LIBRARY_PATH_VENDOR_64BIT
#define HAL_LIBRARY_PATH_ODM    HAL_LIBRARY_PATH_ODM_64BIT
#else
#define HAL_LIBRARY_PATH_SYSTEM HAL_LIBRARY_PATH_SYSTEM_32BIT
#define HAL_LIBRARY_PATH_VNDK_SP HAL_LIBRARY_PATH_VNDK_SP_32BIT
#define HAL_LIBRARY_PATH_VENDOR HAL_LIBRARY_PATH_VENDOR_32BIT
#define HAL_LIBRARY_PATH_ODM    HAL_LIBRARY_PATH_ODM_32BIT
#endif

也就是说在这些路径中搜索 IFoo-impl.so 库文件,直到找到为止。

IFoo.hal 的实现文件 Foo.cpp 中,必须包含 HIDL_FETCH_IFoo 的函数,这个是在 openLibs 中代码写死的。通常在 HIDL_FETCH_IFoo 中, new Foo() 来新建 Foo 对象。

直通式中获取服务端的流程:先通过 openLibs 加载实现库 IFoo-impl.so ,再调用 HIDL_FETCH_IFoo 方法,得到 Foo 对象(即服务端)。
也就是说,直通式 Passthrough HAL 中客户端直接将服务端的代码库加载到当前进程中,这也是 Treble 架构中对老版本 HAL 的兼容:在 HIDL 之前, HAL 都是通过 dlopen 来直接加载的。

libhwbinder 目录

libhwbinder 库目录,主要是实现了 hwbinder 通信,它的实现方式绝大部分都和 Framework Binder 中一致,参考Android Binder 机制 。
libhwbinder 目录的代码基本是从 Framework Binder 代码拷贝过来,修改了部分 Bp, Bn Binder 的名称,以及 /dev/hwbinder 驱动设备文件。

hwBinder 类图结构

Android HIDL源码分析

  • BpHwRefBase 中的 mRemote 指向了 BpHwBinder
  • BpHwBinder 中的 mHandle 是一个句柄,指向 BHwBinder ,它们两个之间通过 Binder Driver 来通信

IBase 类图结构

Android HIDL源码分析

IBase 是所有 HIDL 服务的基类, BnHwBase 中的 _hidl_mImpl 指向了 HIDL 服务的具体实现类。

ProcessState

这里主要介绍 ProcessState 中两个函数:构造函数和 getContextObject

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// ProcessState.cpp

// 大约 1 M
#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
#define DEFAULT_MAX_BINDER_THREADS 0        // 默认最大线程数为 0

static int open_driver()
{
    // 打开驱动文件
    int fd = open("/dev/hwbinder", O_RDWR | O_CLOEXEC);
    if (fd >= 0) {
        int vers = 0;
        status_t result = ioctl(fd, BINDER_VERSION, &vers);
        ...
        size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
        // 设置默认最大线程数
        result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
        if (result == -1) {
            ALOGE("...);
        }
    } else {
        ALOGW(...);
    }
    return fd;
}

ProcessState::ProcessState()
    : mDriverFD(open_driver())
    , mVMStart(MAP_FAILED)
    , ...
{
    if (mDriverFD >= 0) {
        // mmap the binder, providing a chunk of virtual 
        // address space to receive transactions.
        // 映射内存空间
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, 
            MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        if (mVMStart == MAP_FAILED) {
            // *sigh*
            ALOGE("Using /dev/hwbinder failed...\n");
            close(mDriverFD);
            mDriverFD = -1;
        }
    }
    else {
        ALOGE("...");
    }
}

ProcessState 构造函数中实现了如下功能:

  • 打开 /dev/hwbinder 设备,该文件节点和 Binder 通信
  • 初始配置驱动的最大线程数为 0 ,后续可以通过 setThreadPoolConfiguration 来修改
  • mmap 映射内存空间,大概 1MB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// ProcessState.cpp
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    return getStrongProxyForHandle(0);
}

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);

    handle_entry* e = lookupHandleLocked(handle);

    if (e != NULL) {
        // We need to create a new BpHwBinder if there isn't currently 
        // one, OR we are unable to acquire a weak reference on this 
        // current one.  See comment in getWeakProxyForHandle() 
        // for more info about this.
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            b = new BpHwBinder(handle);
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a 
            // primary reference to the remote proxy when this 
            // team doesn't have one but another team is 
            // sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }

    return result;
}

getContextObject 函数中调用 getStrongProxyForHandle(0) ,即返回句柄为 0 的代理,而句柄为 0 表示是服务大管家,后面 hwservicemanager 中会详细介绍。
当查到句柄存在时,新建 BpHwBinder ,它是所有 Bp***Binder 的父类。

hwservicemanager 进程

hwservicemanager 是 HIDL 服务大管家,负责管理系统中的所有 HIDL 注册的绑定式服务,由 init 进程启动。

Binder 通信基础知识

先复习下 Framework Binder 通信的基础知识:

  • IInterface 表示服务端能够提供的服务
  • IBinder 用来实现跨进程通信,分为 BnBinder, BpBinder

客户端和服务端通信过程:

  • 服务端通过 BnBinder 实现 IInterface 对应功能
  • 客户端通过 BpBinder 调用 IInterface 对应功能; BpBinder 是 BnBinder 的代理,代理的实现过程为通过 Binder Driver 转发

Framework Binder 中两个重要概念:

  • service_manager 进程:它是服务大管家,负责保存注册的服务
  • IServiceManager 供客户端和服务端查询和注册服务,它和 service_manager 是跨进程通信

IServiceManager.cpp 作为客户端和服务大管家 service_manager 进程通过 Binder 来通信;其他 aidl 服务进程作为客户端,通过 IServiceManager.cpp 注册服务;其他 app 进程作为客户端,通过 IServiceManager.cpp 查询服务。

IServiceManager 类图结构

IServiceManager 类图结构,查看大图

Android HIDL源码分析

这个类图体现了一般的 HIDL 服务的类结构:

  • IServiceManager 可以看做一个标准的 HIDL 服务,默认继承 IBase
  • IServiceManager 服务的实现类有两个: PassthroughServiceManager, ServiceManager
  • BnHwServiceManager 中 _hidl_mImpl 指向 IServiceManager 的具体实现类,这里具体是 ServiceManager
  • BpHwServiceManager 是代理类,父类 BpHwRefBase 中的 mRemote 指向了 BpHwBinder ,而 BpHwBinder 中的 mHandle 是指向 BHwBinder 的句柄,这里实际指向的是它的子类 BnHwServiceManager
  • Bp** 和 Bn** 是通过 Binder 驱动来通信的,设备名 /dev/hwbinder

hwBinder 简述

hwBinder 和 Binder 模型基本一样,而且是共用 Binder Driver ,仅仅是设备关键字不一样。 hwBinder 中,服务端持有 BnHw 并实现 IInterface 的具体功能;客户端持有 BpHw 调用 IInterface 对应功能, BpHw 是 BnHw 的代理,通过 Binder Driver 来通信。 BpHw, BnHw 的通信过程(读写 Parcel ),都是在自动生成的 IFooAll.cpp 中实现的。
hwservicemanager 进程功能和 Framework Binder 中的 service_manager 进程相同,它是 HIDL 的服务大管家;但具体由 ServiceManager.cpp 来保存注册的服务, ServiceManager 属于 hwservicemanager 进程,所以通信过程是函数直接调用。
客户端和服务端通过 ServiceManagement.cpp 来和 hwservicemanager 通信。 ServiceManagement 会根据绑定式或是直通式来返回 IServiceManager 的具体实现:如果是绑定式,则 ServiceManagement 持有 BpHwServiceManager (即 BnHwServiceManager 的代理),它会和 hwservicemanager 通过 Binder 通信,查询并返回服务接口。

rc 文件

hwservicemanager.rc 文件定义了 hwservicemanager 进程的启动方式:

1
2
3
4
5
6
7
8
9
10
11
service hwservicemanager /system/bin/hwservicemanager
    user system
    disabled
    group system readproc
    critical
    onrestart setprop hwservicemanager.ready false
    onrestart class_restart hal
    onrestart class_restart early_hal
    writepid /dev/cpuset/system-background/tasks
    class animation
    shutdown critical

main 方法

主进程文件为 service.cpp ,对应的 main 方法为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// service.cpp
int main() {
    configureRpcThreadpool(1, true /* callerWillJoin */);

    ServiceManager *manager = new ServiceManager();
    if (!manager->add(serviceName, manager)) {
        ALOGE("Failed to register hwservicemanager with itself.");
    }

    TokenManager *tokenManager = new TokenManager();
    if (!manager->add(serviceName, tokenManager)) {
        ALOGE("Failed to register ITokenManager with hwservicemanager.");
    }

    sp<Looper> looper(Looper::prepare(0 /* opts */));

    int binder_fd = -1;
    IPCThreadState::self()->setupPolling(&binder_fd);
    if (binder_fd < 0) {
        ALOGE("Failed to aquire binder FD. Aborting...");
        return -1;
    }
    // Flush after setupPolling(), to make sure the binder driver
    // knows about this thread handling commands.
    IPCThreadState::self()->flushCommands();

    sp<BinderCallback> cb(new BinderCallback);
    if (looper->addFd(binder_fd, Looper::POLL_CALLBACK, 
        Looper::EVENT_INPUT, cb, nullptr) != 1) {
        ALOGE("Failed to add hwbinder FD to Looper. Aborting...");
        return -1;
    }

    // Tell IPCThreadState we're the service manager
    sp<BnHwServiceManager> service = new BnHwServiceManager(manager);
    IPCThreadState::self()->setTheContextObject(service);
    // Then tell binder kernel
    ioctl(binder_fd, BINDER_SET_CONTEXT_MGR, 0);
    ...
    while (true) {
        looper->pollAll(-1 /* timeoutMillis */);
    }

    return 0;
}

main 中主要实现了这些功能:

  • 新建 ServiceManager 对象,并将它注册到 hwservicemanager 中(注册过程实际是存储到一个 map 中)
  • 新建 TokenManager 对象,并将它注册为服务
  • 轮询 /dev/hwbinder 设备文件,监听事件
  • 新建 BnHwServiceManager 对象,并将 ServiceManager 传入作为 IServiceManager 的实现
  • ioctl 向驱动发送消息,将 hwservicemanager 进程,以句柄 0 向驱动注册为服务大管家 BINDER_SET_CONTEXT_MGR (这就是为什么拿到句柄 0 ,即表示为 hwservicemanager )

ServiceManager

ServiceManager 用于管理服务的注册和查询, mServiceMap 中保存了服务端相关信息,以全限定名称作为 key 保存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
struct ServiceManager : public IServiceManager, hidl_death_recipient {
    //Methods from::android::hidl::manager::V1_0::IServiceManager follow
    Return<sp<IBase>> get(const hidl_string& fqName,
                          const hidl_string& name) override;
    Return<bool> add(const hidl_string& name,
                     const sp<IBase>& service) override;
    ...
    using InstanceMap = std::map<
            std::string, // instance name e.x. "manager"
            std::unique_ptr<HidlService>
        >;

    struct PackageInterfaceMap {
    ...
    private:
        InstanceMap mInstanceMap{};
        ...
    };
    /**
     * Access to this map doesn't need to be locked, since hwservicemanager
     * is single-threaded.
     *
     * e.x.
     * mServiceMap["[email protected]::IServiceManager"]["manager"]
     *     -> HidlService object
     */
    std::map<
        std::string, // package::interface 
                     // e.x. "[email protected]::IServiceManager"
        PackageInterfaceMap
    > mServiceMap;
};
  • mServiceMap
    关键字为全限定名: package::interface ,比如:
    [email protected]::IServiceManager 。
  • mInstanceMap
    关键字为服务名称,默认为 default ;服务端也可以在注册时,指定服务名称;比如 CameraProvider 中注册时,指定为 "legacy/0" 。

因为先通过全限定从 mServiceMap 中取 mInstanceMap ,而全限定名称包含软件包、主次版本号、接口名称,全限定名称一定不会重复,所以拿到的是唯一的 mInstanceMap ;此时再根据服务名称获取服务端接口时,服务名已经不是很重要,所以通常服务名称使用的默认的 default 。

服务注册

服务端通过 Binder 注册,最终是在 ServiceManager 中实现的,并保存在 mServiceMap 中。
注意: add 来注册服务,是 hwbinder 机制内部使用的;服务端应该使用 registerAsService 来注册,而自动生成代码 IFooAll.cpp 中在实现 registerAsService 时,会调用 add 来完成注册。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// ServiceManager.cpp
Return<bool> ServiceManager::add(const hidl_string& name,
    const sp<IBase>& service) {
    ...
    auto ret = service->interfaceChain(
        [&](const auto &interfaceChain) {
        ...

        for(size_t i = 0; i < interfaceChain.size(); i++) {
            // 拿到全限定名
            std::string fqName = interfaceChain[i];
            // 根据全限定名查找 mInstanceMap
            PackageInterfaceMap &ifaceMap = mServiceMap[fqName];
            // 根据服务名称查找对应服务 
            HidlService *hidlService = ifaceMap.lookup(name);

            // 没有找到或找到为空,则添加或更新
            if (hidlService == nullptr) {
                ifaceMap.insertService(std::make_unique<HidlService>(
                    fqName, name, service, pid));
            } else {
                ...
                hidlService->setService(service, pid);
            }

            ifaceMap.sendPackageRegistrationNotification(fqName, name);
        }
        ...
    });
    ...
}

注册的过程为:

  • 先根据全限定名和服务名,查找服务是否存在
  • 如果不存在,则添加并保存;如果存在则更新

服务查询

查询 Binder 服务,最终是在 ServiceManager 中实现的,也就是从 mServiceMap 中查找。
注意: get 来查询服务,是 hwbinder 机制内部使用的;客户端应该通过接口 Interface::getService 获取:它会先使用 defaultServiceManager 来获取 IServiceManager ,然后再调用 get 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ServiceManager.cpp
Return<sp<IBase>> ServiceManager::get(const hidl_string& fqName,
                                      const hidl_string& name) {
    pid_t pid = IPCThreadState::self()->getCallingPid();
    if (!mAcl.canGet(fqName, pid)) {
        return nullptr;
    }

    auto ifaceIt = mServiceMap.find(fqName);
    if (ifaceIt == mServiceMap.end()) {
        return nullptr;
    }

    const PackageInterfaceMap &ifaceMap = ifaceIt->second;
    const HidlService *hidlService = ifaceMap.lookup(name);

    if (hidlService == nullptr) {
        return nullptr;
    }

    return hidlService->getService();
}

查询过程很简单:就是从 mServiceMap 中根据全限定名和服务名查找。

示例

hardware/interfaces/tests 中有简单的 HIDL 服务示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
tests/
├── Android.bp
├── bar
├── baz
├── expression
├── extension
├── foo
├── hash
├── inheritance
├── libhwbinder
├── memory
├── msgq
├── multithread
├── myintere
└── pointer

小结

Binder 总结

Binder 域有三个,但它们都是共用了 Binder Driver ,只是设备文件名称不一样(在 kernel 编译配置中设定 CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder,vndbinder" ):

  • /dev/binder
    标准的 Framework Binder ,使用 AIDL 接口;服务大管家对应的是 servicemanager 进程。
  • /dev/hwbinder
    HIDL 服务相关,使用 HIDL 接口;服务大管家对应的是 hwservicemanager 进程。
  • /dev/vndbinder
    供应商之间的通信,使用 AIDL 接口;服务大管家对应的是 vndservicemanager 进程。

在 HIDL 服务中,除了使用 /dev/hwbinder 和 Framework 通信外;还可以同时使用 /dev/vndbinder 和 vendor 通信:

1
2
3
4
5
6
7
// hardware/interfaces
camera/provider/2.4/default/service.cpp:32:    android::ProcessState::initWithDriver("/dev/vndbinder");
cas/1.0/default/service.cpp:37:    android::ProcessState::initWithDriver("/dev/vndbinder");
drm/1.0/default/service.cpp:38:    android::ProcessState::initWithDriver("/dev/vndbinder");
gnss/1.0/default/service.cpp:15:    android::ProcessState::initWithDriver("/dev/vndbinder");
graphics/composer/2.1/default/service.cpp:31:    android::ProcessState::initWithDriver("/dev/vndbinder");
hal-server/hal-server.cpp:107:android::ProcessState::initWithDriver("/dev/vndbinder");

这里 vndservicemanager, servicemanager 进程对应的源码文件都是 service-manager.c 文件,只是在 Android.bp 中做了编译区分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// frameworks/native/cmds/servicemanager/Android.bp
cc_binary {
    name: "servicemanager",
    defaults: ["servicemanager_flags"],
    srcs: [
        "service_manager.c",
        "binder.c",
    ],
    shared_libs: ["libcutils", "libselinux"],
    init_rc: ["servicemanager.rc"],
}

cc_binary {
    name: "vndservicemanager",
    defaults: ["servicemanager_flags"],
    vendor: true,
    srcs: [
        "service_manager.c",
        "binder.c",
    ],
    cflags: [
        "-DVENDORSERVICEMANAGER=1",
    ],
    shared_libs: ["libcutils", "libselinux_vendor"],
    init_rc: ["vndservicemanager.rc"],
}

后续