前言:
Android P 引入了一种新的音频政策配置文件格式 (XML),用于描述音频拓扑。新的 XML 文件支持定义输出输入流配置文件、可用于播放和捕获的设备以及音频属性的数量和类型。但是每个项目的代码里面都有很多音频配置文件,具体使用的是哪个文件呢?本文将详细讲述。
1. 查看运行时具体使用的xml
adb shell dumpsys media.audio_policy | grep -iE "Config source:"
结果如下:
Config source: /vendor/etc/audio/demo_xxx/audio_policy_configuration.xml
上面结果就是这台机器在运行时使用的配置文件为: /vendor/etc/audio/demo_xxx/audio_policy_configuration.xml
2. audiopolicy里面具体使用哪个xml是怎么决定的?
2.1 代码xml和本地xml的对应
在 vendor/qcom/opensource/audio-hal/primary-hal/configs/目录下,每个平台都有自己的文件夹,里面存放这个平台的所有项目的配置文件。还有一个commom文件夹,存放基础的配置。common目录下的配置内容比较少,就是一些公用的配置,每个平台自己的配置都在平台对应的文件夹下。
每个平台都有自己的makefile,具体路径为: eg(8250): vendor/qcom/opensource/audio-hal/primary-hal/configs/kona/kona.mk
在makefile中会指定代码xml和本地xml的对应关系:
ifneq ($(TARGET_USES_AOSP_FOR_AUDIO), true)
PRODUCT_COPY_FILES += \
$(TOPDIR)vendor/qcom/opensource/audio-hal/primary-hal/configs/kona/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio/audio_policy_configuration.xml
endif
PRODUCT_COPY_FILES += \
$(TOPDIR)vendor/qcom/opensource/audio-hal/primary-hal/configs/common/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml \
也就是说,如果TARGET_USES_AOSP_FOR_AUDIO是false, 就会把平台对应的xml放在本地的vendor/etc/audio目录下,把common的xml放在本地的vendor/etc目录下。
如果TARGET_USES_AOSP_FOR_AUDIO是true,就只把common的xml放在本地的vendor/etc目录下。
2.2 本地vendor/etc/audio和vendor/etc下都有对应的配置文件,具体运行的时候用哪个是谁决定?
上代码:
在AudioPolicyManager创建的时候(audiopolicyserver启动的时候,开机的时候就会启动)会调用loadConfig。
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
: AudioPolicyManager(clientInterface, false /*forTesting*/)
{
loadConfig(); // //加载xml配置文件 ,加载到mConfig中
}
void AudioPolicyManager::loadConfig() {
if (deserializeAudioPolicyXmlConfig(getConfig()) != NO_ERROR) {
ALOGE("could not load audio policy configuration file, setting defaults");
getConfig().setDefault();
}
}
#define AUDIO_POLICY_XML_CONFIG_FILE_NAME "audio_policy_configuration.xml"
static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) {
std::vector<const char*> fileNames;
fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME); // 把audio_policy_configuration.xml push进去
// 这里会根据property判断是否给fileNames里面push一些文件
for (const char* fileName : fileNames) { // 循环每个文件
for (const auto& path : audio_get_configuration_paths()) { // 循环每个路径!这里的路径!
snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile),
"%s/%s", path.c_str(), fileName);
ret = deserializeAudioPolicyFile(audioPolicyXmlConfigFile, &config); // 判断这个路径是否可用
if (ret == NO_ERROR) {
config.setSource(audioPolicyXmlConfigFile); // 把路径设置给config,dumpsys的时候打印的就是这个路径
return ret;
}
}
}
return ret;
}
然后看看上面的路径都有什么:
static inline std::vector<std::string> audio_get_configuration_paths() {
static const std::vector<std::string> paths = []() {
char value[PROPERTY_VALUE_MAX] = {};
char va_aosp[PROPERTY_VALUE_MAX] = {};
if (property_get("ro.boot.product.vendor.sku", value, "") <= 0) {
return std::vector<std::string>({"/odm/etc", "/vendor/etc/audio",
"/vendor/etc", "/system/etc"});
} else {
property_get("ro.vendor.qti.va_aosp.support", va_aosp, "");
return std::vector<std::string>({
"/odm/etc",
std::string("/vendor/etc/audio/sku_") + value +
((va_aosp[0] == '1') ? "_qssi" : ""),
std::string("/vendor/etc/audio/sku_") + value,
"/vendor/etc/audio", "/vendor/etc", "/system/etc"});
}
}();
return paths;
}
如上其实就是根据"ro.boot.product.vendor.sku"判断,如果prop有设置,具体路径就是:
"/odm/etc",
"/vendor/etc/audio/sku_" + value + ((va_aosp[0] == '1') ? "_qssi" : "")
"/vendor/etc/audio/sku_" + value
"/vendor/etc/audio"
"/vendor/etc"
"/system/etc"
如果prop没有设置(一般都是没有设置),具体路径如下:
"/odm/etc"
"/vendor/etc/audio"
"/vendor/etc"
"/system/etc"
然后deserializeAudioPolicyXmlConfig的for循环会解析每个路径的配置文件,一旦找到一个可用路径就会直接返回。
具体解析过程:
status_t deserializeAudioPolicyFile(const char *fileName, AudioPolicyConfig *config)
{
PolicySerializer serializer;
return serializer.deserialize(fileName, config); // 进行xml解析
}
status_t PolicySerializer::deserialize(const char *configFile, AudioPolicyConfig *config)
{
auto doc = make_xmlUnique(xmlParseFile(configFile));
if (doc == nullptr) {
ALOGE("%s: Could not parse %s document.", __func__, configFile);
return BAD_VALUE;
}
xmlNodePtr root = xmlDocGetRootElement(doc.get());
if (root == NULL) {
ALOGE("%s: Could not parse %s document: empty.", __func__, configFile);
return BAD_VALUE;
}
if (xmlXIncludeProcess(doc.get()) < 0) {
ALOGE("%s: libxml failed to resolve XIncludes on %s document.", __func__, configFile);
}
if (xmlStrcmp(root->name, reinterpret_cast<const xmlChar*>(rootName))) {
ALOGE("%s: No %s root element found in xml data %s.", __func__, rootName,
reinterpret_cast<const char*>(root->name));
return BAD_VALUE;
}
std::string version = getXmlAttribute(root, versionAttribute);
if (version.empty()) {
ALOGE("%s: No version found in root node %s", __func__, rootName);
return BAD_VALUE;
}
if (version != mVersion) {
ALOGE("%s: Version does not match; expect %s got %s", __func__, mVersion.c_str(),
version.c_str());
return BAD_VALUE;
}
// Lets deserialize children
// Modules // 开始解析Modules子节点
ModuleTraits::Collection modules;
status_t status = deserializeCollection<ModuleTraits>(root, &modules, config); // 这里是解析xml的关键,比较复杂,本问先忽略。具体内容
// 大概就是解析输出流和输入流的列表、输入和输出设备、设备描述列表、输入和输出设备之间或音频流和设备之间可能存在的连接的列表等等。
if (status != NO_ERROR) {
return status;
}
config->setHwModules(modules); // 把解析出来的内容放到config里面。
// Global Configuration
GlobalConfigTraits::deserialize(root, config);
// Surround configuration
SurroundSoundTraits::deserialize(root, config);
return android::OK;
}
总结:
回顾下本文,重点是就是三个:
1. 怎么看设备运行使用的配置文件: adb shell dumpsys media.audio_policy | grep -iE "Config source:"
2. 代码和设备本地的配置文件对应关系是谁决定的: vendor/qcom/opensource/audio-hal/primary-hal/configs/平台名/平台名.mk
3. 运行时使用哪个本地配置文件是怎么决定的: 看代码AudioPolicyManager::loadConfig()