在上一篇博客里,大致介绍了下Android O 中treble计划的一些背景与相关基本架构,这一篇中跟大家一起来探讨下HIDL相关的内容。

Android HAL类型 
在此之前的ANDROID版本当中Android HAL没有什么特殊的特殊的,也么有什么分类,但是从android 8.0开始,Android重构了HAL与Android FW之间的联系结构,所以Android HAL会被区分成以下2种类型: 
1,Binderized HALs,从名字上应该是指Binder化的HAL,对Android 比较熟悉的同学应该对binder这个东西很熟悉,我们是不是可以大胆猜猜下Android 8.0里的HAL是不是都是binder化了?也就是说HAL都被写成了binder service了?Android FW都是binder client?后续我们研究研究再来看看我们的猜测是不是正确的。 
2,Passthrough HALs,从google的官方介绍来说,这个是对原先HAL的包装,但是最终的binder service 跟binder client都是活在同一个进程当中。这个应该是对老版本HAL的兼容。 
3,Same-Process HALs,由于某些性能的因素,这些HALs必须运行在Android Framework 所在的进程当中。

按照google的要求,新设计生产的Android O设备,必须而且只能支持 Binderized HALs,而老版本的设备升级到Android O可以支持 Passthrough HALs,但是有些HAL也必须修改成 Binderized HALs。 
 
以下HAL可以根据是升级设备或者新设备自由选择: 
 
以下跟graphic相关的HAL因为涉及到性能问题,只能在同一个进程当中运行: 

HIDL的相关介绍 
HIDL的全称是HAL interface definition language(硬件抽象层接口定义语言),在此之前Android 有AIDL,架构在Android binder 之上,用来定义Android 基于Binder通信的Client 与Service之间的接口。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的语法,以及通过一个实际的例子来真实感受下HIDL。

HIDL的基本语法:

1)定义接口:

package android.hardware.tests.foo@1.0;
   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当中可以像JAVA/C代码一样,很容易定义出联合体/结构体变量。

3)定义成员函数: 
如上所示,定义的都是无参函数,如果需要定义一个带有参数的函数,可以写成doThis(float param);,这就代表是一个有参数,但是无返回值的函数。

而以上定义的 getCookie() generates (int32_t cookie); 其含义为:函数名为 getCookie,无参数传入,函数的返回值为一个int32_t 类型。

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。

3 filegroup {
   4     name: "android.hardware.tests.foo@1.0_hal",
   5     srcs: [
   6         "types.hal",
   7         "IFoo.hal",
   8         "IFooCallback.hal",
   9         "IMyTypes.hal",
  10         "ISimple.hal",
  11         "ISimpleTest.hal",
  12         "ITheirTypes.hal",
  13     ],
  14 }


我们可以看到”ISimpleTest.hal”,已经被加进编译文件列表当中。

而在生成的C++文件:

16 genrule {
  17     name: "android.hardware.tests.foo@1.0_genc++",
  18     tools: ["hidl-gen"],
  19     cmd: "$(location hidl-gen) -o $(genDir) -Lc++-sources -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport android.hardware.tests.foo@1.0",
  20     srcs: [
  21         ":android.hardware.tests.foo@1.0_hal",
  22     ],
  23     out: [
  24         "android/hardware/tests/foo/1.0/types.cpp",
  25         "android/hardware/tests/foo/1.0/FooAll.cpp",
  26         "android/hardware/tests/foo/1.0/FooCallbackAll.cpp",
  27         "android/hardware/tests/foo/1.0/MyTypesAll.cpp",
  28         "android/hardware/tests/foo/1.0/SimpleAll.cpp",
  29         "android/hardware/tests/foo/1.0/SimpleTestAll.cpp",
  30         "android/hardware/tests/foo/1.0/TheirTypesAll.cpp",
  31     ],                                                                                                                                                               
  32 }
  33


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

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


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

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

34 genrule {
  35     name: "android.hardware.tests.foo@1.0_genc++_headers",
  36     tools: ["hidl-gen"],
  37     cmd: "$(location hidl-gen) -o $(genDir) -Lc++-headers -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport android.hardware.tests.foo@1    .0",
  38     srcs: [
  39         ":android.hardware.tests.foo@1.0_hal",
  40     ],
  41     out: [
                ……………………….
  64         "android/hardware/tests/foo/1.0/ISimpleTest.h",
  65         "android/hardware/tests/foo/1.0/IHwSimpleTest.h",
  66         "android/hardware/tests/foo/1.0/BnHwSimpleTest.h",
  67         "android/hardware/tests/foo/1.0/BpHwSimpleTest.h",
  68         "android/hardware/tests/foo/1.0/BsSimpleTest.h",
                ………………………...
  74     ],
  75 }


从生成的头文件里看,我们看到有 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定义函数。

我们大胆的猜测下,HIDL编译生成的这些头文件使用方式应该是与AIDL编译出来的Bnxxxxx/Bpxxxxx作用类似,恭喜你,你的猜测很正确。

IFoo.h. Describes the pure IFoo interface in a C++ class; it contains the methods and types defined in the IFoointerface in the IFoo.hal file, translated to C++ types where necessary. Does not contain details related to the RPC mechanism (e.g., HwBinder) used to implement this interface. The class is namespaced with the package and version, e.g. ::android::hardware::samples::IFoo::V1_0. Both clients and servers include this header: Clients for calling methods on it and servers for implementing those methods. 
IHwFoo.h. Header file that contains declarations for functions that serialize data types used in the interface. Developers should never include his header directly (it does not contain any classes). 
BpFoo.h. A class that inherits from IFoo and describes the HwBinder proxy (client-side) implementation of the interface. Developers should never refer to this class directly. 
BnFoo.h. A class that holds a reference to an IFoo implementation and describes the HwBinder stub (server-side) implementation of the interface. Developers should never refer to this class directly.

FooAll.cpp. A class that contains the implementations for both the HwBinder proxy and the HwBinder stub. When a client calls an interface method, the proxy automatically marshals the arguments from the client and sends the transaction to the binder kernel driver, which delivers the transaction to the stub on the other side (which then calls the actual server implementation).

Google的解释还是挺清楚,我就不画蛇添足的翻译成中文了。

.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

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(); 
 } 
 而且nfc 模块自己写了一个.rc文件: 
 service nfc_hal_service /vendor/bin/hw/android.hardware.nfc@1.0-service 
 class hal 
 user nfc 
 group nfc

这样就能让系统的Init进程在开机阶段就把这个service启动起来。

稍微总结下HIDL相关的内容: 
1)HIDL是Android O里的Treble计划的核心。目的是通过HIDL语法构建出一个松耦合的系统,最终目的是为了方便Android 升级,解决碎片化的问题。 
2)Android为了实现 Binderized HAL有一个比较清晰的road map: 
不过其实还有些东西还没有涉及到,比如FMQ,后续再来做探讨。下一部分,我们开始来研究下Android 新给出的Vendor NDK.