Android HIDL概述(一)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android HIDL概述(一)相关的知识,希望对你有一定的参考价值。
参考技术AAndroid O(8.0) 版本之后,底层实现有了比较大的变化,最显著的一个方面就是 HIDL 机制的全面实施。本文及接下来的几篇博文将从 HIDL的基本概念 、 HIDL服务模拟 、 framework层aidl服务 、 应用层程序 这四个方面来全面的阐述 HIDL 工作全过程,这对于理解系统源码中 Gnss 、 Usb 、 Camera 等模块的工作原理有极大帮助。
在 Android O(8.0) 之前系统的升级牵扯多方协作,极为麻烦, HIDL 机制的推出就是将 framework 与 hal 层分开,使得框架部分可以直接被覆盖、更新,而不需要重新对 HAL 进行编译,这样在系统升级时, OEM 厂商 跳过 SoC 厂商,先对 framework 进行升级。
framework 与 hal 紧紧耦合存在于 system.img 中,因此在版本升级时需要: OEM 厂商适配 framework , SoC厂商 适配 hal , 之后将修改打包到 system.img ,生成 OTA 升级包,推送到手机进行 OTA 升级
framework 与 hal 进行了解耦, framework 存在于 system.img , hal 存在于 vendor.img ,进行版本升级时,分为两次升级:
正如上述所言,旧版的系统架构中, android Framework 层与 Hal 层是打包成一个 system.img 的,且 Framework 与 hal 层之间是紧密耦合的,通过链接的方式使用相应的硬件 so 库。它们之间的架构一般有如下两种方式:
为了解决两者之间这种紧耦合所带来的弊端,google 引入 HIDL 来定义 Framework 与 HAL 之间的接口,可以用下图来描述:
事实上虽然 google 推出了这种机制,但是很多厂商没有很快的跟上节奏,因此为了向前兼容, google 定义了三种类型:
上述可总结为 :
[ 1 ] hidl
[ 2 ] hidl trebl 演进
android treble项目&&HIDL学习总结
概念介绍
Treble项目—HIDL产生背景,独立升级framework需求
Android O(8.0) 重新设计了 Android 操作系统框架,在一个名为Treble的项目中,以便让制造商能够以更低的成本更轻松、更快速地将设备更新到新版 Android 系统。在这种新架构中,HAL 接口定义语言HIDL指定了 HAL 和其用户之间的接口,让用户能够替换 android framework,而无需重新编译 HAL。
HIDL概念—HAL 接口定义语言,目标framework和HAL解耦
HIDL: HAL 接口定义语言HAL interface definition language。在 Android 8.0 中被全面使用,用于在可以独立编译的代码库之间进行通信的系统,定义Android Framework与Android HAL实现之间的接口。把framework和hal隔离,目的是使 Android 可以在不重新编译 HAL 的情况下对 Framework 进行 OTA 升级(OTA:Over The Air,一种远程无线升级技术,在线升级系统)。
HIDL与AIDL比较类似,底层也是基于binder机制。但是也有稍微不一样的地方。为了支持HIDL,Android 对BInder做了一定程度的修改,/dev/hwbinder。
HIDL的两种模式—Passthrough直通 / Binderized绑定
为了将以往设备的 Android 版本更新到 Android O,开发者需要将传统的 HAL 封装到新的 HIDL 接口中,这个接口为 HAL 提供了 Binder 模式以及 Passthrough 模式。
(1)Passthrough直通模式:对原先HAL的包装,老版本HAL的兼容,最终的binder service 跟binder client都是活在同一个进程当中,仅对 C++ 客户端与实现适用。
(2)Binderized绑定模式:Binder化的HAL,HAL都被写成了binder service,Android framework是binder client,两者再不同的上下文。
推出时即搭载了 Android 8.0 或后续版本的设备都必须只支持绑定式 HAL。如下图:
HIDL设计分析
基于HIDL接口的HAL模块整体流程如下:调用者需要包含Client端对应so,通过getService获取服务端实例,通过对client的操作对服务端Service进程发出申请。而Serive端是开机时init进程 通过rc启动脚本执行service对应可执行文件 创建进程,并在启动过程中向HwServiceManager注册服务,来响应client的访问。而实际项目过程,往往service可执行文件并不会包含过多的业务实现,而是通过链接impl.so共享库来实现,impl.so中为真正的业务实现代码,并调用Vender底层SDK,涉及硬件驱动就通过kernel中内核驱动模块提供支持。
整个设计android的HIDL机制主要就是client与service之间的访问接口设计。 HIDL的原理大致如下:android提供了一种特定编写规则的.hal文件(类似c++和java),然后通过特定的hidl-gen工具和一些构建脚本 把.hal转换为可以编译的.cpp/.h文件、android.bp构建文件,这些生成的文件已经实现了client对应so,service可执行文件(包含了注册service过程)。而接下来只需要在对应.cpp实现内部细节,然后添加对应权限配置,加入编译过程等就完成了hal模块新增。
HAL模块与package软件包(包含.hal文件)
每个HAL模块设计一个接口软件包package,可以具有子级,如 package.subpackage。已发布的 HIDL 软件包的根目录为 hardware/interfaces 或 vendor/vendorName。软件包名称在根目录下形成一个或多个子目录;定义同一个软件包的所有文件都位于同一目录下。
软件包目扩展名为 .hal 的文件。每个文件均必须包含一个指定文件所属的软件包和版本的 package 语句。types.hal 文件(如果存在)并不定义接口,而是定义软件包中每个接口可以访问的数据类型。
.hal文件 语法简介
https://source.android.google.cn/devices/architecture/hidl/code-style
HIDL 语言与 C 语言类似。
注释:/** */ 表示文档注释(只用于类型、方法、字段和枚举值声明);/* */ 表示多行注释;// 表示注释一直持续到行尾;
[empty] 表示该字词可能为空。
? 跟在文本或字词后,表示它是可选的。
... 表示包含零个或多个项、使用指定的分隔符号分隔的序列。HIDL 中不含可变参数。
逗号用于分隔序列元素。
分号用于终止各个元素,包括最后的元素。
大写字母是非终止符。
italics 是一个令牌系列,如 integer 或 identifier(标准 C 解析规则)。
constexpr 是 C 样式的常量表达式(例如 1 + 1 和 1L << 3)。
import_name 是软件包或接口名称,按 HIDL 版本控制中所述的方式加以限定。
小写 words 是文本令牌。
.hal文件 (hidl-gen工具)编译转换.h/.cpp文件
hidl-gen 编译器会将 .hal 文件编译成一组 .h 和 .cpp 文件。 这些自动生成的文件可用来构建客户端/服务器实现链接到的共享库。用于构建此共享库的 Android.bp 文件由 hardware/interfaces/update-makefiles.sh 脚本自动生成。
IFoo.h :描述 C++ 类中的纯 IFoo 接口;它包含 IFoo.hal 文件中的 IFoo 接口中所定义的方法和类型,类的命名空间包含软件包名称和版本号,客户端和服务器都包含此标头:客户端用它来调用方法,服务器用它来实现这些方法。
IHwFoo.h :其中包含用于对接口中使用的数据类型进行序列化的函数的声明。开发者不得直接包含其标头(它不包含任何类)。
BpFoo.h : 从 IFoo 继承的类,可描述接口的 HwBinder 代理(客户端)实现。开发者不得直接引用此类。
BnFoo.h: 保存对 IFoo 实现的引用的类,可描述接口的 HwBinder 服务器端实现。开发者不得直接引用此类。
FooAll.cpp :包含 HwBinder 客户端和 HwBinder 服务器端的实现的类。当客户端调用接口方法时,代理会自动从客户端封送参数,并将事务发送到绑定内核驱动程序,该内核驱动程序会将事务传送到另一端的服务器端实现。
.hal文件hash值与多版本匹配
HIDL的设计目标就是 把 framework 与 vender之间 的接口固定下来,HIDL用的方法就是给每个hal文件生成一个hash值,记录到current.txt文件中,类似如下composer模块的hash值,在编译的时候就可以校验。当需要新增接口的时候,就新增一个版本,继承上一个版本的hal接口进行扩展,同时生成新版本对应的hash值。
HIDL设计举例—hwc composer的分析
hwc composer是android里面对叠加器的加速,相关概念参考:https://blog.csdn.net/runafterhit/article/details/118884165
代码:http://androidxref.com/9.0.0_r3/xref/hardware/interfaces/graphics/composer/2.2/
composer相关源码分析
/hardware/interfaces/graphics/composer/2.2/
IComposer.hal:定义了composer模块的hal接口。
package android.hardware.graphics.composer@2.2;; // 指定文件所属的软件包和版本
import @2.1::IComposer; // 导入2.1的类
interface IComposer extends @2.1::IComposer {
// 完全继承2.1包定义的IComposer
}
// 2.1包定义的IComposer 如下:
interface IComposer {
getCapabilities() generates (vec<Capability> capabilities); // 获取能力级别
dumpDebugInfo() generates (string debugInfo); // 打印debug信息
createClient() generates (Error error, IComposerClient client); // 创建clenit实例
}
IComposerClient.hal:定义了composer的client的hal接口:
package android.hardware.graphics.composer@2.1; // 指定文件所属的软件包和版本
interface IComposerClient extends @2.2::IComposerClient {
// 继承2.1包定义的IComposerClient ,同时专门新增了2.2相关功能接口,部分如下:
getReadbackBufferFence(Display display) generates (Error error,handle acquireFence); // 获取buf的fence
}
// 2.1包定义的IComposerClient 如下:
interface IComposerClient {
// 重点相关函数如下:
registerCallback(IComposerCallback callback); // 回调注册接口
createVirtualDisplay(/* 省略*/); // 创建虚拟display通道
createLayer(Display display, uint32_t bufferSlotCount) generates (Error error,Layer layer); // 创建layer
destroyLayer(Display display, Layer layer) generates (Error error); // 销毁layer
setVsyncEnabled(Display display, Vsync enabled) generates (Error error); // 使能通道vsync
setInputCommandQueue(fmq_sync<uint32_t> descriptor) generates (Error error);// 设置输入命令到queue
executeCommands(/* 省略*/); // 直行命令queue中命令
// 还有各种设置属性接口
}
IComposerCallback.hal:定义client注册的回调类型,定义在2.1包中
package android.hardware.graphics.composer@2.1;
interface IComposerCallback {
onHotplug(Display display, Connection connected); // display通路的拔插 回调
oneway onRefresh(Display display); // display的刷新回调,强制刷新
oneway onVsync(Display display, int64_t timestamp); // display的vsync信号回调
}
composer相关权限配置分析
Selinux相关概念可以参考: https://blog.csdn.net/runafterhit/article/details/119920733
composer要能被访问,或者要访问其他资源,需要进行selinux相关权限配置。这里完全是更具具体产品项目的权限设置,往往需要设置:1、对应服务service进程的权限管理(可以访问那些资源,包括binder);2、service可执行文件的安全上下文,谁能访问它也需要添加allow权限;3、涉及硬件设备节点访问,需要在service追加相关权限。
举例如下:
/device/google/marlin/sepolicy/hal_graphics_composer_default.te :主要设置service的文件访问、binder访问等
userdebug_or_eng(`
allow hal_graphics_composer_default diag_device:chr_file rw_file_perms;
')
dontaudit hal_graphics_composer_default diag_device:chr_file rw_file_perms;
# misc
typeattribute hal_graphics_composer_default data_between_core_and_vendor_violators;
allow hal_graphics_composer_default display_data_file:dir create_dir_perms;
allow hal_graphics_composer_default display_data_file:file create_file_perms;
# persist
allow hal_graphics_composer_default persist_file:dir search;
# persist/display
allow hal_graphics_composer_default persist_display_file:dir r_dir_perms;
allow hal_graphics_composer_default persist_display_file:file create_file_perms;
vndbinder_use(hal_graphics_composer_default);
add_service(hal_graphics_composer_default, qdisplay_service)
# HWC_UeventThread
allow hal_graphics_composer_default self:netlink_kobject_uevent_socket create_socket_perms_no_ioctl;
# Access /sys/devices/virtual/graphics/fb0/mdp/caps and maybe others
r_dir_file(hal_graphics_composer_default, sysfs_type)
composer相关编译文件分析
在/hardware/interfaces/graphics/composer/目录下mm编译,会生成:
1、android.hardware.graphics.composer@2.2-service. rc : 启动配置脚本,直接从copy到vendor/etc/init。
2、android.hardware.graphics.composer@2.*. so:客户端client使用时需要链接的客户端so。
3、android.hardware.graphics.composer@2.2-service :service的可执行文件,生成在vendor/bin/hw/,见2.2/default/Android.mk中编译规则,service 服务的作用就是向 hwservicemanager 注册 HAL,并链接impl .so调用实现。
4、 android.hardware.graphics.composer@2.1-impl .so :service编译时链接的共享库,在service进程加载时会链接进去。
composer启动流程分析
1、开机init进程会读取vendor/etc/init下的xxx_service. rc初始脚本文件,执行vendor/bin/hw/xx_service可执行文件,从而启动service进程:
// 脚本内容:
service vendor.hwcomposer-2-2 /vendor/bin/hw/android.hardware.graphics.composer@2.2-service
class hal animation
user system
group graphics drmrpc
capabilities SYS_NICE
onrestart restart surfaceflinger
进程状态:
1|HWRVL:/vendor/etc/init $ ps -A | grep composer
system 610 1 2248748 2824 0 0 S android.hardware.graphics.composer@2.2-service
2、进程启动后,servie执行main函数,建立Binder IPC 通信。
/hardware/interfaces/graphics/composer/2.2/default/service.cpp,HIDL 框架为了支持供应商 HAL 进程,提供了 libbinder 用户空间库用于操作 Binder 设备节点(hwbinder & vndbinder)。然后调用HwcLoader::load()进行hwc模块加载。
int main() {
// the conventional HAL might start binder services
android::ProcessState::initWithDriver("/dev/vndbinder");
android::ProcessState::self()->setThreadPoolMaxThreadCount(4);
android::ProcessState::self()->startThreadPool();
// same as SF main thread
struct sched_param param = {0};
param.sched_priority = 2;
if (sched_setscheduler(0, SCHED_FIFO | SCHED_RESET_ON_FORK, ¶m) != 0) {
ALOGE("Couldn't set SCHED_FIFO: %d", errno);
}
android::hardware::configureRpcThreadpool(4, true /* will join */);
android::sp<IComposer> composer = HwcLoader::load();
if (composer == nullptr) {
return 1;
}
if (composer->registerAsService() != android::NO_ERROR) {
ALOGE("failed to register service");
return 1;
}
return 1;
}
3、HWC模块load,这里最终调用到createComposer,其中主要实现调用hal::Composer::create(std::move(hal)) 来调用到ComposerHal 类中的
composer/2.2/utils/passthrough/include/composer-passthrough/2.2/HwcLoader.h
class HwcLoader : public V2_1::passthrough::HwcLoader {
public:
static IComposer* load() {
const hw_module_t* module = loadModule();
if (!module) {
return nullptr;
}
auto hal = createHalWithAdapter(module);
if (!hal) {
return nullptr;
}
return createComposer(std::move(hal)).release();
}
/* 省略 */
static std::unique_ptr<IComposer> createComposer(std::unique_ptr<hal::ComposerHal> hal) {
return hal::Composer::create(std::move(hal));
}
}
新增基于HIDL的HAL模块流程
以下梳理添加一个基于hidl的hal模块test的大致流程,作为开发参考。具体例子可以看参考中的实例。
整个流程大致为:写.hal文件通过工具和配置文件生成hidl相关编译文件,实现内部细节,添加xml的接口配置和current接口hash版本,按使用需求编写client访问,设置selinux配置权限,添加包编译构建关系。
1. 建立模块包目录、编写.hal文件创建HIDL接口
在接口目录添加对应模块目录,如hardware/interfaces/test/1.0/,表示新增test模块,默认版本号1.0。创建文件ITest.hal,参考内容如下(如果有必要,还可以types.hal定义复杂结构体,或者拆解为多个.hal文件):
package android.hardware.test@1.0; // 声明包的名称test,版本1.0
interface ITest {
init(TestID id);
//无返回值
helloWorld(string name) generates (string result);
};
2. 执行构建脚本,生成对应makefile和default目录与文件
使用hidl-gen生成default目录 里的C++文件
PACKAGE=android.hardware.test@1.0
LOC=hardware/interfaces/test/1.0/default
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
根目录使用update-makefiles.sh生成1.0目录下的Android.bp
./hardware/interfaces/update-makefiles.sh
生成文件如下:
├── 1.0
│ ├── default
│ │ ├── Android.bp
│ │ ├── Test.cpp // 需要实现service的实现代码
│ │ ├── Test.h
│ ├── Android.bp
│ ├── ITest.hal
再新增android.hardware.test@1.0-service. rc启动脚本文件
service test_service /vendor/bin/hw/android.hardware.test@1.0-service
class hal
user system
group system
3. 在xml文件添加接口声明、在current.txt添加版本hash值
在manifest.xml文件中添加新增hal模块的接口ITest声明。
<hal format="hidl">
<name>android.hardware.test</name>
<transport>hwbinder</transport>
<version>1.0</version>
<interface>
<name>ITest</name>
<instance>default</instance>
</interface>
</hal>
在current.txt中添加对应软件版本的hash值。用hidl-gen -L hash命令生成。
// 参考命令,xxx为供应商vender名称,和产品使用具体版本相关:
hidl-gen -L hash -r vendor.xxx.harware:vendor/xxx/hardware -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport vendor.xxx.harware.test@1.0
生成后写入current.txt,类似如下composer模块的hash值:
4. 实现service服务端hal注册与功能实现代码
在工具生成的Test.cpp中实现service的内部实现。实际项目中,经常再单独实现一个impl .so,service本身是一个轻量级的可执行文件,service编译时链接的共享库impl .so包含真正的实现,在service进程加载时会链接进去。
// 部分代码如下
Return<void> Test::init(const ::android::hardware::test::V1_0::TestID& id) {
mExit = false;
mName = id.name;
mID = id.id;
ALOGD("init:");
return Void();
}
Return<void> Test::helloWorld(const hidl_string& name, helloWorld_cb _hidl_cb) {
ALOGD("helloWorld:");
char buf[100];
::memset(buf,0x00,100);
::snprintf(buf,100,"Hello World,%s",name.c_str());
hidl_string result(buf);
_hidl_cb(result);
return Void();
}
5. 实现client客户端调用hal模块代码
从framework层 当需要调用的对应hal接口,Android.bp添加test编译时链接对应.so的模块。
然后通过getService获取client实例。参考类似如下:
private ITest halService ;
halService = ITest.getService();//获取service对应实例
halService.helloWorld(str); // 调用对应client方法,向service发出申请
6. 配置新增hal模块Selinux配置权限
Selinux相关概念可以参考: https://blog.csdn.net/runafterhit/article/details/119920733
一般新增一个模块,往往需要添加已下几处典型selinux配置:
1)新增一个hal_xxx安全策略文件,定义服务进行,追加服务对binder使用等权限等(若涉及property写需要自己添加);
2)在file_contexts中,新增service可执行文件的安全上下文;
3)在hwservice_context中,新增服务相关安全上下文,及其关联type类型;
4)如果涉及设备节点的访问,还需要对应hal_xxx安全策略文件添加响应访问权限;
7. 在mk文件添加编译关系
在对应的包(项目不同配置文件有差异)编译文件添加新增hal模块相关包,举例:/build/target/product/emulator.mk
PRODUCT_PACKAGES += \\
/* 省略 */
android.hardware.test@1.0-impl \\
android.hardware.test@1.0-service \\
参考
官方文档:
https://source.android.google.cn/devices/architecture/hidl
https://source.android.google.cn/devices/architecture/hidl/code-style
HIDL概述:https://blog.csdn.net/u013357557/article/details/84561457
AndroidO Treble架构下Hal进程启动及HIDL服务注册过程:https://blog.csdn.net/yangwen123/article/details/79854267
Android P HAL层添加HIDL实例: https://blog.csdn.net/sinat_18179367/article/details/95940030
https://blog.csdn.net/qq_19923217/article/details/88398660
https://blog.csdn.net/qq_19923217/article/details/89173162
https://blog.csdn.net/kuang_tian_you/article/details/86599869
以上是关于Android HIDL概述(一)的主要内容,如果未能解决你的问题,请参考以下文章