图片来自http://gityuan.com/2019/03/20/android_future/
Android 8.0系统(Android Oreo简称 Android O),Google开展了计划(Project Treble),为了解决Android 系统的碎片化问题和提高系统更新的效率,减少framework 和HAL 的耦合性,进而引出了HIDL 的概念。
Treble引入后,新增了一个vendor.img,即原先的system分区,被拆分为了system分区和vendor分区,SOC及供应商的功能实现都需要放到vendor分区,这样将system和vendor相关的镜像分开,便于能方便地更新和升级system,并且不依赖vendor等底层。
在Android 8.0之前,HAL是一个个的.so库,通过dlopen来进行打开,库和Android Framework位于同一个进程,具体如下图所示。
所以,Android Framework 与Android HAL被打包成一个system.img。Framework 与HAL是紧耦合的,通过link方式使用相应的硬件相关so库。
而Android 8.0引入HIDL,把Android Framework放在system.img分区,而Vendor HAL Implemetation放在新的分区vendor.img,这样刷新的system.img 就不会影响到Vendor HAL Implemetation。达到解耦的目的。
我们来看看官方的介绍:https://source.android.google.cn/devices/architecture/hidl-cpp
对于Android 8.0之前的设备,对应图①。 对于从之前的设备升级到8.0的版本,对应图②、图③。 对于直接基于Android 8.0开发的设备,对应图④。
Legacy HALs: 旧版本,Android 8.0开始不再维护 https://source.android.google.cn/devices/architecture/hal
通常代码里面会有相应的宏定义隔离 USE_LEGACY_XXX: 比如 USE_LEGACY_LOCAL_AUDIO_HAL,USE_LEGACY_PRESENTER
也有对应的源码目录 legacy dirs: 比如./frameworks/av/media/libaaudio/src/legacy, ./kernel/drivers/usb/gadget/legacy ./hardware/qcom/audio/legacy, ./hardware/rockchip/audio/legacy_hal .....
Conventional HALs (传统HALs): deprecated in Android 8.0 .
HIDL全称为HAL interface definition language(发音为“hide-l”),分为两种模式:Passthrough(直通式)和Binderized(绑定式)。
为了兼容Android 8.0之前的版本,Android 8.0设计了Passthrough HAL,就是把传统或旧版的HALs做一层封装,所以不需要重新编写相关的HAL就能支持8.0架构。
所以接下来就继续用audio模块来看一下HIDL(Passthrough HAL)的框架。
Passthrough HIDL
以audio为例, 当编译./hardware/interfaces/audio/2.0 目录下的源码时候,会按如下规则生成对应的文件:
./hardware/interfaces/audio/2.0 目录下的源码编译后: 对应 ./out/target/product/px30_evb/system/lib/android.hardware.audio@2.0.so
./hardware/interfaces/audio/2.0/default 目录下的源码编译后: 对应 ./out/target/product/px30_evb/vendor/lib/hw/android.hardware.audio@2.0-impl.so
同时还会生成对应的可执行程序: ./out/target/product/px30_evb/vendor/bin/hw/android.hardware.audio@2.0-service 以及android.hardware.audio@2.0-service.rc文件,此文件会被拷贝到vendor.img里面的./vendor/etc/init目录下。
hidl-gen
上面的规则实际是使用工具hidl-gen来生成的,在 ./out/host/linux-x86/bin/hidl-gen, 命令使用方法如下:
fqname: 完全限定名称的输入文件。比如android.hardware.audio@2.0,就要求在源码目录下必须有./hardware/interfaces/audio/2.0/目录。
Android.mk:通过hidl-gen -Lmakefile -r xxx 生成
Android.bp:通过hidl-gen -Landroidbp -r xxx 生成
hidl-gen -Lc++-sources 生成源码文件,在如下目录 out/soong/.intermediates/hardware/interfaces/audio/2.0/android.hardware.audio@2.0_genc++/gen
hidl-gen -Lc++-headers 生成头文件在如下目录 out/soong/.intermediates/hardware/interfaces/audio/2.0/android.hardware.audio@2.0_genc++_headers/gen
android.hardware.audio@2.0-service
在Andorid系统开机过程的某一个阶段,会启动class是hal的服务(init下rc文件的解析),执行./vendor/lib/hw/android.hardware.audio@2.0-service,从而调用./hardware/interfaces/audio/2.0/default/service.cpp的main()方法。代码如下:
分别调用registerPassthroughServiceImplementation<xxx>来注册对应的Service. 此函数位于 ./system/libhidl/transport/include/hidl/LegacySupport.h
首先调用IXXX 的getService(“default”,true)来获取IXXX的类对象,比如: IDevicesFactory::getService(“default”, true); IEffectsFactory::getService(“default”, true); ISoundTriggerHw::getService(“default”, true);
接着调用registerAsService来将它们注册到hwservicemanager中。由于这个过程调用的函数很多都是在hidl-gen工具生成的中间文件里,略微复杂,所以先用下图来直观描述下:
参考代码:
./hardware/interfaces/audio/2.0/default/service.cpp
./system/hwservicemanager/service.cpp
./system/libhidl/transport/ServiceManagement.cpp
hidl-gen工具生成的中间文件: ./out/soong/.intermediates/hardware/interfaces/audio/2.0/android.hardware.audio@2.0_genc++/gen/android/hardware/audio/2.0/DevicesFactoryAll.cpp 、EffectsFactoryAll.cpp、SoundTriggerHwAll.cpp、ServiceManagerAll.cpp
这里我们不去细究hwservicemanager,无非就是负责管理所有 hal services, 属于Android 8.0新增的,就像原来的servicemanager。
然后图中还看到了HwBinder它也是Android 8.0新增的, 但是和传统的Binder一样都是用于两个进程间的Binder IPC通信。
根据上图得知getService会通过dlopen方式加载对应的 xxx-impl.so,下面就从源码来梳理一下,以 DevicesFactoryAll.cpp 为例:
Android 8.0中定义的HIDL Tranport包括passthrough和hwbinder,每个HIDL Service都会在 ./out/target/product/px30_evb/system/manifest.xml 或 ./out/target/product/px30_evb/vendor/manifest.xml中指定它们对应的Tranport类型。 IXXX::getService(“default”, true)函数会先调用Return<Transport> transportRet = sm->getTransport(IDevicesFactory::descriptor, serviceName); 得到IXXX的Tranport类型。 本例中IDevicesFactory的Tranport类型按配置为Transport::HWBINDER
但是注意:如果调用者把第二个参数getStub标志都置为了true,那么无论getTransport()返回的是 passthrough还是hwbinder,最后都会走到 const sp<::android::hidl::manager::V1_0::IServiceManager> pm = getPassthroughServiceManager();
PassthroughServiceManager的get()方法,会根据传入的descriptor(android.hardware.audio@2.0::IDeviceFactory"),获取接口名IDeviceFactory,拼接出需要载入的函数名HIDL_FETCH_IDeviceFactory 和库名android.hardware.audio@2.0-impl, 接着通过dlopen载入/vendor/lib/hw/android.hardware.audio@2.0-impl.so, 然后通过dlsym载入HIDL_FETCH_IDeviceFactory函数地址。
另外两个模块 IEffectsFactory和ISoundTriggerHw也是类似,即:
./hardware/interfaces/audio/effect/2.0目录通过hidl-gen生成源码后编译结果对应 ./system/lib/android.hardware.audio.effect@2.0.so 主要实现在如下文件: ./out/soong/.intermediates/hardware/interfaces/audio/effect/2.0/android.hardware.audio.effect@2.0_genc++_headers/gen/android/hardware/audio/effect/2.0/IEffectsFactory.h ./out/soong/.intermediates/hardware/interfaces/audio/effect/2.0/android.hardware.audio.effect@2.0_genc++/gen/android/hardware/audio/effect/2.0/EffectsFactoryAll.cpp
./hardware/interfaces/audio/effect/2.0/default目录下面源码编译结果对应 ./vendor/lib/hw/android.hardware.audio.effect@2.0-impl.so 主要实现在如下文件: ./hardware/interfaces/audio/effect/2.0/default/EffectsFactory.cpp
./hardware/interfaces/soundtrigger/2.0目录通过hidl-gen生成源码后编译结果对应 ./system/lib/android.hardware.soundtrigger@2.0.so 主要实现在如下文件: ./out/soong/.intermediates/hardware/interfaces/soundtrigger/2.0/android.hardware.soundtrigger@2.0_genc++_headers/gen/android/hardware/soundtrigger/2.0/ISoundTriggerHw.h ./out/soong/.intermediates/hardware/interfaces/soundtrigger/2.0/android.hardware.soundtrigger@2.0_genc++/gen/android/hardware/soundtrigger/2.0/SoundTriggerHwAll.cpp
./hardware/interfaces/soundtrigger/2.0/default目录下面源码编译结果对应./vendor/lib/hw/android.hardware.soundtrigger@2.0-impl.so 主要实现在如下文件: ./hardware/interfaces/soundtrigger/2.0/default/SoundTriggerHalImpl.cpp
总结来说,就是安卓系统在开机过程的某一阶段,会启动class是hal的服务(可以看./vendor/etc/init/下的android.hardware.xxx@vvv-service.rc文件,比如 android.hardware.audio@2.0-service.rc),也就是启动相应的进程android.hardware.xxx@vvv-service,该进程在编译(通过hidl-gen工具)时就link到了对应的接口定义类的库文件./system/lib/android.hardware.xxx@vvv.so (还可能link到其它子模块,比如android.hardware.audio@2.0-service就会link到3个模块:android.hardware.audio@2.0.so,android.hardware.audio.effect@2.0.so,android.hardware.soundtrigger@2.0.so);
然后它的main函数(./hardware/interfaces/xxx/vvv/default/service.cpp)会向 HW Service Manger注册,并通过dlopen方式打开接口实现类对应的库文件./vendor/lib/hw/android.hardware.xxx@vvv-impl.so,通过dlsym查找并调用HIDL_FETCH_Iyyy函数(android.hardware.audio@2.0-impl.so对应的HIDL_FETCH_IDeviceFactory()函数定义在./hardware/interfaces/audio/2.0/default/DeviceFactory.cpp)。
另外impl.so最终会和厂家HAL层代码(本例中对应./vendor/lib/hw/audio.primary.rk30board.so)通过 hw_get_module_by_class()方式关联起来。
通过lsof命令,可以看到进程对应加载的so文件,例如 lsof -p 226 | grep audio.primary (226是 android.hardware.audio@2.0-service的进程id)
audio@2.0-service 226 mem REG 179,13 145580 123 /vendor/lib/hw/audio.primary.rk30board.so
openDevice - binder clinet端
在上一篇 Android Framework源码解读 - Audio - audioserver启动流程(1) 提到AudioFlinger会通过loadHwModule加载这些audio HAL层模块, 而Andorid 8.0后就是采用HIDL方式,所以最后走的是./frameworks/av/media/libaudiohal/DevicesFactoryHalHidl.cpp 的openDevice函数。
但是注意:从上一节得知android.hardware.audio@2.0-service才是真正负责加载HIDL HAL模块的,所以AudioFlinger(audioservice进程)调用的loadHwModule/openDevice必须要通过Binder IPC“告知” audio@2.0-service。也就是audioservice得是个binder client。那么是怎么做到的呢? 我们来看DevicesFactoryHalHidl的构造函数:
同binder server端的getService()不同的是:binder client端的getService()没有参数,所以最终binder client端是通过hwservicemanager拿到了IDeviceFactory对象;
而binder server端是使用openLibs打开对应的-impl.so,得到IDeviceFactory对象。 binder client端拿到IDeviceFactory后就会new出一个BpHwDeviceFactory。
同样我们也先通过一个简单的图来直观理解一下:
三个进程 audioserver, audio@2.0-service, hwservicemanager通过Binder IPC来通信。
audioserver的openDevice操作 由 BpHwDeviceFactory::transact(1,,)发出,被audio@2.0-service的BnHwDevicesFactory::onTransact(1,,)收到后调用DevicesFactory.cpp (位于android.hardware.audio@2.0-impl.so)的openDevice()最终加载了厂家的hal层代码(位于audio.primary.rk30board.so),下面详细看一下server端的openDevice();
openDevice - binder server端
./hardware/interfaces/audio/2.0/default/DevicesFactory.cpp
./hardware/interfaces/audio/2.0/default/DevicesFactory.h
load audio interface的功能就是调用hw_get_module_by_class()来加载HAL层APIs。
当调用者需要加载某个HAL层模块时,只要指定它们的ID值(audio模块的 ID是 AUDIO_HARDWARE_MODULE_ID 即字符串”audio”, 定义在./hardware/libhardware/include/hardware/audio.h)就可以了。
./hardware/libhardware/hardware.c
hw_get_module_by_class() 函数会根据传入的字符(例如audio)补全为so库名称(根据配置 ./frameworks/av/services/audiopolicy/config/audio_policy_configuration.xml),然后在系统中目录中查找,按顺序 ① HAL_LIBRARY_PATH3 "/odm/lib/hw" -> ② HAL_LIBRARY_PATH2 "/vendor/lib/hw" -> ③HAL_LIBRARY_PATH1 "/system/lib/hw",若找到so文件则调用load()函数打开,同时按固定符号HAL_MODULE_INFO_SYM查找结构体hw_module_t,获取硬件结构地址。
**** dlopen() 和 android_load_sphal_libaray() 这里也不去细究,后者无非是有一些sphal namespace的解析。
load audio interface的第二步是 audio_hw_device_open()代码位于:
./hardware/libhardware/include/hardware/audio.h
./hardware/rockchip/audio/tinyalsa_hal/audio_hw.c
./hardware/rockchip/audio/tinyalsa_hal/audio_route.c
1)、route_init函数,主要用于mixer功能,先建立route_table,为之后的行为做初始化工作:
fp = fopen("/proc/asound/card0/id", "rt");
route_set_input_source() route_set_voice_volume() route_pcm_open()
2)、sound_trigger_init() ,伪代码:
adev_open() {
sound_trigger_init() {
adev_sound_trigger_open();
}
}
adev_sound_trigger_open() (代码位于./hardware/rockchip/audio/tinyalsa_hal/audio_hw_soundtrigger.c)里通过dlopen方式加载:./system/lib/hw/ sound_trigger.primary.default.so
小结
3个进程,通过Binder(确切说是Android8.0的hwbinder)机制进程IPC通信: audioserver、android.hardware.audio@2.0-service、hwservicemanager
其中android.hardware.audio@2.0-service是真正负责和HAL硬件驱动交互的进程。
audioserver所有相关的硬件操作都要通过IPC来告知audio@2.0-service。
audioserver运行的时候启动三个service: audioflinger service, audiopolicy service, soundtrigger service。
audioserver通过openDevice请求来告知audio@2.0-service去加载厂商代码audio.primary.rk30board.so (会再dlopen sound_trigger.primary.default.so)。
audioserver通过openInputStream/openOutputStream请求来告知audio@2.0-service去创建对应的Binder Server: BnHwDevice,BnHwStreamIn,BnHwStreamOut
B: Binder ,n: native,p: proxy所以Bnxxx是binder server端实现,Bpxxx是binder客户端实现
附录
下面截取了一段rockchip平台 android 8.1系统开机日志, 可看到本文提到的三个进程启动顺序:
[ 4.178486] init: Parsing file /init.rc...
[ 4.199762] init: Parsing file /system/etc/init/audioserver.rc...
[ 4.207524] init: Parsing file /system/etc/init/hwservicemanager.rc...
[ 4.235558] init: Parsing file /vendor/etc/init/android.hardware.audio@2.0-service.rc...
[ 4.935777] init: starting service 'hwservicemanager'...
[ 5.703909] init: starting service 'audio-hal-2-0'...
[ 6.394742] init: starting service 'audioserver'...