【车载Audio】【AudioHal 06】【高通音频架构】【深入浅出 Android Audio HAL:从加载到函数指针绑定的全链路解析】

深入浅出 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 工作中的帮助

阅读并掌握本文所述流程,在实际工作中具有以下巨大价值:

  1. 链路追踪能力 :当音频无法打开时,你可以从 AudioFlingermAudioHwDevs 列表顺藤摸瓜,确认对应的接口指针是否已正确加载。
  2. 性能优化:理解跨进程回调的同步机制,有助于分析音频模块加载时的系统阻塞问题。
  3. 支持新特性:当你需要在 Framework 层添加新的控制逻辑时,你清楚地知道该如何修改包装类链条以触达底层硬件。
相关推荐
无巧不成书02181 小时前
Kotlin Multiplatform (KMP) 鸿蒙开发整合实战|2026最新方案
android·开发语言·kotlin·harmonyos·kmp
l1t2 小时前
利用DeepSeek和qwen 3.5辅助生成SQL优化方法幻灯片视频
数据库·sql·音视频
yq1982043011562 小时前
基于Python爬虫原理的Pinterest视频资源获取技术解析与工具实践
爬虫·python·django·音视频
不吃鱼的猫7482 小时前
【ffplay 源码解析系列】01-开篇-ffplay整体架构与启动流程
c++·架构·ffmpeg·音视频
恋猫de小郭11 小时前
丰田正在使用 Flutter 开发游戏引擎 Fluorite
android·前端·flutter
似霰14 小时前
Unix Domain Socket —— UDP 篇
android·unix
哈__15 小时前
基础入门 Flutter for OpenHarmony:video_thumbnail 视频缩略图详解
flutter·音视频
独自破碎E15 小时前
BISHI54货物堆放
android·java·开发语言