Android HIDL概述与绑定模式的实现

2024-01-25 16:20

本文主要是介绍Android HIDL概述与绑定模式的实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 一、前言

Android O(8.0) 版本之后,底层实现有了比较大的变化,最显著的一个方面就是 HIDL 机制的全面实施。本文对于理解系统源码中 GnssUsbCamera 等模块的工作原理有极大帮助。

二、HIDL 设计目的

Android O(8.0) 之前系统的升级牵扯多方协作,极为麻烦,HIDL机制的推出就是将 frameworkhal 层分开,使得框架部分可以直接被覆盖、更新,而不需要重新对 HAL 进行编译,这样在系统升级时,OEM 厂商 跳过 SoC 厂商,先对 framework 进行升级。

2.1、8.0 之前

frameworkhal 紧紧耦合存在于 system.img 中,因此在版本升级时需要: OEM 厂商适配 frameworkSoC厂商 适配 hal, 之后将修改打包到 system.img,生成 OTA 升级包,推送到手机进行 OTA 升级

2.2、8.0 之后

frameworkhal 进行了解耦, framework 存在于 system.imghal 存在于vendor.img,进行版本升级时,分为两次升级:

  • framework升级 : OEM 厂商适配 framework,将修改打包到 system.img, 生成OTA 升级包,推送到手机进行 OTA 升级(framework 发生改变,hal 层未变)。
  • hal升级 :SoC 厂商适配 hal, 将修改打包到 vendor.img, 生成OTA 升级包,推送到手机进行OTA升级(framework发生改变,hal 层发生改变)。

三、HIDL机制演进

3.1 老版本 Framework 与 HAL 的通信框架

正如上述所言,旧版的系统架构中, Android Framework 层与 Hal 层是打包成一个 system.img 的,且 Framework 与 hal 层之间是紧密耦合的,通过链接的方式使用相应的硬件 so 库。它们之间的架构一般有如下两种方式:

3.2 HIDL 类型介绍

为了解决两者之间这种紧耦合所带来的弊端,google 引入 HIDL 来定义 Framework 与 HAL 之间的接口,可以用下图来描述:

事实上虽然 google 推出了这种机制,但是很多厂商没有很快的跟上节奏,因此为了向前兼容, google 定义了三种类型:

  • ① 是 Treble Project 之前使用的实现架构,使用的是传统 HAL 和旧版 HAL
  • ② 直通模式,passthrough mode。如图所示,Framework 和 HAL 层工作在同一个进程当中,下面的 HAL 是使用 HIDL 封装后的库,是直通式 HAL。这些库文件也可用于 ③ 绑定模式
  • ③ 绑定模式,binderized mode。是直通式 HAL binder 化,变为绑定式 HAL。Framework 和 HAL 层工作在不同的进程,之间通过 Binder 进行 IPC
  • ④ 纯绑定式。相对于 ③ 来说,绑定式 HAL 中并不包含直通式 HAL,因此称为纯绑定式

上述可总结为

上述介绍参考此处

四、HIDL实现

绑定模式 是 google 为了向前兼容而定义的一种类型,且 Android 8.0 及后续版本的设备都必须只支持这种模式。这种模式下 Framework 与 Hal 分别位于不同的进程中,其实从具体实现来讲这种模式也更应该被称为 Binder 化的直通式。下面将通过这种方式实现一个具有加减乘除功能的 HIDL 服务,该服务的名称为 MyTest

4.1 创建IMyTest.hal文件

在系统源码中的 hardware/interfaces 目录下有很多的 HIDL,我们仿照其他 HIDL 来创建自己的目录,在源码根目录执行以下命令:

mkdir -p hardware/interfaces/my_test/1.0/default

之后创建 IMyTest.hal 文件:

touch hardware/interfaces/my_test/1.0/IMyTest.hal

这里定义了四种基本的运算:加、减、乘、除,这是上层调用 HAL 的入口,该文件内容如下:

package android.hardware.my_test@1.0;interface IMyTest{//加法add(uint32_t a,uint32_t b) generates (uint32_t result);//减法sub(uint32_t a,uint32_t b) generates (uint32_t result);//乘法mul(uint32_t a,uint32_t b) generates (uint32_t result);//除法div(uint32_t a,uint32_t b) generates (uint32_t result);};

4.2 使用hidl-gen生成HAL相关文件

Google帮我们提供了 hidl-gen 工具来生成 HAL 层相关的代码框架和代码实例,这样子我们只需要关心实现部分。在源码根目录执行以下命令:

source build/envsetup.sh
lunch xxx
PACKAGE=android.hardware.my_test@1.0
LOC=hardware/interfaces/my_test/1.0/default/
make hidl-gen
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

之后执行 update-makefiles.sh 脚本来为 HIDL 生成对应的 Android.bp 文件,源码根目录执行:

./hardware/interfaces/update-makefiles.sh

现在我们的工程目录结构如下:

接下来我们需要再创建两个文件:

touch hardware/interfaces/my_test/1.0/default/android.hardware.my_test@1.0-service.rc
touch hardware/interfaces/my_test/1.0/default/service.cpp

最终我们的工程目录结构如下:

4.3 调用流程分析

上述过程已经将 HIDL 服务所需要的文件创建完成,虽然其中很多文件还没有具体实现,我们先放在一边,先来对整体的调用流程及各个文件的作用略作说明:

Application上层应用
JNI指 framework 层,getService 获取 hal 层 service
android.hardware.my_test@1.0.so接口库,由hardware/interfaces/my_test/1.0/Android.bp 通过 IMyTest.hal 生成,这样只要这个接口库不变,那么 framework 的更新和 hal 层就隔绝开了
android.hardware.my_test@1.0-impl.so实现库,上层应用的最终调用
mytest-hal-serviceservice的名称
android.hardware.my_test@1.0-service.rc设备开机时通过解析 rc 文件启动此服务

 4.4 接口库生成

android.hardware.my_test@1.0.so 由hardware/interfaces/my_test/1.0/Android.bp 通过 IMyTest.hal 生成,该 Android.bp 文件是在上面一系列命令执行之后生成,而接口库是当我们最终执行编译模块时生成,可以说这个过程不需要我们手动参与。

4.5 实现库生成

由 hardware/interfaces/galaxy_one/1.0/default/Android.bp 在最后模块编译时通过 GalaxyOne.cpp 生成,

Android.bp 内容如下:

// FIXME: your file license if you have onecc_library_shared {// FIXME: this should only be -impl for a passthrough hal.// In most cases, to convert this to a binderized implementation, you should:// - change '-impl' to '-service' here and make it a cc_binary instead of a//   cc_library_shared.// - add a *.rc file for this module.// - delete HIDL_FETCH_I* functions.// - call configureRpcThreadpool and registerAsService on the instance.// You may also want to append '-impl/-service' with a specific identifier like// '-vendor' or '-<hardware identifier>' etc to distinguish it.name: "android.hardware.my_test@1.0-impl",relative_install_path: "hw",// FIXME: this should be 'vendor: true' for modules that will eventually be// on AOSP.proprietary: true,srcs: ["MyTest.cpp",],shared_libs: [    //可以添加需要的库"liblog","libhidlbase","libhidltransport","libhwbinder","libutils","android.hardware.my_test@1.0",],
}

4.6 MyTest.cpp的实现

实现库是通过 MyTest.cpp 编译生成的,现在完善 MyTest.h 和 MyTest.cpp

MyTest.h

Binder化直通式,同样需要将 HIDL_FETCH_XXX 打开

// FIXME: your file license if you have one#pragma once#include <android/hardware/my_test/1.0/IMyTest.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <log/log.h>namespace android::hardware::my_test::V1_0::implementation {using ::android::hardware::hidl_array;
using ::android::hardware::hidl_memory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;struct MyTest : public V1_0::IMyTest {// Methods from ::android::hardware::my_test::V1_0::IMyTest follow.Return<uint32_t> add(uint32_t a, uint32_t b) override;Return<uint32_t> sub(uint32_t a, uint32_t b) override;Return<uint32_t> mul(uint32_t a, uint32_t b) override;Return<uint32_t> div(uint32_t a, uint32_t b) override;// Methods from ::android::hidl::base::V1_0::IBase follow.};// FIXME: most likely delete, this is only for passthrough implementations
extern "C" IMyTest* HIDL_FETCH_IMyTest(const char* name);}  // namespace android::hardware::my_test::V1_0::implementation

注意,hidl-gen生成的 MyTest.h 的代码中,命名空间不对;将android::hardware::my_test::implementation 改成 android::hardware::my_test::V1_0::implementation

MyTest.cpp

// FIXME: your file license if you have one#include "MyTest.h"namespace android::hardware::my_test::V1_0::implementation {// Methods from ::android::hardware::my_test::V1_0::IMyTest follow.
Return<uint32_t> MyTest::add(uint32_t a, uint32_t b) {// TODO implementuint32_t result = a + b;ALOGE("MyTest::add  a = %d,b = %d,result = %d",a,b,result);return uint32_t {};
}Return<uint32_t> MyTest::sub(uint32_t a, uint32_t b) {// TODO implementuint32_t result = a - b;ALOGE("MyTest::sub  a = %d,b = %d,result = %d",a,b,result);return uint32_t {};
}Return<uint32_t> MyTest::mul(uint32_t a, uint32_t b) {// TODO implementuint32_t result = a * b;ALOGE("MyTest::mul  a = %d,b = %d,result = %d",a,b,result);return uint32_t {};
}Return<uint32_t> MyTest::div(uint32_t a, uint32_t b) {// TODO implementuint32_t result = a / b;ALOGE("MyTest::div  a = %d,b = %d,result = %d",a,b,result);return uint32_t {};
}// Methods from ::android::hidl::base::V1_0::IBase follow.IMyTest* HIDL_FETCH_IMyTest(const char* /* name */) {ALOGE("my_test service init success....");return new MyTest();
}
//
}  // namespace android::hardware::my_test::V1_0::implementation

同样需要更改命名空间

4.7 编译

现在除了需要的 rc 文件没有补充、mytest-hal-service 服务没有生成外其余均已配置好了,现在进行编译生成对应的库。进入根目录下执行如下命令:(注意是在刚刚执行过的 source build/envsetup.sh 和 lunch 的窗口下编译,若是新窗口则需要重新执行这两条命令)

mmm  hardware/interfaces/my_test/1.0

编译成功后会在 out\target\product\xxxxxxx\vendor\lib64\hw 下生成 android.hardware.my_test@1.0-impl.so;在 out\target\product\xxxxxxx\system\lib64 下生成 android.hardware.my_test@1.0.so;

4.8 生成Service

接下来我们需要生成对应的 service 可执行文件,这个过程一共分为三步:

1.在 /default 下的 Android.bp 文件中追加如下内容

cc_binary {name: "android.hardware.my_test@1.0-service",defaults: ["hidl_defaults"],relative_install_path: "hw",vendor: true,srcs: ["service.cpp"],init_rc: ["android.hardware.my_test@1.0-service.rc"],shared_libs: ["liblog","libhidlbase","libhidltransport","libhwbinder","libutils","android.hardware.my_test@1.0",],
}

2.补充 service.cpp 

defaultPassthroughServiceImplementation 会帮我们自动注册服务;

#define LOG_TAG "android.hardware.my_test@1.0-service"#include <android/hardware/my_test/1.0/IMyTest.h>
#include <hidl/LegacySupport.h>
#include "MyTest.h"// Generated HIDL files
using android::hardware::my_test::V1_0::IMyTest;
using android::hardware::my_test::V1_0::implementation::MyTest;using android::hardware::defaultPassthroughServiceImplementation;
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;int main() {return defaultPassthroughServiceImplementation<IMyTest>();
} 

3.补充 rc 文件

这里的 mytest-hal-service 相当于这个服务的别名

service mytest-hal-service /vendor/bin/hw/android.hardware.my_test@1.0-serviceclass haluser systemgroup system

再次在根目录执行如下命令:

mmm  hardware/interfaces/my_test/1.0

完成之后就会得到如下二进制可执行文件:

out\target\product\xxxxxxx\vendor\bin\hw\android.hardware.my_test@1.0-service

4.9 编写测试Client 

经过一系列过程之后,我们得到了三个产物:

1、android.hardware.my_test@1.0.so
2、android.hardware.my_test@1.0-impl.so
3、android.hardware.my_test@1.0-service

接下来模拟一个客户端来测试调用;在 default 目录下创建 test 目录,并新建 client.cpp、Android.bp 文件;创建好后工程目录如下:

client.cpp内容:

#include <android/hardware/my_test/1.0/IMyTest.h>
#include <hidl/Status.h>
#include <log/log.h>using android::sp;
using android::hardware::my_test::V1_0::IMyTest;
using android::hardware::Return;int main(){android::sp<IMyTest> service = IMyTest::getService();if (service == nullptr) {ALOGD("faile to get my_test service......");return -1;}ALOGE("success to get my_test service.....");uint32_t addResult = service->add(3,4);ALOGE("my_test service add: result = %d",(int)addResult);uint32_t subResult = service->sub(8,3);ALOGE("my_test service sub: result = %d",(int)subResult);uint32_t mulResult = service->mul(3,8);ALOGE("my_test service mul: result = %d",(int)mulResult);uint32_t divResult = service->div(8,2);ALOGE("my_test service div: result = %d",(int)divResult);return 0;
}

Android.bp内容:

cc_binary {name: "my_hidl_test",    //表示生成的 client 名称srcs: ["client.cpp"],shared_libs: ["liblog","android.hardware.my_test@1.0","libhidlbase","libhidltransport","libhwbinder","libutils",],
}

根目录执行 mmm  hardware/interfaces/my_test/1.0 编译工程,成功后可在 out\target\product\xxxxxxx\system\bin 目录下找到 my_hidl_test

五、验证

5.1 将产物推入机器

现在我们一共得到 4 个产物,使用 adb 命令将其 push 到车机对应目录下:

android.hardware.my_test@1.0.so             ====》     /vendor/lib64
android.hardware.my_test@1.0-impl.so     ====》     /vendor/lib64/hw
android.hardware.my_test@1.0-service     ====》     /vendor/bin/hw
my_hidl_test                                                ====》     /system/bin

5.2 修改manifest.xml

HIDL 想要被 framework 获取使用还需要在 manifest.xml 中注册,该文件在车机 /vendor/etc/vintf/ 目录下(不同厂商可能不同,以实际情况为准),添加下面的内容:

<hal format="hidl"><name>android.hardware.my_test</name><transport>hwbinder</transport><version>1.0</version><interface><name>IMyTest</name><instance>default</instance></interface><fqname>@1.0::IMyTest/default</fqname></hal>
<hal format="hidl">

5.3 运行Service

手动后台运行

adb root
adb remount
./vendor/bin/hw/android.hardware.my_test@1.0-service &

5.4 运行client

./system/bin/my_hidl_test &

运行后查看系统日志,有如下内容则成功:

01-01 00:01:58.127 13377 13377 E my_hidl_test: success to get my_test service.....
01-01 00:01:58.127 11110 11110 E android.hardware.my_test@1.0-service: MyTest::add  a = 3,b = 4,result = 7
01-01 00:01:58.128 13377 13377 E my_hidl_test: my_test service add: result = 0
01-01 00:01:58.128 11110 11110 E android.hardware.my_test@1.0-service: MyTest::sub  a = 8,b = 3,result = 5
01-01 00:01:58.128 13377 13377 E my_hidl_test: my_test service sub: result = 0
01-01 00:01:58.128 11110 11110 E android.hardware.my_test@1.0-service: MyTest::mul  a = 3,b = 8,result = 24
01-01 00:01:58.128 13377 13377 E my_hidl_test: my_test service mul: result = 0
01-01 00:01:58.129 11110 11110 E android.hardware.my_test@1.0-service: MyTest::div  a = 8,b = 2,result = 4
01-01 00:01:58.129 13377 13377 E my_hidl_test: my_test service div: result = 0

这篇关于Android HIDL概述与绑定模式的实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/643841

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景