Android Audio cec架构 android.hardware.audio@2.0_android

图片来自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 Audio cec架构 android.hardware.audio@2.0_java_02

而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 Audio cec架构 android.hardware.audio@2.0_java_03

对于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 HALs: Devices running Android 8.0 and higher must support HALs written in HIDL to adopt a new, more modular architecture.

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, 命令使用方法如下:

Android Audio cec架构 android.hardware.audio@2.0_c++_04

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()方法。代码如下:

Android Audio cec架构 android.hardware.audio@2.0_java_05

分别调用registerPassthroughServiceImplementation<xxx>来注册对应的Service. 此函数位于 ./system/libhidl/transport/include/hidl/LegacySupport.h

Android Audio cec架构 android.hardware.audio@2.0_Android Audio cec架构_06

首先调用IXXX 的getService(“default”,true)来获取IXXX的类对象,比如: IDevicesFactory::getService(“default”, true); IEffectsFactory::getService(“default”, true); ISoundTriggerHw::getService(“default”, true);

接着调用registerAsService来将它们注册到hwservicemanager中。由于这个过程调用的函数很多都是在hidl-gen工具生成的中间文件里,略微复杂,所以先用下图来直观描述下:

Android Audio cec架构 android.hardware.audio@2.0_android_07

参考代码:

./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 Audio cec架构 android.hardware.audio@2.0_android_08

    

Android Audio cec架构 android.hardware.audio@2.0_Android Audio cec架构_09

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();

Android Audio cec架构 android.hardware.audio@2.0_android_10

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 Audio cec架构 android.hardware.audio@2.0_java_11

Android Audio cec架构 android.hardware.audio@2.0_Android Audio cec架构_12

但是注意:从上一节得知android.hardware.audio@2.0-service才是真正负责加载HIDL HAL模块的,所以AudioFlinger(audioservice进程)调用的loadHwModule/openDevice必须要通过Binder IPC“告知” audio@2.0-service。也就是audioservice得是个binder client。那么是怎么做到的呢? 我们来看DevicesFactoryHalHidl的构造函数:

Android Audio cec架构 android.hardware.audio@2.0_c++_13

同binder server端的getService()不同的是:binder client端的getService()没有参数,所以最终binder client端是通过hwservicemanager拿到了IDeviceFactory对象;

而binder server端是使用openLibs打开对应的-impl.so,得到IDeviceFactory对象。 binder client端拿到IDeviceFactory后就会new出一个BpHwDeviceFactory。

同样我们也先通过一个简单的图来直观理解一下:

Android Audio cec架构 android.hardware.audio@2.0_Android Audio cec架构_14

三个进程 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

Android Audio cec架构 android.hardware.audio@2.0_android_15

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

Android Audio cec架构 android.hardware.audio@2.0_java_16

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

Android Audio cec架构 android.hardware.audio@2.0_c++_17

  

Android Audio cec架构 android.hardware.audio@2.0_java_18

Android Audio cec架构 android.hardware.audio@2.0_java_19

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'...