Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2248957
  • 博文数量: 318
  • 博客积分: 8752
  • 博客等级: 中将
  • 技术积分: 4944
  • 用 户 组: 普通用户
  • 注册时间: 2006-05-23 07:56
文章分类

全部博文(318)

文章存档

2019年(1)

2017年(2)

2016年(12)

2015年(2)

2014年(1)

2013年(17)

2012年(22)

2011年(9)

2010年(37)

2009年(33)

2008年(44)

2007年(43)

2006年(95)

分类: Android平台

2017-11-01 16:21:29

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 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 } 我们可以看到这段逻辑是利用 hidl-gen工具来生成.cpp文件。命令是: cmd: “(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.



使用 passthrough 模式

为了更新运行在Android系统早期版本的设备到Android O操作系统,你可以将传统的(和 legacy遗留的)HALs封装在新的HIDL接口中,这一接口以binderized 和same-process (passthrough)的模式服务于HAL。这种封装对HAL和Android框架都是透明的。

Passthrough模式仅适用于c++的客户端和实现。运行Android早期版本的设备没有Java编写的HALs,因此Java HALs必然使用binderized 的模式。

Passthrough header files

当一个.hal文件被编译后, hidl-gen除了用于binder通信的头文件外,还生成一个额外的 passthrough 头文件 BsFoo.h ;这个头文件定义了dlopened的函数. 由于passthrough HALs 运行在调用它们的相同的进程中,大多数情况下 passthrough 方法被直接通过函数调用 (相同线程)。oneway 方法在他们自己的线程中运行,因为它们并不打算等待HAL来处理它们(这意味着任何在passthrough模式中使用oneway方法的HAL必须是线 程安全的)。

给定一个IFoo.hal文件,BsFoo.h封装了hidl生成的方法以提供额外的特性(比如在另一个线程中运行oneway事务)。这个文件类 似于BpFoo。但是,不是通过binder进行IPC调用,而是直接调用所需的函数。未来HALs可能提供多种实现,例如FooFast HAL和FooAccurate HAL。在这种情况下,将创建每个额外实现的文件(例如:PTFooFast.cpp和PTFooAccurate.cpp)。

Binderizing passthrough HALs

支持passthrough 模式的HAL实现可以binderized 。对于一个HAL 接口a.b.c.d@M.N::IFoo,需要创建两个包:

  • a.b.c.d@M.N::IFoo-impl. 包含HAL的实现,并暴露函数IFoo * HIDL_FETCH_IFoo(const char * name)。在legacy hal设备上,这个包是dlopened 的,实现是使用HIDL_FETCH_IFoo实例化的。 您可以使用hidl - gen和- lc + + - impl和- landroidbp - impl生成基本代码。
  • a.b.c.d@M.N::IFoo-service. 打开passthrough HAL并将其注册为一个binder化服务,使相同的HAL实现被用作passthrough 和binderized。

给定类型IFoo,您可以调用sp < IFoo > IFoo::getService(string name, bool getStub)来获取IFoo的实例。

如果getStub值是true,getService尝试只以passthrough模式打开HAL。如果getStub为false,则 getService尝试查找到一个binderized 服务;如果失败,则尝试找到passthrough服务。getStub参数除了 defaultPassthroughServiceImplementation不应使用。(使用Android O的设备是完全binderized 的设备,因此不允许以passthrough模式打开一个服务。)


每个 HIDL package包里都含有一个名为types.hal的文件,该文件中定义了这个包里所有 interface 共享的用户自定义数据类型,并且一般也会导入需要用到的其它包里的数据类型。
  当前包中新的定义的 interface 可以继承自从其它包里导入的 interface,这样的继承关系可以使用extend关键字实现。比如下面示例中的 1.1 版本包中的 IQuux 接口就继承自 1.0 版本包中的 IQuux 接口:
// types.hal package android.hardware.example@1.1 import android.hardware.example@1.0 // 导入1.0的包

// IQuux.hal package android.hardware.example@1.1 interface IQuux extends @1.0::IQuux {  // 继承1.0包中的接口
  fromBarToFoo(foo.bar b) generates (foo f);  // 直接使用fromBarToFoo方法而不再在当前包中声明 }


阅读(8191) | 评论(0) | 转发(0) |
1

上一篇:hidl 摘抄

下一篇:将博客搬至CSDN

给主人留下些什么吧!~~