跳到主要内容
高通音频架构:从 AHAL adev_open 到 PAL XML 解析的调用链追踪 | 极客日志
C++
高通音频架构:从 AHAL adev_open 到 PAL XML 解析的调用链追踪 Android 车载音频系统启动时,AHAL 层调用 adev_open 后触发 PAL 层 XML 解析的调用链路。通过源码追踪发现,30 微秒内完成了从 HAL 接口到 ResourceManager 单例初始化及 card-defs.xml 配置加载的过程。重点解析了 card-defs.xml 中 bus_name 与 PCM ID 的映射逻辑,阐述了高通音频架构如何通过 XML 定义硬件拓扑以实现芯片适配灵活性。
从 AHAL adev_open 到 PAL XML 解析:30 微秒内的调用链追踪
1. 问题的引入
在分析 Android 音频系统启动日志时,我们经常会看到两条紧挨着的日志,时间间隔极其微小(本例中仅为 30 微秒):
02-11 10:35:14.248370 698 698 I AHAL: AudioDevice: adev_open 2650 ahal_log_lvl 0xf
02-11 10:35:14.248400 698 698 I PAL: ResourceManager: XmlParser: 10897: XML parsing started - file name /vendor/etc/card-defs.xml
02-11 10:35:14.251292 698 698 I PAL: ResourceManager: processDeviceIdProp: 9964: processDeviceIdProp find bus card BUS00_MEDIA bind to 124
02-11 10:35:14.251347 698 698 I PAL: ResourceManager: processDeviceIdProp: 9964: processDeviceIdProp find bus card BUS01_SYS_NOTIFICATION bind to 125
02-11 10:35:14.251371 698 698 I PAL: ResourceManager: processDeviceIdProp: 9964: processDeviceIdProp find bus card BUS02_NAV_GUIDANCE bind to 126
AHAL (Audio HAL) 刚说要 open,PAL (Platform Audio Layer) 紧接着就开始解析 XML 配置文件了。这中间到底发生了什么?是谁触发了 PAL 的加载?本文将通过源码深度揭秘。
2. 核心调用链路总览 ResourceManager (PAL Core) -> Pal.cpp (PAL API) -> AudioDevice (AHAL) -> AudioFlinger
打印日志:adev_open 2650...
触发单例构造函数
打印日志:XML parsing started...
adev_open() (通过 hw_module->methods->open)
adevice->Init()
pal_init()
ResourceManager::getInstance()
ResourceManager::ResourceManager()
ResourceManager::XmlParser("/vendor/etc/card-defs.xml")
3. 源码级解析
第一阶段:AHAL 的入口 adev_open 当 Android 的 AudioFlinger 通过 audio hal 加载厂商的 audio.primary.xxx.so 时,会通过 HAL 框架调用 .open 接口。在高通的实现中,这个函数位于 AudioDevice.cpp。
文件路径 :vendor/qcom/opensource/audio-hal-ar/primary-hal/hal-pal/AudioDevice.cpp
static int adev_open (const hw_module_t *module ,
const char * name __unused,
hw_device_t **device) {
property_get ("vendor.audio.hal.log_type" , ahal_log_type, "" );
ALOGI ("%s %d ahal_log_lvl 0x%x" , __func__, __LINE__, ahal_log_lvl);
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance ();
ret = adevice->Init (device, module );
}
第二阶段:从 AHAL 跨越到 PAL 在 AudioDevice::Init 函数中,高通 AHAL 会初始化其核心引擎——PAL (Platform Audio Layer) 。
文件路径 :vendor/qcom/opensource/audio-hal-ar/primary-hal/hal-pal/AudioDevice.cpp
int AudioDevice::Init (hw_device_t **device, const hw_module_t *module ) {
int ret = 0 ;
ret = pal_init ();
if (ret) {
AHAL_ERR ("pal_init failed ret=(%d)" , ret);
return -EINVAL;
}
}
第三阶段:PAL 内部单例触发 pal_init() 是 PAL 库暴露给 AHAL 的标准 API。它的职责是拉起 PAL 内部的资源管理器 ResourceManager。
文件路径 :vendor/qcom/opensource/pal/Pal.cpp
int32_t pal_init (void ) {
int32_t ret = 0 ;
std::shared_ptr<ResourceManager> ri = NULL ;
try {
ri = ResourceManager::getInstance ();
} catch (const std::exception& e) {
PAL_ERR (LOG_TAG, "pal init failed: %s" , e.what ());
return -EINVAL;
}
}
第四阶段:ResourceManager 构造与 XML 解析 由于 ResourceManager 是单例模式,getInstance() 会执行 new ResourceManager()。在这个构造函数中,系统会根据配置文件初始化所有的音频路由、设备和策略。
文件路径 :vendor/qcom/opensource/pal/resource_manager/src/ResourceManager.cpp
ResourceManager::ResourceManager () {
int ret = 0 ;
ret = ResourceManager::XmlParser (SNDPARSER);
ret = ResourceManager::XmlParser (rmngr_xml_file);
if (ret) {
PAL_ERR (LOG_TAG, "error in resource xml parsing ret %d" , ret);
throw std::runtime_error ("error in resource xml parsing" );
}
}
int ResourceManager::XmlParser (std::string xmlFile) {
PAL_INFO (LOG_TAG, "XML parsing started - file name %s" , xmlFile.c_str ());
}
4. 深度解析 card-defs.xml:PAL 的硬件资源底座 在上面的 Log 2 中,我们看到了 card-defs.xml 被解析。这个文件到底起到了什么作用?
4.1 XML 标签与源码 C++ 映射关系 当 ResourceManager::XmlParser 被调用后,它会利用 libxml2 遍历文件。核心的解析逻辑位于 ResourceManager::processDeviceIdProp 等函数中。
XML 标签 源码解析函数 对应的 C++ 数据结构 / 逻辑 <card>startTagHandler开启一个新的声卡定义(虚拟声卡概念)。 <pcm-device>processDeviceIdProp创建一个新的 deviceCap 结构体,存入 devInfo 容器。 <id>processDeviceIdProp存储为 deviceId(如 124),对应底层的 PCM 端口 ID。 <bus_name>processDeviceIdProp关键映射点 :将逻辑 Bus(如 BUS00_MEDIA)与物理 ID 绑定。<props>processDeviceCapability解析播放(playback)、录音(capture)及会话模式(session_mode)。
4.2 标签背后的源码逻辑实现
void ResourceManager::processDeviceIdProp (struct xml_userdata* data, const XML_Char *tag_name) {
} else if (!strcmp (tag_name, "bus_name" )) {
strlcpy (devInfo[size].ext_name, data->data_buf, strlen (data->data_buf)+1 );
devInfo[size].device_flag = BUS_NAME_FLAG;
PAL_INFO (LOG_TAG, "find bus card %s bind to %d" , data->data_buf, devInfo[size].deviceId);
}
4.3 card-defs.xml 的核心作用:桥梁与清单
作为'硬件配置清单' :它告知 PAL 这一代芯片(如 sa8295)到底开放了多少个虚拟 PCM 端口(100 号到 155 号)。
实现'逻辑到物理'的路由映射 :
Android 框架层通过 Audio Bus 寻址(逻辑地址)。
底层驱动通过 PCM ID 传输数据(物理 ID)。
card-defs.xml 里的 <bus_name> 标签正是这两者之间的强绑定关系 。当 AudioPolicy 请求 Media 播放时,系统通过查这张'表',知道该去敲 124 号 PCM 的大门。
定义'会话模式' :通过 session_mode 标签,区分了普通播放通道(DEFAULT)和用于特殊处理的通道(如 HOSTLESS:不需要 CPU 参与的直接通路)。
5. 总结 那 30 微秒的跨度,完成了从 Android 原生 HAL 接口调用 到 厂商私有硬件描述解析 的华丽转身。
AHAL 只负责'迎宾',它遵守 Android 的标准协议。
PAL 才是'内管家',它通过解析 card-defs.xml 来掌控整个声卡的硬件拓扑。
通过这种'XML 定义拓扑,源码执行逻辑'的架构,高通实现了同一套代码支持不同变体芯片(如 sa8155 vs sa8295)的灵活性。
vendor/qcom/opensource/audio-hal-ar/primary-hal/hal-pal/AudioDevice.cpp
vendor/qcom/opensource/pal/Pal.cpp
vendor/qcom/opensource/pal/resource_manager/src/ResourceManager.cpp
相关免费在线工具 Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
JSON美化和格式化 将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online