深入浅出 Android Audio HAL:从加载到函数指针绑定的全链路解析
感兴趣的同学可以先阅读下面的文章在开始今天的学习:
深入解析 Android 音频策略:onNewAudioModulesAvailableInt 的全链路探索
audio_hw_device 核心接口解析
1. 背景:音频"心脏"的启动
在 Android 音频架构中,AudioFlinger 是上层音频控制的核心,但它并不直接触碰底层硬件。它通过加载插件(HAL 库)的方式,将任务委派给厂商实现的驱动。
本文将追踪一个核心流程:当 AudioFlinger 启动时,它是如何一步步调用到厂商定义的 adev_open 接口,并最终拿到硬件控制权(audio_hw_device_t)的?
2. 详解:跨进程的加载链路
2.1 启动入口 (DevicesFactory)
流程始于 audiohal 进程中的 openPrimaryDevice。这是一个 HIDL 接口,用于请求打开主音频设备。
cpp
// 路径:android/hardware/interfaces/audio/core/all-versions/default/DevicesFactory.cpp
Return<void> DevicesFactory::openPrimaryDevice(openPrimaryDevice_cb _hidl_cb) {
return openDevice<PrimaryDevice>(AUDIO_HARDWARE_MODULE_ID_PRIMARY, _hidl_cb);
}
紧接着触发 openDevice 模板函数。这里有两个重点:加载库 和封装对象。
cpp
template <class DeviceShim, class Callback>
Return<void> DevicesFactory::openDevice(const char* moduleName, Callback _hidl_cb) {
audio_hw_device_t* halDevice; // 重点关注 1: 待填充的 C 风格硬件接口结构体
Result retval(Result::INVALID_ARGUMENTS);
sp<DeviceShim> result;
// 重点关注 2: 触发 loadAudioInterface,真正去加载厂商的 .so 库
int halStatus = loadAudioInterface(moduleName, &halDevice);
if (halStatus == OK) {
// 重点关注 3: 将拿到的 halDevice 封装进 C++ Shim 类 (即 PrimaryDevice)
result = new DeviceShim(halDevice);
android::hardware::setMinSchedulerPolicy(result, SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
retval = Result::OK;
} else if (halStatus == -EINVAL) {
retval = Result::NOT_INITIALIZED;
}
_hidl_cb(retval, result); // 回调回 AudioFlinger
return Void();
}
2.2 寻找厂商接口 (loadAudioInterface)
cpp
int DevicesFactory::loadAudioInterface(const char* if_name, audio_hw_device_t** dev) {
const hw_module_t* mod;
int rc;
// 1. 根据 "audio" 和 "primary" 找到对应的 so 库并 dlopen
rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod);
// 2. 直接触发 Vendor HAL 中定义的 adev_open 函数调用
rc = audio_hw_device_open(mod, dev);
return (rc == 0) ? OK : rc;
}
3. 厂商实现:AudioDevice 接口绑定
在我们的项目中,hal-pal/AudioDevice.cpp 是硬件能力的真正实现者。
3.1 模块入口定义
cpp
// 厂商定义的函数指针映射
static struct hw_module_methods_t hal_module_methods = {
.open = adev_open,
};
// HAL 标准符号,系统加载 so 时会寻找这个结构体
struct audio_module HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = AUDIO_MODULE_API_VERSION_0_1,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = AUDIO_HARDWARE_MODULE_ID,
.name = "QTI Audio HAL",
.author = "The Linux Foundation",
.methods = &hal_module_methods,
},
};
3.2 adev_open 的单例化处理
cpp
static int adev_open(const hw_module_t *module, const char *name __unused,
hw_device_t **device) {
int32_t ret = 0;
AHAL_DBG("Enter");
// 获取 AudioDevice 的 C++ 单例对象
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
// 调用 Init 函数,完成真正的硬件初始化和接口挂钩
ret = adevice->Init(device, module);
return ret; // 纠错:必须返回 ret,以反馈 Init 过程中的 PAL 初始化状态
}
3.3 核心"挂钩"过程 (Init 函数)
这是 AHAL 开发最核心的代码。它将 Android 框架期望的 C 函数指针,指向我们实现的 C++ 包装函数。
cpp
int AudioDevice::Init(hw_device_t **device, const hw_module_t *module) {
// 1. 初始化 PAL 层(Qualcomm 架构核心)
pal_init();
// 2. 填充版本信息
adev_->device_.get()->common.tag = HARDWARE_DEVICE_TAG;
adev_->device_.get()->common.version =
#ifdef ANDROID_U_HAL7
HARDWARE_DEVICE_API_VERSION(3, 2); // Android U 支持
#else
HARDWARE_DEVICE_API_VERSION(3, 0);
#endif
// 3. 关键:绑定各种功能函数指针
adev_->device_.get()->common.close = adev_close;
adev_->device_.get()->init_check = adev_init_check;
adev_->device_.get()->set_voice_volume = adev_set_voice_volume;
adev_->device_.get()->set_mode = adev_set_mode;
adev_->device_.get()->set_mic_mute = adev_set_mic_mute;
adev_->device_.get()->set_parameters = adev_set_parameters;
adev_->device_.get()->get_parameters = adev_get_parameters;
adev_->device_.get()->open_output_stream = adev_open_output_stream;
adev_->device_.get()->close_output_stream = adev_close_output_stream;
adev_->device_.get()->open_input_stream = adev_open_input_stream;
adev_->device_.get()->close_input_stream = adev_close_input_stream;
// 4. 将绑定好的结构体地址返回给调用者
*device = &(adev_->device_.get()->common);
return 0;
}
4. 全链路时序图
通过时序图,我们可以清晰地看到调用是如何跨越进程并最终回传的:
PAL (Qualcomm Layer) AudioDevice (AHAL.so) DevicesFactory (audiohal) AudioFlinger (audioserver) PAL (Qualcomm Layer) AudioDevice (AHAL.so) DevicesFactory (audiohal) AudioFlinger (audioserver) 1. 发起加载请求 2. 厂商实例化 3. 封装与回调 AudioFlinger 完成结果接收与保存 openPrimaryDevice() openDevice<PrimaryDevice>() adev_open() (C 接口) GetInstance() (Singleton) pal_init() Init() (绑定函数指针) 返回 audio_hw_device_t 指针 new PrimaryDevice(halDevice) 回调 _hidl_cb(result)
5. 深度追踪:回调后的数据归宿
在 audiohal 进程执行 _hidl_cb(retval, result) 后,这个 result(实际上是一个指向 HAL 进程 PrimaryDevice 的 Binder 代理对象)是如何在 AudioFlinger 中安家的?
5.1 第一站:DevicesFactoryHalHidl 的 Lambda 回调
在 AudioFlinger 进程中,它通过一个 Lambda 表达式接收这个 result:
cpp
// 路径:frameworks/av/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp
ret = factory->openPrimaryDevice(
[&](Result r, const sp<IPrimaryDevice>& result) {
retval = r;
if (retval == Result::OK) {
// 重点:将 HIDL 代理对象封装进 DeviceHalHidl
*device = new DeviceHalHidl(result);
}
});
5.2 第二站:AudioFlinger 的 loadHwModule_l
在 AudioFlinger.cpp 中,这个 device(类型为 DeviceHalInterface)被进一步封装并存储:
cpp
audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name) {
sp<DeviceHalInterface> dev;
// 这里的 dev 就是上面 new 出来的 DeviceHalHidl
int rc = mDevicesFactoryHal->openDevice(name, &dev);
// 将 DeviceHalHidl 存入 AudioHwDevice 包装类中
AudioHwDevice *audioDevice = new AudioHwDevice(handle, name, dev, flags);
if (strcmp(name, AUDIO_HARDWARE_MODULE_ID_PRIMARY) == 0) {
// 如果是主设备,保存在 mPrimaryHardwareDev 成员变量中
mPrimaryHardwareDev = audioDevice;
}
// 同时加入到全局 Vector 列表中,供后续根据 module handle 查找
mAudioHwDevs.add(handle, audioDevice);
return handle;
}
6. 实战演练:调用 openOutputStream 的全过程
当系统需要播放声音,调用 openOutputStream 时,它是如何利用上面保存的指针的?
6.1 步骤一:在 AudioFlinger 中查找设备
cpp
sp<AudioFlinger::ThreadBase> AudioFlinger::openOutput_l(audio_module_handle_t module, ...) {
// 根据加载时分配的 module 句柄,从 mAudioHwDevs 列表中找回 AudioHwDevice
AudioHwDevice *outHwDev = findSuitableHwDev_l(module, deviceType);
// 发起打开输出流的请求
status_t status = outHwDev->openOutputStream(&outputStream, ...);
}
6.2 步骤二:包装类的层层转发
AudioHwDevice 会调用其内部持有的 DeviceHalHidl:
cpp
// AudioStreamOut.cpp 中的调用
int status = hwDev()->openOutputStream(..., &outStream);
// hwDev() 返回的就是之前保存的 DeviceHalHidl 实例
sp<DeviceHalInterface> AudioStreamOut::hwDev() const {
return audioHwDev->hwDevice(); // 即 mHwDevice
}
6.3 步骤三:跨越 Binder 触达 Vendor HAL
DeviceHalHidl::openOutputStream 会调用它内部持有的 HIDL result 指针。由于这是一个 Binder 代理对象,它会发起一次跨进程调用,回到 audiohal 进程中的 PrimaryDevice 对象。
最终,PrimaryDevice 通过它持有的 audio_hw_device_t* 指针,调用到我们在 Init 函数中挂载的 adev_open_output_stream 函数。
7. 总结与职业成长
7.1 核心总结
- C 与 C++ 的桥梁 :Android HAL 通过
audio_hw_device_t结构体提供了一套 C 风格的 API 合同,厂商通过在 C++ 类(AudioDevice)中填充这些函数指针来实现功能。 - 对象生命周期 :回传的
result经历了从 HIDL Proxy 到DeviceHalHidl再到AudioHwDevice的层层封装,最终驻留在AudioFlinger的管理列表中。 - 解耦设计 :通过
DeviceHalInterface抽象接口,AudioFlinger不需要关心底层是 HIDL 还是 AIDL 实现。
7.2 工作中的帮助
阅读并掌握本文所述流程,在实际工作中具有以下巨大价值:
- 链路追踪能力 :当音频无法打开时,你可以从
AudioFlinger的mAudioHwDevs列表顺藤摸瓜,确认对应的接口指针是否已正确加载。 - 性能优化:理解跨进程回调的同步机制,有助于分析音频模块加载时的系统阻塞问题。
- 支持新特性:当你需要在 Framework 层添加新的控制逻辑时,你清楚地知道该如何修改包装类链条以触达底层硬件。