2. 音频硬件抽象层(HAL)基础

音频HAL层是Android音频系统的核心桥梁,它位于Android Framework与底层音频硬件驱动之间。本章将深入剖析HAL层的接口定义、模块加载机制以及核心数据结构,为后续的驱动适配实战打下坚实基础。

2.1 HAL接口定义与模块加载机制

Android HAL采用模块化设计,每个硬件抽象层模块通过标准接口进行加载和卸载。音频HAL模块遵循这一通用规范,同时定义了音频特有的接口。

2.1.1 HAL模块结构体

每个HAL模块必须实现hw_module_t结构体,音频HAL在此基础上扩展为audio_module_t。模块加载时,系统通过hw_get_module()函数查找并加载对应的共享库。

关键点:HAL模块的加载路径通常为/vendor/lib/hw/或/system/lib/hw/,库命名规则为<MODULE_ID>.<variant>.so,例如audio.primary.tiny.so。
// hardware/libhardware/include/hardware/hardware.h
typedef struct hw_module_t {
    uint32_t tag;               // 必须为 HARDWARE_MODULE_TAG
    uint16_t module_api_version;
    uint16_t hal_api_version;
    const char *id;             // 模块ID,如 AUDIO_HARDWARE_MODULE_ID
    const char *name;
    const char *author;
    struct hw_module_methods_t* methods; // 模块方法表
    void* dso;                  // 动态库句柄
    uint32_t reserved[32];
} hw_module_t;

// audio HAL 扩展
typedef struct audio_module {
    struct hw_module_t common;  // 通用模块结构体
} audio_module_t;

2.1.2 模块加载流程

音频HAL模块的加载遵循以下步骤,整个过程由AudioFlinger在系统启动时触发:

  1. 调用hw_get_module():传入模块ID(AUDIO_HARDWARE_MODULE_ID)和指向hw_module_t指针的地址。
  2. 查找共享库:系统按优先级顺序扫描/vendor/lib/hw/、/system/lib/hw/等路径。
  3. 动态加载:使用dlopen()加载共享库,并通过dlsym()获取HAL_MODULE_INFO_SYM符号。
  4. 验证模块:检查tag字段和module_api_version。
  5. 调用open()方法:通过hw_module_methods_t::open创建hw_device_t实例。
实战提示:在调试HAL加载问题时,可以使用adb shell lsof | grep audio查看已加载的音频HAL库,或通过adb shell dumpsys media.audio_flinger检查HAL状态。

2.1.3 模块加载流程图

下图展示了音频HAL模块从系统调用到硬件驱动的完整加载链路:

音频HAL模块加载流程图 AudioFlinger hw_get_module() 扫描 /vendor/lib/hw/ dlopen + dlsym open() 创建设备 硬件驱动层 系统启动时自动触发加载

2.2 audio_hw_device 结构体解析

audio_hw_device是音频HAL层的核心设备结构体,它封装了所有音频设备操作接口。每个音频HAL实现必须创建一个该结构体的实例,并填充其函数指针。

2.2.1 结构体定义

// hardware/libhardware/include/hardware/audio.h
struct audio_hw_device {
    struct hw_device_t common;  // 继承自通用设备结构体

    // 模块描述信息
    uint32_t (*get_supported_devices)(const struct audio_hw_device *dev);

    // 初始化与配置
    int (*init_check)(const struct audio_hw_device *dev);
    int (*set_voice_volume)(struct audio_hw_device *dev, float volume);
    int (*set_master_volume)(struct audio_hw_device *dev, float volume);
    int (*get_master_volume)(struct audio_hw_device *dev, float *volume);
    int (*set_master_mute)(struct audio_hw_device *dev, bool mute);
    int (*get_master_mute)(struct audio_hw_device *dev, bool *mute);

    // 输入输出流管理
    int (*open_input_stream)(struct audio_hw_device *dev,
                             audio_io_handle_t handle,
                             audio_devices_t devices,
                             struct audio_config *config,
                             struct audio_stream_in **stream_in,
                             audio_input_flags_t flags,
                             const char *address,
                             audio_source_t source);
    int (*open_output_stream)(struct audio_hw_device *dev,
                              audio_io_handle_t handle,
                              audio_devices_t devices,
                              struct audio_config *config,
                              struct audio_stream_out **stream_out,
                              audio_output_flags_t flags,
                              const char *address,
                              audio_source_t source);
    int (*close_input_stream)(struct audio_hw_device *dev,
                              struct audio_stream_in *stream_in);
    int (*close_output_stream)(struct audio_hw_device *dev,
                               struct audio_stream_out *stream_out);

    // 路由与场景管理
    int (*set_parameters)(struct audio_hw_device *dev, const char *kv_pairs);
    char *(*get_parameters)(const struct audio_hw_device *dev,
                            const char *keys);
    int (*init)(const struct audio_hw_device *dev);
    int (*set_audio_port_config)(const struct audio_hw_device *dev,
                                 const struct audio_port_config *config);
    // ... 其他接口
};

2.2.2 关键接口说明

接口函数 功能描述 适配注意事项
init_check() 检查硬件是否初始化成功 返回0表示成功,负值表示失败
open_output_stream() 创建音频输出流 需根据audio_output_flags_t配置不同的输出路径
open_input_stream() 创建音频输入流 需处理audio_source_t(如MIC、CAMCORDER)
set_parameters() 设置设备参数(键值对形式) 常见参数:routing、bt_sco、screen_state
get_parameters() 获取设备参数 返回以分号分隔的键值对字符串
常见陷阱:在实现open_output_stream()时,必须正确处理audio_config中的采样率、通道数和格式。如果硬件不支持请求的配置,应返回-ENOSYS或尝试最接近的匹配配置,而不是直接返回成功但使用错误参数。

2.3 audio_stream_in 与 audio_stream_out 结构体解析

音频流结构体是实际进行音频数据读写的接口。输出流负责将音频数据写入硬件,输入流负责从硬件读取音频数据。

2.3.1 audio_stream_out 结构体

struct audio_stream_out {
    struct audio_stream common;  // 继承自通用流结构体

    // 写入音频数据(阻塞)
    ssize_t (*write)(struct audio_stream_out *stream, const void* buffer,
                     size_t bytes);

    // 获取渲染位置
    int (*get_render_position)(const struct audio_stream_out *stream,
                               uint32_t *dsp_frames);

    // 获取延迟时间(微秒)
    uint32_t (*get_latency)(const struct audio_stream_out *stream);

    // 设置音量
    int (*set_volume)(struct audio_stream_out *stream, float left,
                      float right);

    // 暂停/恢复
    int (*pause)(struct audio_stream_out *stream);
    int (*resume)(struct audio_stream_out *stream);

    // 获取缓冲区大小
    size_t (*get_buffer_size)(const struct audio_stream_out *stream);

    // 获取帧计数
    int (*get_frame_count)(const struct audio_stream_out *stream);

    // 设置回调(用于低延迟场景)
    int (*set_callback)(struct audio_stream_out *stream,
                        stream_callback_t callback, void *cookie);
};

2.3.2 audio_stream_in 结构体

struct audio_stream_in {
    struct audio_stream common;  // 继承自通用流结构体

    // 读取音频数据(阻塞)
    ssize_t (*read)(struct audio_stream_in *stream, void* buffer,
                    size_t bytes);

    // 设置增益
    int (*set_gain)(struct audio_stream_in *stream, float gain);

    // 获取输入缓冲大小
    size_t (*get_buffer_size)(const struct audio_stream_in *stream);

    // 获取帧计数
    int (*get_frame_count)(const struct audio_stream_in *stream);

    // 获取捕获位置
    int (*get_capture_position)(const struct audio_stream_in *stream,
                                int64_t *frames, int64_t *time);

    // 启动/停止输入
    int (*start)(struct audio_stream_in *stream);
    int (*stop)(struct audio_stream_in *stream);

    // 获取输入帧大小(字节)
    size_t (*get_input_frames_lost)(struct audio_stream_in *stream);
};

2.3.3 通用流结构体

输入输出流都继承自audio_stream,该结构体定义了流的通用属性和操作:

struct audio_stream {
    // 流的句柄
    audio_hw_device_t *device;

    // 流参数
    audio_format_t format;      // 音频格式(如PCM_16_BIT)
    uint32_t sample_rate;       // 采样率(如44100)
    audio_channel_mask_t channel_mask; // 通道掩码

    // 流操作
    uint32_t (*get_sample_rate)(const struct audio_stream *stream);
    int (*set_sample_rate)(struct audio_stream *stream, uint32_t rate);
    size_t (*get_buffer_size)(const struct audio_stream *stream);
    audio_format_t (*get_format)(const struct audio_stream *stream);
    int (*set_format)(struct audio_stream *stream, audio_format_t format);
    audio_channel_mask_t (*get_channels)(const struct audio_stream *stream);
    audio_channel_mask_t (*set_channels)(struct audio_stream *stream,
                                          audio_channel_mask_t mask);
    int (*standby)(struct audio_stream *stream);
    int (*dump)(int fd, const struct audio_stream *stream);
    int (*set_parameters)(struct audio_stream *stream, const char *kv_pairs);
    char *(*get_parameters)(const struct audio_stream *stream,
                            const char *keys);
    int (*add_audio_effect)(const struct audio_stream *stream,
                            effect_handle_t effect);
    int (*remove_audio_effect)(const struct audio_stream *stream,
                               effect_handle_t effect);
};

2.4 音频流生命周期与数据流向

下图展示了音频输出流从创建到销毁的完整生命周期,以及音频数据在HAL层的流动路径:

音频输出流生命周期与数据流向 1. open_output_stream 2. 配置参数 3. write() 循环 4. standby() 5. close_output_stream 音频数据流向 AudioFlinger → HAL write() → ALSA驱动 → DMA → DAC → 扬声器 |-- 用户空间 --| |-- 内核空间 --| |-- 硬件 --| write() 为阻塞调用

2.5 核心接口对比总结

对比维度 audio_hw_device audio_stream_out audio_stream_in
职责 设备级管理(初始化、路由、参数) 音频输出(播放) 音频输入(录音)
核心函数 open_*_stream、set_parameters write()、get_latency() read()、set_gain()
生命周期 进程启动时创建,进程退出时销毁 每次播放会话创建/销毁 每次录音会话创建/销毁
线程安全 需要实现内部锁机制 通常由AudioFlinger保证单线程访问 通常由AudioFlinger保证单线程访问
调试关键 检查init_check()返回值 检查write()返回字节数是否匹配 检查read()返回数据是否有效
适配经验:在实现音频HAL时,建议先实现open_output_stream()和write(),因为播放功能是验证HAL是否正常工作的最快路径。使用tinyplay工具可以快速测试输出流,使用tinycap测试输入流。

2.6 本章小结

本章深入剖析了音频HAL层的三大核心要素:

  • 模块加载机制:从hw_get_module()到dlopen()的完整链路,以及模块库的命名和路径规范。
  • 设备结构体:audio_hw_device作为设备级接口,负责流管理、参数设置和路由控制。
  • 流结构体:audio_stream_out和audio_stream_in分别封装了播放和录音的核心操作。