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 one

cc_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 implement
	uint32_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 implement
	uint32_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 implement
	uint32_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 implement
	uint32_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 文件

系统开机会解析该 .rc 文件,启动该服务。这里的 mytest-hal-service 相当于这个服务的别名;如果需要开机自启动,需要配置 seLinux 权限,这里暂不介绍;因此下面我们测试验证的时候通过手动启动的方式启动服务。

service mytest-hal-service /vendor/bin/hw/android.hardware.my_test@1.0-service
    class hal
    user system
    group 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
  • 25
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Android HIDL(Hardware Interface Definition Language)是一种用于定义硬件抽象层(HAL)接口的语言。它旨在提供一个标准化的接口规范,以促进硬件和软件之间的互操作性。 根据官方网站的介绍,Android HIDL允许将早期版本的Android设备升级为使用Android O。通过将传统的HAL封装到新的HIDL接口中,HAL和Android框架之间的通信变得更加透明。这种封装可以在绑定模式和同进程(直通)模式下提供HAL,并具有跨设备兼容性的优势。 Android 8.0引入了Treble项目,旨在重新设计Android操作系统框架,以解决系统版本碎片化问题和复杂的系统更新流程。Treble引入了HIDL作为其核心组件之一,以提供更好的硬件和软件兼容性。 综上所述,Android HIDL是一种用于定义硬件抽象层接口的语言,它在Android O及其后续版本中起到了重要的作用,帮助解决Android系统版本碎片化和系统更新的难题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Android HIDL(1) ---- 概述](https://blog.csdn.net/h_8410435/article/details/126283950)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [Android HIDL 详解](https://blog.csdn.net/ismartv_dagou2/article/details/124361229)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A-sL1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值