1.1、Treble 计划概览
Android O 引入” Treble” 计划,目标是通过重构 Android OS 的 framework,使 Android 设备制造商能更快、更易、更低成本地升级 Android 版本。新的架构主要有两个特点:
1)切分出 system 分区和 vendor 分区;
2)使用 HIDL 勾通 system 分区和 vendor 分区。
(Android O 与之前的版本相比,多出了一个 vendor.img 分区)
1.2、Android 7.x vs Android O
Android 7.x 及更早版本中没有正式的供应商接口,Android Framework 和 Android HAL 是打包成一个 system.img,每次升级 Android Framework,都需升级对应的 Android HAL,且二者是紧耦合的,通过链接的方式使用相应的硬件so库。Framework 和 HAL 之间一般架构如下:
Android O 及以后版本更新了框架,引入HIDL(硬件抽象层接口定义语言)来定义 Framework 和 Vendor HAL 之间的接口,以此解耦 Android Framework 和 Vendor HAL Implementation。它的出现可以让 HAL 层运行在独立的进程中,不再以链接库的形式被调用。新的架构如下:
其中,HAL 分为 绑定式 HAL 和 直通式HAL :
1)绑定式 HAL:以 HAL 接口定义语言 (HIDL) 表示的 HAL。在绑定式 HAL 中,Android 框架和 HAL 之间通过 Binder 进程间通信 (IPC) 调用进行通信。所有在推出时就搭载 Android 8.0 或后续版本的设备都必须只支持绑定式 HAL(少数 HAL 除外)。
2)直通式 HAL: 以 HIDL封装的传统 HAL 或旧版 HAL。这些 HAL 封装了现有的 HAL,可在绑定模式和 Same-Process(直通)模式下使用。升级到 Android 8.0 的设备可以使用直通式 HAL。
所谓绑定式,即通过 Binder 化进行传输,这个在 Android AIDL中已应用了很久,提供了代码库独立的基本条件。既然是进程间通信,难免会有耗时过程的问题,这对于速度和性能要求特别高地的进程来说是不利的,因此可以通过直通式 HAL来弥补绑定式 HAL 的缺点。
2、HIDL 基本概念
HAL 接口定义语言(简称 HIDL,发音为 hide-I)是用于指定 HAL 和其用户之间接口的一种接口描述语言。它的目标是框架可以在无需重新构建 HAL的情况下进行替换。HAL 将由供应商或 SOC 制造商构建,放在设备的 /vendor 分区中,这样框架就可以在其分区中通过 OTA 进行替换,而无需重新编译 HAL。
HIDL 用于进程间通信,进程之间的通信经过 Binder 化。在此之前 Android 有 AIDL,架构在 Android binder 之上,用来定义Android 基于 Binder 通信的 Client 与 Service 之间的接口。HIDL 的作用类似,不过 HIDL 定义的是 Android Framework 与Android HAL 实现之间的接口。
在 AIDL 机制中 Android 会提供一系列工具将用户定义的 *.aidl 文件编译生成 Client 端代码与 Service 端代码,用户仅仅需要:
1)在 Service 端实现所需实现的接口。
2)在 Client 端调用相关接口。
基于 Binder 机制,在 Clinet 端的调用会自动通过 Binder 驱动跨进程到 service 进程当中。
而 HIDL与 AIDL 类似,底层也是基于 Binder 机制,但也有稍微不一样的地方。为了支持 HIDL,Android 对 Binder 做了一定程度的修改:
软件包:
HIDL 接口软件包位于 hardware/interfaces 或 vendor/ 目录下(少数例外情况除外)。hardware/interfaces 顶层会直接映射到 android.hardware 软件包命名空间;版本是软件包(而不是接口)命名空间下的子目录。
3、HIDL 的使用例子-- 蓝牙 HCI 服务进程
下面介绍 Passthrough 模式的 HIDL 实现机制。以 hardware/interfaces/bluetooth/1.0 为例。
3.1、定义 & 实现接口
该路径下的目录结构如下图所示:
其中:
A)Ixxx.hal 和 types.hal 文件定义 HIDL 接口和数据类型;
B)default 文件夹中实现 HIDL 接口。 包括 bluetooth_hci.h、bluetooth_hci.cc,vendor_interface.h、vendor_interface.cc 等文件
3.2、编译生成的文件
当编译 hardware/interfaces/bluetooth/1.0 时,会进行以下操作:
1)生成 .cpp 和 .h文件,生成 HIDL库:[email protected]
2)生成 /vendor/bin/hw/[email protected] 的可执行文件(服务),通过该服务调用 HIDL 库的接口。Android.mk 中声明了该 service 运行时依赖于HIDL共享库。
3)生成 /vendor/lib/hw/[email protected] 的库文件(实现了 HIDL 库的接口)。Android.bp 中声明了该库运行时依赖于 HIDL 共享库。
4)初始化配置脚本 [email protected] 会被拷贝到 vendor.img 里的 vendor/etc/init 目录。rc文件的内容如下:
service bluetooth-1-0 /vendor/bin/hw/[email protected]
class hal
user bluetooth
group bluetooth
writepid /dev/stune/foreground/tasks
on property:vts.native_server.on=1 && property:ro.build.type=userdebug
stop bluetooth-1-0
on property:vts.native_server.on=1 && property:ro.build.type=eng
stop bluetooth-1-0
on property:vts.native_server.on=0 && property:ro.build.type=userdebug
start bluetooth-1-0
on property:vts.native_server.on=0 && property:ro.build.type=eng
start bluetooth-1-0
3.3、启动 HIDL service
在开机过程的某一个阶段,系统会启动 class 是 hal 的服务,进而会执行 /vendor/bin/hw/[email protected],从而调用 hardware/interfaces/bluetooth/1.0/default/service.cpp 的 main 方法:
#define LOG_TAG "[email protected]"
#include
#include
// Extra threads make priority inheritance faster.
static const size_t kMaxThreads = 5;
// Generated HIDL files
using android::hardware::bluetooth::V1_0::IBluetoothHci;
using android::hardware::defaultPassthroughServiceImplementation;
int main() {
return defaultPassthroughServiceImplementation
}
defaultPassthroughServiceImplementation 通过 dlopen 载入 /vendor/lib/hw/[email protected],把当前的服务(HidlService)注册进 HwServiceManage,然后等待客户端的调用。
3.4、Client 端获取并使用 Service
客户端通过 getService() 来获取 HidService,进而调用 service 的相应方法。
路径:system/bt/hci/src/Hci_layer_android.cc
#include
#include
#include
#include
using android::hardware::bluetooth::V1_0::IBluetoothHci;
android::sp
void hci_initialize() {
LOG_INFO(LOG_TAG, "%s", __func__);
btHci = IBluetoothHci::getService();
CHECK(btHci != nullptr);
LOG_INFO(LOG_TAG, "%s: IBluetoothHci::getService() returned %p (%s)",
__func__, btHci.get(), (btHci->isRemote() ? "remote" : "local"));
{
android::sp
btHci->initialize(callbacks);
}
}