1.加载流程
在之前的章节中有提到,在Android系统启动时会启动两个服务,AudioFlinger和AudioPolicyService, 在AudioPolicyService初始化时,调用createAudioPolicyManger创建AudioPolicyManager以及进行初始化操作,总体流程如下:

loadConfig()功能是分析加载音频配置文件audio_policy_config.xml文件,在第十二节有详细的介绍。
apm->initialize中的加载Engine流程在第十三章有详细的介绍,这节我们主要分析onNewAudioModulesAvailableInt中一个重要的流程------加载硬件设备,调用流程如下:

af->loadHwModule调用到audioFlinger服务中的loadHwModule函数,然后再调用到mDevicesFactoryHal->openDevice, loadHwModule最终的目标是openDevice。
具体来说,loadHwModule加载的是实现了audio_hw_device_t结构体的动态共享库(.so文件)。每个硬件模块代表一种音频设备类型,例如:
-
primary:主音频设备(通常对应设备内置的扬声器、听筒、麦克风等)
-
a2dp:蓝牙A2DP设备
-
usb:USB音频设备
-
remote_submix:用于模拟音频输出/输入,常用于Wifi Display或Android模拟器
在解析配置文件(audio_policy_configuration.xml)时,会为每个<module>标签创建一个HwModule对象,并指定其名称(如"primary")和HAL版本。然后,在初始化过程中,APM会调用loadHwModule来加载对应的HAL库。举个例子:假设配置文件中有一个模块名为"primary",那么APM会尝试加载名为"audio.primary.<device>.so"的库,其中<device>是设备名称(由ro.product.board(这个值代表cpu型号)等属性决定)。
例如,在Pixel设备上,可能会加载"audio.primary.sailfish.so"。具体过程如下:
(1) APM在初始化时(loadConfig)解析配置文件,创建HwModule对象。
(2)当调用loadHwModule时,会通过HAL的hw_get_module_by_class()函数利用HAL层注册信息id和name,获取相应的模块函数根据模块名,主要用于id相同、name不同,即获取相同功能但厂家不同的硬件库。如"AUDIO_HARDWARE_MODULE_ID_PRIMARY"即"primary",获取对应的HAL模块。
- 然后调用audio_hw_device_open找到对应的open函数打开该模块,得到audio_hw_device_t结构体,用于后续操作(如打开音频流)。
例如,加载的音频库有:
-
主音频模块:/vendor/lib/hw/audio.primary.sdm660.so(qcom)
-
蓝牙A2DP模块:/vendor/lib/hw/audio.a2dp.default.so
所以,loadHwModule加载的是音频硬件抽象层(HAL)的模块,这些模块以动态库的形式存在,并实现了音频硬件的操作接口(如打开/关闭流、设置参数等)。
2.加载内容
(1) 硬件模块对象
在加载音频配置文件时创建 HwModule
对象,包含:
-
HAL 接口 :
audio_hw_device_t
结构体指针 -
设备描述 :
DeviceDescriptor
集合(扬声器/麦克风等) -
I/O 能力 :
IOProfile
列表(输入/输出端口配置) -
路由策略 :
AudioRoute
拓扑关系
(2)HAL 动态库
实际加载的共享库文件,命名规则:
示例路径
/vendor/lib/hw/audio.primary.${platform}.so
如高通平台
/vendor/lib/hw/audio.primary.msm8998.so
3. HAL层源码示例
cpp
// audio.primary.msm8998.c
static struct hw_module_methods_t hal_module_methods = {
.open = adev_open
};
struct audio_module HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.version_major = 7,
.id = AUDIO_HARDWARE_MODULE_ID,
.name = "QCOM Primary Audio HAL",
.methods = &hal_module_methods,
}
};
static int adev_open(const hw_module_t* module,
const char* name,
hw_device_t** device) {
struct primary_audio_dev *adev;
adev = calloc(1, sizeof(struct primary_audio_dev));
// 填充HAL操作函数
adev->device.common.close = adev_close;
adev->device.init_check = adev_init_check;
adev->device.set_voice_volume = adev_set_voice_volume;
// ...其他音频操作函数
*device = &adev->device.common;
return 0;
}
4. 调试技巧
(1)检查加载状态
adb shell dumpsys media.audio_policy | grep "HW Modules"
输出示例:
HW Modules:
-
Module primary: handle 42
-
Output profiles: primary output
-
Devices: Speaker (AUDIO_DEVICE_OUT_SPEAKER)
(2)查看 HAL 版本
// 在HAL实现中打印版本
ALOGI("HAL version: %d.%d",
module->halVersionMajor,
module->halVersionMinor);
(3)加载失败排查
常见错误日志
loadHwModule() error 0xfffffffe opening HAL "a2dp"
排查步骤:
-
检查
/vendor/lib/hw/audio.a2dp.default.so
是否存在 -
验证 SELinux 权限:
avc: denied
相关日志 -
确认 HAL 符号导出:
nm -D audio.primary.so | grep HAL_MODULE_INFO_SYM
总结:
loadHwModule 是 Android 音频系统的核心初始化函数,负责加载音频硬件抽象层(HAL)的模块实现 。它建立 AudioPolicyService 与底层硬件的桥梁,将配置文件(audio_policy_config.xml)中的 标签<module>定义转化为可操作的硬件对象,加载的是音频硬件模块(HAL实现),每个模块对应一种音频设备类型,以动态库的形式存在,负责与底层音频硬件交互。