Android HIDL 学习

链接:https://blog.csdn.net/lin20044140410/article/details/79578664

链接:https://blog.csdn.net/junwua/article/details/80594202

将上面几片文章整理了下,供后续学习参考。

1、Android HIDL 官方介绍

HAL 接口定义语言(简称 HIDL,发音为“hide-l”)是用于指定 HAL 和其用户之间的接口的一种接口描述语言 (IDL)。HIDL 允许指定类型和方法调用(会汇集到接口和软件包中)。从更广泛的意义上来说,HIDL 是用于在可以独立编译的代码库之间进行通信的系统。

HIDL 旨在用于进程间通信 (IPC)。进程之间的通信经过 Binder 化。对于必须与进程相关联的代码库,还可以使用直通模式(在 Java 中不受支持)。

HIDL 可指定数据结构和方法签名,这些内容会整理归类到接口(与类相似)中,而接口会汇集到软件包中。尽管 HIDL 具有一系列不同的关键字,但 C++ 和 Java 程序员对 HIDL 的语法并不陌生。此外,HIDL 还使用 Java 样式的注释。

2、HIDL design

HIDL 的目标是,框架可以在无需重新构建 HAL 的情况下进行替换。HAL 将由供应商或 SOC 制造商构建,放置在设备的 /vendor 分区中,这样一来,框架就可以在其自己的分区中通过 OTA 进行替换,而无需重新编译 HAL。

HIDL 设计在以下方面之间保持了平衡:

    互操作性。在可以使用各种架构、工具链和编译配置来编译的进程之间创建可互操作的可靠接口。HIDL 接口是分版本的,发布后不得再进行更改。
    效率。HIDL 会尝试尽可能减少复制操作的次数。HIDL 定义的数据以 C++ 标准布局数据结构传递至 C++ 代码,无需解压,可直接使用。此外,HIDL 还提供共享内存接口;由于 RPC 本身有点慢,因此 HIDL 支持两种无需使用 RPC 调用的数据传输方法:共享内存和快速消息队列 (FMQ)。
    直观。通过仅针对 RPC 使用 in 参数,HIDL 避开了内存所有权这一棘手问题(请参阅 Android 接口定义语言 (AIDL));无法从方法高效返回的值将通过回调函数返回。无论是将数据传递到 HIDL 中以进行传输,还是从 HIDL 接收数据,都不会改变数据的所有权,也就是说,数据所有权始终属于调用函数。数据仅需要在函数被调用期间保留,可在被调用的函数返回数据后立即清除。

2.1 使用直通模式

要将运行早期版本的 Android 的设备更新为使用 Android O,您可以将惯用的(和旧版)HAL 封装在一个新 HIDL 接口中,该接口将在绑定式模式和同进程(直通)模式提供 HAL。这种封装对于 HAL 和 Android 框架来说都是透明的。

直通模式仅适用于 C++ 客户端和实现。运行早期版本的 Android 的设备没有用 Java 编写的 HAL,因此 Java HAL 自然而然经过 Binder 化。

直通式标头文件

编译 .hal 文件时,除了用于 Binder 通信的标头之外,hidl-gen 还会生成一个额外的直通标头文件 BsFoo.h;此标头定义了会被执行 dlopen 操作的函数。由于直通式 HAL 在它们被调用的同一进程中运行,因此在大多数情况下,直通方法由直接函数调用(同一线程)来调用。oneway 方法在各自的线程中运行,因为它们不需要等待 HAL 来处理它们(这意味着,在直通模式下使用 oneway 方法的所有 HAL 对于线程必须是安全的)。

如果有一个 IFoo.hal,BsFoo.h 会封装 HIDL 生成的方法,以提供额外的功能(例如使 oneway 事务在其他线程中运行)。该文件类似于 BpFoo.h,不过,所需函数是直接调用的,并未使用 Binder 传递调用 IPC。未来,HAL 的实现可能提供多种实现结果,例如 FooFast HAL 和 FooAccurate HAL。在这种情况下,系统会针对每个额外的实现结果创建一个文件(例如 PTFooFast.cpp 和 PTFooAccurate.cpp)。


Binder 化直通式 HAL

您可以将支持直通模式的 HAL 实现 Binder 化。如果有一个 HAL 接口 a.b.c.d@M.N::IFoo,系统会创建两个软件包:

    a.b.c.d@M.N::IFoo-impl。包含 HAL 的实现,并暴露函数 IFoo* HIDL_FETCH_IFoo(const char* name)。在旧版设备上,此软件包经过 dlopen 处理,且实现使用 HIDL_FETCH_IFoo 进行了实例化。您可以使用 hidl-gen 和 -Lc++-impl 以及 -Landroidbp-impl 来生成基础代码。
    a.b.c.d@M.N::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 化的设备,因此不得在直通模式下打开服务。)

3、HIDL 语法

在HIDL里,与AIDL比较类似,底层也是基于binder机制。但是也有稍微不一样的地方。为了支持HIDL,Android 对BInder做了一定程度的修改。

这里写图片描述

HIDL的基本语法:

1)定义接口 文件IsimpleTest.hal:

interface ISimpleTest {
    //定义成员
    enum SomeBaseEnum : uint8_t {
        bar = 66
    };

    struct Goober {
        int32_t q;
        string name;
        string address;
    };
    //定义成员函数
    getCookie() generates (int32_t cookie);

    customVecInt() generates (vec<int32_t> chain);

    customVecStr() generates (vec<string> chain);

    mystr() generates (string str);

    myhandle() generates (handle str);
};

 

在这里,我们定义一个新的HIDL接口,取名叫做 ISimpleTest, 从语法上看有点像JAVA的语法。interface是关键字,代表要创建一个HIDL的接口。我们把上述接口保存成 IsimpleTest.hal文件存放在hardware/interfaces/tests/foo/1.0/ISimpleTest.hal,其实我们完全可以新建一个新目录,使用一个新的package名,而不使用android.hardware.tests.foo,

2)HIDL编译

在根目录执行./hardware/interfaces/update-makefiles.sh,我们能够看到会把Android 在hardware/interfaces下的所有package都会更新一遍,我们看下hardware/interfaces/tests/foo/1.0/Android.bp,在Android O当中,貌似使用了 Android.bp来替代Android.mk来作为编译管理工具。至于Android.bp的东西可以后续在研究,这里我们只关注于HIDL。

filegroup {
    name: "android.hardware.tests.foo@1.0_hal",
     srcs: [
         "types.hal",
         "IFoo.hal",
         "IFooCallback.hal",
         "IMyTypes.hal",
        "ISimple.hal",
         "ISimpleTest.hal",
         "ITheirTypes.hal",
     ],
}

而在生成的C++文件:

genrule {
    name: "android.hardware.tests.foo@1.0_genc++",
    tools: ["hidl-gen"],
    cmd: "$(location hidl-gen) -o $(genDir) -Lc++-sources -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport android.hardware.tests.foo@1.0",
    
    srcs: [
        ":android.hardware.tests.foo@1.0_hal",
    ],

    out: [
        "android/hardware/tests/foo/1.0/types.cpp",

        "android/hardware/tests/foo/1.0/FooAll.cpp",

        "android/hardware/tests/foo/1.0/FooCallbackAll.cpp",

        "android/hardware/tests/foo/1.0/MyTypesAll.cpp",

        "android/hardware/tests/foo/1.0/SimpleAll.cpp",

        "android/hardware/tests/foo/1.0/SimpleTestAll.cpp",

        "android/hardware/tests/foo/1.0/TheirTypesAll.cpp",
    ],
}

我们可以看到这段逻辑是利用 hidl-gen工具来生成.cpp文件。命令是: cmd: “(genDir) -Lc++-sources -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport android.hardware.tests.foo@1.0”, 

.hal源码是:

srcs: [
    ":android.hardware.tests.foo@1.0_hal",
],

而这部分就是上面所定义的各种.hal文件。最终输出就是各种.cpp文件。我们比较关注的就是 SimpleTestAll.cpp文件。

同时会生成以下一些头文件:

genrule {
    name: "android.hardware.tests.foo@1.0_genc++_headers",
    tools: ["hidl-gen"],
    cmd: "$(location hidl-gen) -o $(genDir) -Lc++-headers -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport android.hardware.tests.foo@1 .0",
    
    srcs: [
        ":android.hardware.tests.foo@1.0_hal",
    ],
    out: [
        ……………………….
        "android/hardware/tests/foo/1.0/ISimpleTest.h",
        "android/hardware/tests/foo/1.0/IHwSimpleTest.h",
        "android/hardware/tests/foo/1.0/BnHwSimpleTest.h",
        "android/hardware/tests/foo/1.0/BpHwSimpleTest.h",
        "android/hardware/tests/foo/1.0/BsSimpleTest.h",
        ………………………...
    ],
}

 

从生成的头文件里看,我们看到有 ISimpleTest.h, BnHwSimpleTest.h, BpHwSimpleTest.h,Bnxxxxx与Bpxxxxx这两个东西我们是不是看起来很眼熟?在Binder里, Ixxxxx.h定义了client与service统一的通用接口,而Bnxxxxx.h 派生自 Ixxxxx.h,做为service端实现的头文件,Bpxxxxx.h同样派生自 Ixxxxx.h做为client端的头文件。这样调用Bpxxxxx.h定义的接口,就自动利用binder机制跨进程由service端实现了Bnxxxxx.h定义函数。

由编译器生成的文件。

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

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

注意:HwBinder 只是一种可能的传输机制,未来可能会添加新的传输机制。

.hal最终编译出来的结果是:

cc_library_shared {
    name: "android.hardware.tests.foo@1.0",
    defaults: ["hidl-module-defaults"],
    generated_sources: ["android.hardware.tests.foo@1.0_genc++"],
    generated_headers: ["android.hardware.tests.foo@1.0_genc++_headers"],
    export_generated_headers: ["android.hardware.tests.foo@1.0_genc++_headers"],
    vendor_available: true,
    
    shared_libs: [
        "libhidlbase",
        "libhidltransport",
        "libhwbinder",
        "liblog",
        "libutils",
        "libcutils",
        "android.hidl.base@1.0",
    ],
    
    export_shared_lib_headers: [
    "libhidlbase",
    "libhidltransport",
    "libhwbinder",
    "libutils",
    "android.hidl.base@1.0",
    ],
}

从上面看得很清楚,.hal文件被编译后会生成一个动态库文件 android.hardware.tests.foo@1.0.so

3)HIDL的使用 

HIDL的使用,其实就是指怎么在service端实现,怎么在client端调用。其实也挺简单,基本流程就是service端往系统里注册,client从系统里拿到service 的proxy,然后调用。跟AIDL的Binder一样一样的。

Client端拿service的proxy: 
foo = IFoo::getService(“foo”, mode == PASSTHROUGH /* getStub */);

使用的是Ixxx里自动生成的 getService函数,拿到之后就能使用.hal定义的接口了。

Service端往系统注册: 
int main() { 
return defaultPassthroughServiceImplementation(); 
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值