HAL binder是Android O(8.0)专门用于HAL(Hardware Abstract Layer)层(native)进程与其clients之间的通信机制(clients可以是native进程,也可以是Java Framework进程)。 HAL binder替代了早先使用的socket通信,其kernel层实际是基于原有的binder驱动,但为了配合Client与Server之间的数据传输,需要使用特定的中间层HIDL来进行接口与数据的转换。那么,相对之前的HAL通信方式(socket),基于HIDL的HAL通信有什么优势了?从系统架构的角度,HIDL为客户端与服务端提供了清晰的接口;从效率的角度,binder IPC实际在传输数据上只有一次拷贝,而socket实际传输需要两次数据拷贝。
目前Android有两种类型的HAL:
binder化的HAL: 利用HIDL(HAL interface Definition Language)来描述HAL接口,Framework层与HAL层通过binder IPC的方式进行通信;如下的HAL模块都是利用binder IPC来进行通信的:
直通式HAL: 基于HIDL或者传统HAL方式来实现,在这种模式下,Framework层与HAL层的通信可以通过IPC的方式进行,也可以使用共享内存的方式在同一个进程内进行(passthrough,直通)。目前有如下两个HAL模块使用直通式方式进行通信:
相关代码
/android/system/tools/hidl/
: 根据HAL接口.hal
来产生相应的Proxy(client端接口)以及stub(server端接口)。/android/system/libhwbinder/
: 初始化binder线程,负责与binder驱动交互,读写数据;/android/system/libhidl
: HIDL状态与HAL服务管理接口;/android/os/HwBinder
: Java层Hardware binder代码,获取server端接口以及向server发起IPC调用;android_os_HwBinder.cpp (JNI)
:Java层HAL binder的JNI代码,负责将Java层的请求传递给相应的server进程;/android/hardware/interfaces/
: 各个模块HAL层接口,每个模块都包含了一个Android.bp
的脚步来生成对应的代理(Proxy)与存根对象(stub),如radio接口IRadio.hal
,sensors接口ISensors.hal
;/android/kernel/drivers/staging/android/
: binder驱动;
下图是HAL binder的结构简图, 了解Binder的同学应该很快能看出,这个结构跟binder的C/S IPC架构很相似,区别的地方在对于HAL binder来说,server进程是Android的native进程而已。在接下来的两篇文章里,我将以Telephony Framwork(RILJ)与native进程RILD是如何通过hardware binder来进行通信为例,从以下两个方面来说明HAL Binder的实现机制与工作原理:
- HAL服务管家
HwServiceMananger
是如何启动以及如何注册、获取系统服务; - RILJ如何通过HAL binder与RILD进行通信?
关于Android.bp脚本可以参考https://android.googlesource.com/platform/build/soong/
这篇文章,主要来看下第一个问题,HAL binder是如何启动以及管理所有HAL服务的。跟常规的binder通信(AMS,PMS等使用的binder)一样,HAL binder也需要有一个专门的服务管家,来统一管理系统的服务,同时为客户端提供诸如注册、获取服务等API。
HwServiceMananger的启动
在/android/system/hwservicemanager/hwservicemanager.rc
目录下,有个启动脚本hwservicemanager.rc
,init进程在启动之后会对该脚本进行解析:
1 |
|
在/android/system/core/rootdir/init.rc
文件中,有个控制指令,专门来启动hwservicemananger
这个系统服务:
1 |
|
这样,系统加载hwservicemanager
,进入main函数:
1 |
|
ServiceManager
启动主要需要做以下几件事情:
- 初始化
/dev/hwbinder
驱动,为其分配一块虚拟内存用于IPC数据交换; - 向
hwbinder
注册HAL服务管家(IPC上下文管理者); - 监听
/dev/hwbinder
是否有数据可读,如有则调用回调执行指令;
hwbinder驱动初始化
HwServiceManager
启动的第一件事情,就是配置binder线程池,告知驱动当前需要启动多少个线程处理IPC指令:对于HwServiceManager
而言,只需要一个binder线程,即调用者线程即可(调用者线程何时加入binder线程池下面会讲到),而无需启动新的线程。
1 |
|
ProcessState
只有一个实例(ProcessState
可以看成是hwbinder
用户空间进程状态管理者),因此在调用ProcessState():self
的时候,如果当前ProcessState
没有创建全局实例,则会创建一个实例:
1 |
|
创建ProcessState
的时候,主要做两件事:
- 打开
/dev/hwbinder
驱动,验证binder版本以及协议版本,并配置最大的binder线程数量(对于hwbinder,默认的最大线程数量为0); - 将
/dev/hwbinder
映射到一块大小约为1M的虚拟内存上用于交换IPC数据;
1 |
|
ProcessState
初始化完成后,获取到相应实例,配置线程池(对于每个HAL服务来是,一般线程池只有一个线程,就是服务进行注册时所在的
主线程),告知线程当前所需的最大线程数目:
1 |
|
注册HAL服务管家
线程池配置完成后,获取当前线程的Looper(对的,就是Framework层常用的Handler消息循环在native的对应),并告知HwBinder驱动由当前线程来处理HAL服务上下文相关的指令, 设置Looper消息回调。接着,就是注册HAL服务的管家ServiceManager
了。
ServiceManager
继承了IServiceManager
接口,负责管理系统中所有的HAL服务,为其他进程提供服务注册、查找等功能:
1 |
|
接着, 让BnHwServiceManager
成为HAL服务的上下文管家,需要做两件事:一是告知IPCThreadState
负责上下文管理的对象,用于接收来自其他进程的IPC请求;一是告知kernel上下文管理者的是一个handle为0的IBinder
对象,kernel会为其保存一个节点,以便IPC时使用。
1 |
|
BnHwServiceManager
是ServiceManager
服务端的stub对象,与客户端代理ProxyBpHwServiceManager
相对应,具体怎么来的,在下一篇文章中再详细讲述
最后,设置hwservicemanager.ready
,表面当前hwservicemanager
已经处于可用状态;pollAll
则表示hwservicemanager
进入消息循环等待的过程,一旦/dev/hwbinder
有数据可读,就会调用之前注册的BinderCallback
进行处理。
1 |
|
在接下来的一篇文章,基于Telephony
与RILD
的通信来讲述HAL服务的具体工作流程。