基于Android 9.0
1.概述
本章将继续《Android 音频子系统--04:AudioPolicyService启动分析 》中的解析音频配置文件的代码分析。
2.解析准备工作
再次回到AudioPolicyManager的构造方法中,代码如下:
// frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
: AudioPolicyManager(clientInterface, false /*forTesting*/)
{
//关键点1:解析配置文件
loadConfig();
initialize();
}
// frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
void AudioPolicyManager::loadConfig() {
/*
系统会首先加载vendor/etc目录下的configure文件,再加载system/etc目录下的configure文件。
若这两者加载都发生错误的话,系统会加载default配置文件,并命名为primary module,从这可以看
出,音频系统中一定必须存在的module就是primary了。
*/
if (deserializeAudioPolicyXmlConfig(getConfig()) != NO_ERROR) {
ALOGE("could not load audio policy configuration file, setting defaults");
getConfig().setDefault();
}
}
实际会去调用deserializeAudioPolicyXmlConfig()方法进行解析,解析出来的结果保存在AudioPolicyConfig对象中(通过getConfig()获取一个新创建的对象,没有填充数据)。下面是该对象的一些参数:
| 变量 | 说明 |
|---|---|
| HwModuleCollection &mHwModules; | .xml中所有module模块的集合 |
| DeviceVector &mAvailableOutputDevices; | .xml中所有output devices模块的集合 |
| DeviceVector &mAvailableInputDevices; | .xml中所有input devices模块的集合 |
| sp &mDefaultOutputDevices; | .xml中默认output device |
| bool mIsSpeakerDrcEnabled; | .xml中speaker的开关属性 |
如果解析失败会调用getConfig().setDefault(),代码实现如下:
// frameworks/av/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
void setDefault(void)
{
mSource = "AudioPolicyConfig::setDefault";
mDefaultOutputDevice = new DeviceDescriptor(AUDIO_DEVICE_OUT_SPEAKER);
mDefaultOutputDevice->addAudioProfile(AudioProfile::createFullDynamic());
sp<DeviceDescriptor> defaultInputDevice = new DeviceDescriptor(AUDIO_DEVICE_IN_BUILTIN_MIC);
defaultInputDevice->addAudioProfile(AudioProfile::createFullDynamic());
sp<AudioProfile> micProfile = new AudioProfile(
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_MONO, 8000);
defaultInputDevice->addAudioProfile(micProfile);
mAvailableOutputDevices.add(mDefaultOutputDevice);
mAvailableInputDevices.add(defaultInputDevice);
sp<HwModule> module = new HwModule(AUDIO_HARDWARE_MODULE_ID_PRIMARY, 2 /*halVersionMajor*/);
mHwModules.add(module);
mDefaultOutputDevice->attach(module);
defaultInputDevice->attach(module);
sp<OutputProfile> outProfile = new OutputProfile(String8("primary"));
outProfile->addAudioProfile(
new AudioProfile(AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 44100));
outProfile->addSupportedDevice(mDefaultOutputDevice);
outProfile->setFlags(AUDIO_OUTPUT_FLAG_PRIMARY);
module->addOutputProfile(outProfile);
sp<InputProfile> inProfile = new InputProfile(String8("primary"));
inProfile->addAudioProfile(micProfile);
inProfile->addSupportedDevice(defaultInputDevice);
module->addInputProfile(inProfile);
setDefaultSurroundFormats();
}
3.audio_policy_configuration.xml
配置文件里面有2种角色:source和sink,每种角色又分为devicePorts和mixPorts。
-
devicePorts(source): 为实际的硬件输入设备;
-
devicePorts(sink): 为实际的硬件输出设备;
-
mixPorts(source): 为经过AudioFlinger之后的流类型;
-
mixPorts(sink): 为进入AudioFlinger之前的流类型;
-
routes: 定义devicePort和mixPorts的路由策略。
下面看一下具体的配置文件:
// frameworks/av/services/audiopolicy/config/audio_policy_configuration.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
<!-- version section contains a “version” tag in the form “major.minor” e.g version=”1.0” -->
<!-- Global configuration Decalaration -->
<globalConfiguration speaker_drc_enabled="true"/>
<modules>
<!-- Primary Audio HAL -->
<!-- 一个module对应厂家提供一个so文件 -->
<module name="primary" halVersion="3.0">
<attachedDevices>
<item>Speaker</item>
<item>Built-In Mic</item>
<item>Built-In Back Mic</item>
</attachedDevices>
<defaultOutputDevice>Speaker</defaultOutputDevice>
<mixPorts>
<mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
<mixPort name="deep_buffer" role="source"
flags="AUDIO_OUTPUT_FLAG_DEEP_BUFFER">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
<mixPort name="compressed_offload" role="source"
flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|AUDIO_OUTPUT_FLAG_NON_BLOCKING">
<profile name="" format="AUDIO_FORMAT_MP3"
samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_MONO"/>
<profile name="" format="AUDIO_FORMAT_AAC"
samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_MONO"/>
<profile name="" format="AUDIO_FORMAT_AAC_LC"
samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_MONO"/>
</mixPort>
<mixPort name="voice_tx" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_OUT_MONO"/>
</mixPort>
<mixPort name="primary input" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
</mixPort>
<mixPort name="voice_rx" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_IN_MONO"/>
</mixPort>
</mixPorts>
<devicePorts>
<!-- Output devices declaration, i.e. Sink DEVICE PORT -->
<devicePort tagName="Earpiece" type="AUDIO_DEVICE_OUT_EARPIECE" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_MONO"/>
</devicePort>
<devicePort tagName="Speaker" role="sink" type="AUDIO_DEVICE_OUT_SPEAKER" address="">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
<gains>
<gain name="gain_1" mode="AUDIO_GAIN_MODE_JOINT"
minValueMB="-8400"
maxValueMB="4000"
defaultValueMB="0"
stepValueMB="100"/>
</gains>
</devicePort>
<devicePort tagName="Wired Headset" type="AUDIO_DEVICE_OUT_WIRED_HEADSET" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</devicePort>
<devicePort tagName="Wired Headphones" type="AUDIO_DEVICE_OUT_WIRED_HEADPHONE" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</devicePort>
<devicePort tagName="BT SCO" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_OUT_MONO"/>
</devicePort>
<devicePort tagName="BT SCO Headset" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_OUT_MONO"/>
</devicePort>
<devicePort tagName="BT SCO Car Kit" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_OUT_MONO"/>
</devicePort>
<devicePort tagName="Telephony Tx" type="AUDIO_DEVICE_OUT_TELEPHONY_TX" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_OUT_MONO"/>
</devicePort>
<devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
</devicePort>
<devicePort tagName="Built-In Back Mic" type="AUDIO_DEVICE_IN_BACK_MIC" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
</devicePort>
<devicePort tagName="Wired Headset Mic" type="AUDIO_DEVICE_IN_WIRED_HEADSET" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
</devicePort>
<devicePort tagName="BT SCO Headset Mic" type="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_IN_MONO"/>
</devicePort>
<devicePort tagName="Telephony Rx" type="AUDIO_DEVICE_IN_TELEPHONY_RX" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_IN_MONO"/>
</devicePort>
</devicePorts>
<!-- route declaration, i.e. list all available sources for a given sink -->
<routes>
<route type="mix" sink="Earpiece"
sources="primary output,deep_buffer,BT SCO Headset Mic"/>
<route type="mix" sink="Speaker"
sources="primary output,deep_buffer,compressed_offload,BT SCO Headset Mic,Telephony Rx"/>
<route type="mix" sink="Wired Headset"
sources="primary output,deep_buffer,compressed_offload,BT SCO Headset Mic,Telephony Rx"/>
<route type="mix" sink="Wired Headphones"
sources="primary output,deep_buffer,compressed_offload,BT SCO Headset Mic,Telephony Rx"/>
<route type="mix" sink="primary input"
sources="Built-In Mic,Built-In Back Mic,Wired Headset Mic,BT SCO Headset Mic"/>
<route type="mix" sink="Telephony Tx"
sources="Built-In Mic,Built-In Back Mic,Wired Headset Mic,BT SCO Headset Mic, voice_tx"/>
<route type="mix" sink="voice_rx"
sources="Telephony Rx"/>
</routes>
</module>
<!-- A2dp Input Audio HAL -->
<xi:include href="a2dp_in_audio_policy_configuration.xml"/>
<!-- Usb Audio HAL -->
<xi:include href="usb_audio_policy_configuration.xml"/>
<!-- Remote Submix Audio HAL -->
<xi:include href="r_submix_audio_policy_configuration.xml"/>
<!-- Bluetooth Audio HAL -->
<xi:include href="bluetooth_audio_policy_configuration.xml"/>
<!-- MSD Audio HAL (optional) -->
<xi:include href="msd_audio_policy_configuration.xml"/>
</modules>
<!-- End of Modules section -->
<!-- Volume section:
IMPORTANT NOTE: Volume tables have been moved to engine configuration.
Keep it here for legacy.
Engine will fallback on these files if none are provided by engine.
-->
<xi:include href="audio_policy_volumes.xml"/>
<xi:include href="default_volume_tables.xml"/>
<!-- End of Volume section -->
<!-- Surround Sound configuration -->
<xi:include href="surround_sound_configuration_5_0.xml"/>
<!-- End of Surround Sound configuration -->
</audioPolicyConfiguration>
4.解析文件
AudioPolicyManager中调用deserializeAudioPolicyXmlConfig()方法去解析文件,下面分析一下该过程。
// frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
static const char *kConfigLocationList[] =
{"/odm/etc", "/vendor/etc/audio", "/vendor/etc", "/system/etc"};
static const int kConfigLocationListSize =
(sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) {
char audioPolicyXmlConfigFile[AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH];
std::vector<const char*> fileNames;
status_t ret;
if (property_get_bool("ro.bluetooth.a2dp_offload.supported", false)) {
if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.disabled", false) &&
property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) {
// Both BluetoothAudio@2.0 and BluetoothA2dp@1.0 (Offlaod) are disabled, and uses
// the legacy hardware module for A2DP and hearing aid.
fileNames.push_back(AUDIO_POLICY_BLUETOOTH_LEGACY_HAL_XML_CONFIG_FILE_NAME);
} else if (property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) {
// A2DP offload supported but disabled: try to use special XML file
fileNames.push_back(AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME);
}
} else if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.disabled", false)) {
fileNames.push_back(AUDIO_POLICY_BLUETOOTH_LEGACY_HAL_XML_CONFIG_FILE_NAME);
}
fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME);
for (const char* fileName : fileNames) {
for (int i = 0; i < kConfigLocationListSize; i++) {
snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile),
"%s/%s", kConfigLocationList[i], fileName);
ret = deserializeAudioPolicyFile(audioPolicyXmlConfigFile, &config);
if (ret == NO_ERROR) {
config.setSource(audioPolicyXmlConfigFile);
return ret;
}
}
}
return ret;
}
其中kConfigLocationList[]设置的配置文件的路径,有先后顺序。然后调用deserializeAudioPolicyFile()方法,代码如下:
// frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
status_t deserializeAudioPolicyFile(const char *fileName, AudioPolicyConfig *config)
{
PolicySerializer serializer;
return serializer.deserialize(fileName, config);
}
继续看一下PolicySerializer::deserialize()方法,代码实现如下:
// frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
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
ModuleTraits::Collection modules;
// 1.解析modules
status_t status = deserializeCollection<ModuleTraits>(root, &modules, config);
if (status != NO_ERROR) {
return status;
}
config->setHwModules(modules);// 解析出modules对象后,保存在config
// Global Configuration
GlobalConfigTraits::deserialize(root, config);
// Surround configuration
SurroundSoundTraits::deserialize(root, config);
return android::OK;
}
deserializeCollection用于解析的方法,其参数Trait::Collection 是各种不同的解析器,比如ModuleTraits用来解析modules,MixPortTraits用来解析mixPorts等。看下它的具体实现:
// frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
template <class Trait>
static status_t deserializeCollection(_xmlDoc *doc, const _xmlNode *cur,
typename Trait::Collection &collection,
typename Trait::PtrSerializingCtx serializingContext)
{
const xmlNode *root = cur->xmlChildrenNode;
while (root != NULL) {
if (xmlStrcmp(root->name, (const xmlChar *)Trait::collectionTag) &&
xmlStrcmp(root->name, (const xmlChar *)Trait::tag)) {
root = root->next;
continue;
}
const xmlNode *child = root;
if (!xmlStrcmp(child->name, (const xmlChar *)Trait::collectionTag)) {
child = child->xmlChildrenNode;
}
while (child != NULL) {
if (!xmlStrcmp(child->name, (const xmlChar *)Trait::tag)) {
typename Trait::PtrElement element;
// 关键点
status_t status = Trait::deserialize(doc, child, element, serializingContext);
if (status != NO_ERROR) {
return status;
}
if (collection.add(element) < 0) {
ALOGE("%s: could not add element to %s collection", __FUNCTION__,
Trait::collectionTag);
}
}
child = child->next;
}
if (!xmlStrcmp(root->name, (const xmlChar *)Trait::tag)) {
return NO_ERROR;
}
root = root->next;
}
return NO_ERROR;
}
就是具体去调用不同Trait::Collection对象的deserialize()方法。
4.1 ModuleTraits
找到module节点开始解析,使用ModuleTraits::deserialize()方法解析。
status_t ModuleTraits::deserialize(xmlDocPtr doc, const xmlNode *root, PtrElement &module,
PtrSerializingCtx ctx)
{
string name = getXmlAttribute(root, Attributes::name);
if (name.empty()) {
ALOGE("%s: No %s found", __FUNCTION__, Attributes::name);
return BAD_VALUE;
}
uint32_t versionMajor = 0, versionMinor = 0;
string versionLiteral = getXmlAttribute(root, Attributes::version);
if (!versionLiteral.empty()) {
sscanf(versionLiteral.c_str(), "%u.%u", &versionMajor, &versionMinor);
ALOGV("%s: mHalVersion = major %u minor %u", __FUNCTION__,
versionMajor, versionMajor);
}
ALOGV("%s: %s %s=%s", __FUNCTION__, tag, Attributes::name, name.c_str());
module = new Element(name.c_str(), versionMajor, versionMinor);
// Deserialize childrens: Audio Mix Port, Audio Device Ports (Source/Sink), Audio Routes
MixPortTraits::Collection mixPorts;
// 解析MixPort
deserializeCollection<MixPortTraits>(doc, root, mixPorts, NULL);
module->setProfiles(mixPorts);
DevicePortTraits::Collection devicePorts;
// 解析DevicePort
deserializeCollection<DevicePortTraits>(doc, root, devicePorts, NULL);
module->setDeclaredDevices(devicePorts);
RouteTraits::Collection routes;
// 解析Route
deserializeCollection<RouteTraits>(doc, root, routes, module.get());
module->setRoutes(routes);
const xmlNode *children = root->xmlChildrenNode;
while (children != NULL) {
if (!xmlStrcmp(children->name, (const xmlChar *)childAttachedDevicesTag)) {
ALOGV("%s: %s %s found", __FUNCTION__, tag, childAttachedDevicesTag);
const xmlNode *child = children->xmlChildrenNode;
while (child != NULL) {
if (!xmlStrcmp(child->name, (const xmlChar *)childAttachedDeviceTag)) {
xmlChar *attachedDevice = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
if (attachedDevice != NULL) {
ALOGV("%s: %s %s=%s", __FUNCTION__, tag, childAttachedDeviceTag,
(const char*)attachedDevice);
sp<DeviceDescriptor> device =
module->getDeclaredDevices().getDeviceFromTagName(String8((const char*)attachedDevice));
ctx->addAvailableDevice(device);
xmlFree(attachedDevice);
}
}
child = child->next;
}
}
if (!xmlStrcmp(children->name, (const xmlChar *)childDefaultOutputDeviceTag)) {
xmlChar *defaultOutputDevice = xmlNodeListGetString(doc, children->xmlChildrenNode, 1);;
if (defaultOutputDevice != NULL) {
ALOGV("%s: %s %s=%s", __FUNCTION__, tag, childDefaultOutputDeviceTag,
(const char*)defaultOutputDevice);
sp<DeviceDescriptor> device =
module->getDeclaredDevices().getDeviceFromTagName(String8((const char*)defaultOutputDevice));
if (device != 0 && ctx->getDefaultOutputDevice() == 0) {
ctx->setDefaultOutputDevice(device);
ALOGV("%s: default is %08x", __FUNCTION__, ctx->getDefaultOutputDevice()->type());
}
xmlFree(defaultOutputDevice);
}
}
children = children->next;
}
return NO_ERROR;
}
4.2 MixPortTraits
MixPortTraits的解析是在ModuleTraits::deserialize()方法中调用的,代码实现如下:
// Deserialize childrens: Audio Mix Port, Audio Device Ports (Source/Sink), Audio Routes
MixPortTraits::Collection mixPorts;
deserializeCollection<MixPortTraits>(doc, root, mixPorts, NULL);
module->setProfiles(mixPorts);
status_t MixPortTraits::deserialize(_xmlDoc *doc, const _xmlNode *child, PtrElement &mixPort,
PtrSerializingCtx /*serializingContext*/)
{
string name = getXmlAttribute(child, Attributes::name);
if (name.empty()) {
ALOGE("%s: No %s found", __FUNCTION__, Attributes::name);
return BAD_VALUE;
}
ALOGV("%s: %s %s=%s", __FUNCTION__, tag, Attributes::name, name.c_str());
string role = getXmlAttribute(child, Attributes::role);
if (role.empty()) {
ALOGE("%s: No %s found", __FUNCTION__, Attributes::role);
return BAD_VALUE;
}
ALOGV("%s: Role=%s", __FUNCTION__, role.c_str());
audio_port_role_t portRole = role == "source" ? AUDIO_PORT_ROLE_SOURCE : AUDIO_PORT_ROLE_SINK;
mixPort = new Element(String8(name.c_str()), portRole);
AudioProfileTraits::Collection profiles;
deserializeCollection<AudioProfileTraits>(doc, child, profiles, NULL);
if (profiles.isEmpty()) {
sp <AudioProfile> dynamicProfile = new AudioProfile(gDynamicFormat,
ChannelsVector(), SampleRateVector());
dynamicProfile->setDynamicFormat(true);
dynamicProfile->setDynamicChannels(true);
dynamicProfile->setDynamicRate(true);
profiles.add(dynamicProfile);
}
mixPort->setAudioProfiles(profiles);
string flags = getXmlAttribute(child, Attributes::flags);
if (!flags.empty()) {
// Source role
if (portRole == AUDIO_PORT_ROLE_SOURCE) {
mixPort->setFlags(OutputFlagConverter::maskFromString(flags));
} else {
// Sink role
mixPort->setFlags(InputFlagConverter::maskFromString(flags));
}
}
string maxOpenCount = getXmlAttribute(child, Attributes::maxOpenCount);
if (!maxOpenCount.empty()) {
convertTo(maxOpenCount, mixPort->maxOpenCount);
}
string maxActiveCount = getXmlAttribute(child, Attributes::maxActiveCount);
if (!maxActiveCount.empty()) {
convertTo(maxActiveCount, mixPort->maxActiveCount);
}
// Deserialize children
AudioGainTraits::Collection gains;
deserializeCollection<AudioGainTraits>(doc, child, gains, NULL);
mixPort->setGains(gains);
return NO_ERROR;
}
4.3 DevicePortTraits
DevicePortTraits的解析也是在ModuleTraits::deserialize()方法中调用的,代码实现如下:
DevicePortTraits::Collection devicePorts;
deserializeCollection<DevicePortTraits>(doc, root, devicePorts, NULL);
module->setDeclaredDevices(devicePorts); // 保存数据
status_t DevicePortTraits::deserialize(_xmlDoc *doc, const _xmlNode *root, PtrElement &deviceDesc,
PtrSerializingCtx /*serializingContext*/)
{
string name = getXmlAttribute(root, Attributes::tagName);
if (name.empty()) {
ALOGE("%s: No %s found", __FUNCTION__, Attributes::tagName);
return BAD_VALUE;
}
ALOGV("%s: %s %s=%s", __FUNCTION__, tag, Attributes::tagName, name.c_str());
string typeName = getXmlAttribute(root, Attributes::type);
if (typeName.empty()) {
ALOGE("%s: no type for %s", __FUNCTION__, name.c_str());
return BAD_VALUE;
}
ALOGV("%s: %s %s=%s", __FUNCTION__, tag, Attributes::type, typeName.c_str());
string role = getXmlAttribute(root, Attributes::role);
if (role.empty()) {
ALOGE("%s: No %s found", __FUNCTION__, Attributes::role);
return BAD_VALUE;
}
ALOGV("%s: %s %s=%s", __FUNCTION__, tag, Attributes::role, role.c_str());
audio_port_role_t portRole = (role == Attributes::roleSource) ?
AUDIO_PORT_ROLE_SOURCE : AUDIO_PORT_ROLE_SINK;
audio_devices_t type = AUDIO_DEVICE_NONE;
if (!deviceFromString(typeName, type) ||
(!audio_is_input_device(type) && portRole == AUDIO_PORT_ROLE_SOURCE) ||
(!audio_is_output_devices(type) && portRole == AUDIO_PORT_ROLE_SINK)) {
ALOGW("%s: bad type %08x", __FUNCTION__, type);
return BAD_VALUE;
}
deviceDesc = new Element(type, String8(name.c_str()));
string address = getXmlAttribute(root, Attributes::address);
if (!address.empty()) {
ALOGV("%s: address=%s for %s", __FUNCTION__, address.c_str(), name.c_str());
deviceDesc->mAddress = String8(address.c_str());
}
AudioProfileTraits::Collection profiles;
deserializeCollection<AudioProfileTraits>(doc, root, profiles, NULL);
if (profiles.isEmpty()) {
sp <AudioProfile> dynamicProfile = new AudioProfile(gDynamicFormat,
ChannelsVector(), SampleRateVector());
dynamicProfile->setDynamicFormat(true);
dynamicProfile->setDynamicChannels(true);
dynamicProfile->setDynamicRate(true);
profiles.add(dynamicProfile);
}
deviceDesc->setAudioProfiles(profiles);
// Deserialize AudioGain children
deserializeCollection<AudioGainTraits>(doc, root, deviceDesc->mGains, NULL);
ALOGV("%s: adding device tag %s type %08x address %s", __FUNCTION__,
deviceDesc->getName().string(), type, deviceDesc->mAddress.string());
return NO_ERROR;
}
4.4 RouteTraits
RouteTraits的解析也是在ModuleTraits::deserialize()方法中调用的,代码实现如下:
RouteTraits::Collection routes;
deserializeCollection<RouteTraits>(doc, root, routes, module.get());
module->setRoutes(routes);// 保存数据
status_t RouteTraits::deserialize(_xmlDoc */*doc*/, const _xmlNode *root, PtrElement &element,
PtrSerializingCtx ctx)
{
string type = getXmlAttribute(root, Attributes::type);
if (type.empty()) {
ALOGE("%s: No %s found", __FUNCTION__, Attributes::type);
return BAD_VALUE;
}
audio_route_type_t routeType = (type == Attributes::typeMix) ?
AUDIO_ROUTE_MIX : AUDIO_ROUTE_MUX;
ALOGV("%s: %s %s=%s", __FUNCTION__, tag, Attributes::type, type.c_str());
element = new Element(routeType);
string sinkAttr = getXmlAttribute(root, Attributes::sink);
if (sinkAttr.empty()) {
ALOGE("%s: No %s found", __FUNCTION__, Attributes::sink);
return BAD_VALUE;
}
// Convert Sink name to port pointer
sp<AudioPort> sink = ctx->findPortByTagName(String8(sinkAttr.c_str()));
if (sink == NULL) {
ALOGE("%s: no sink found with name=%s", __FUNCTION__, sinkAttr.c_str());
return BAD_VALUE;
}
element->setSink(sink);
string sourcesAttr = getXmlAttribute(root, Attributes::sources);
if (sourcesAttr.empty()) {
ALOGE("%s: No %s found", __FUNCTION__, Attributes::sources);
return BAD_VALUE;
}
// Tokenize and Convert Sources name to port pointer
AudioPortVector sources;
char *sourcesLiteral = strndup(sourcesAttr.c_str(), strlen(sourcesAttr.c_str()));
char *devTag = strtok(sourcesLiteral, ",");
while (devTag != NULL) {
if (strlen(devTag) != 0) {
sp<AudioPort> source = ctx->findPortByTagName(String8(devTag));
if (source == NULL) {
ALOGE("%s: no source found with name=%s", __FUNCTION__, devTag);
free(sourcesLiteral);
return BAD_VALUE;
}
sources.add(source);
}
devTag = strtok(NULL, ",");
}
free(sourcesLiteral);
sink->addRoute(element);
for (size_t i = 0; i < sources.size(); i++) {
sp<AudioPort> source = sources.itemAt(i);
source->addRoute(element);
}
element->setSources(sources);
return NO_ERROR;
}
4.5 结果保存
解析出来的结果保存在AudioPolicyConfig对象中,如下是该对象中的数据结构。