audio_policy_configuration.xml是在Android 7.0中新引入了音频政策配置文件格式 (XML),用于描述音频拓扑。
以前的 Android 版本使用 audio_policy.conf 来声明您的产品上存在的音频设备)。但是,CONF 是一种简单的专有格式,有较大的局限性,无法描述电视和汽车等行业的复杂拓扑。
所以,在安卓7.0之后弃用了audio_policy.conf,转而使用XML 文件格式定义音频拓扑的支持,该文件格式更易于阅读,可使用各种修改和解析工具,并且足够灵活,可以描述复杂的音频拓扑。在安卓7.0-9.0依然可以通过在项目的mk中修改宏定义(USE_XML_AUDIO_POLICY_CONF)来使用conf文件,而安卓10以后则彻底不再支持conf文件。
以下代码基于Android 7.1
以下是S5P4418开发板中的audio_policy_configuration.xml文件
<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
<globalConfiguration speaker_drc_enabled="true"/>
<modules>
<module name="primary" halVersion="3.0">
<attachedDevices>
<item>Speakeritem>
<item>Built-In Micitem>
attachedDevices>
<defaultOutputDevice>SpeakerdefaultOutputDevice>
<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="hdmi output" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
mixPort>
<mixPort name="primary input" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
mixPort>
mixPorts>
<devicePorts>
<devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" 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="HDMI Out" type="AUDIO_DEVICE_OUT_AUX_DIGITAL" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
devicePort>
<devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
devicePort>
devicePorts>
<routes>
<route type="mix" sink="Speaker"
sources="primary output"/>
<route type="mix" sink="Wired Headphones"
sources="primary output"/>
<route type="mix" sink="HDMI Out"
sources="hdmi output"/>
<route type="mix" sink="primary input"
sources="Built-In Mic"/>
routes>
module>
modules>
audioPolicyConfiguration>
可以发现,这个xml文件的根节点为"audioPolicyConfiguration", 根节点下面依次是“globalConfiguration“和”modules“,因为这个开发板比较简陋,所以并没有很多属性。
modules节点中定义了module属性,每一个module都对应一个hal的库,在这里只有一个module,也就说只有一个hal库(primary),在module中有以下节点:
这个xml加载过程中会重复调用deserializeCollection函数,这里仅仅以mixPorts节点作为例子
解析modules时相关的结构体
const char *const ModuleTraits::childAttachedDevicesTag = "attachedDevices";
const char *const ModuleTraits::childAttachedDeviceTag = "item";
const char *const ModuleTraits::childDefaultOutputDeviceTag = "defaultOutputDevice";
const char *const ModuleTraits::tag = "module";
const char *const ModuleTraits::collectionTag = "modules";
const char ModuleTraits::Attributes::name[] = "name";
const char ModuleTraits::Attributes::version[] = "halVersion";
struct ModuleTraits
{
static const char *const tag;
static const char *const collectionTag;
static const char *const childAttachedDevicesTag;
static const char *const childAttachedDeviceTag;
static const char *const childDefaultOutputDeviceTag;
struct Attributes
{
static const char name[];
static const char version[];
};
typedef HwModule Element;
typedef sp<Element> PtrElement;
typedef HwModuleCollection Collection;
typedef AudioPolicyConfig *PtrSerializingCtx;
static status_t deserialize(_xmlDoc *doc, const _xmlNode *root, PtrElement &element,
PtrSerializingCtx serializingContext);
// Children are: mixPortTraits, devicePortTraits and routeTraits
// Need to call deserialize on each child
};
解析mixPorts的结构体
const char *const MixPortTraits::collectionTag = "mixPorts";
const char *const MixPortTraits::tag = "mixPort";
const char MixPortTraits::Attributes::name[] = "name";
const char MixPortTraits::Attributes::role[] = "role";
const char MixPortTraits::Attributes::flags[] = "flags";
struct MixPortTraits
{
static const char *const tag;
static const char *const collectionTag;
struct Attributes
{
static const char name[];
static const char role[];
static const char flags[];
};
typedef IOProfile Element;
typedef sp<Element> PtrElement;
typedef IOProfileCollection Collection;
typedef void *PtrSerializingCtx;
static status_t deserialize(_xmlDoc *doc, const _xmlNode *root, PtrElement &element,
PtrSerializingCtx serializingContext);
// Children are: GainTraits
};
const char *const AudioProfileTraits::collectionTag = "profiles";
const char *const AudioProfileTraits::tag = "profile";
const char AudioProfileTraits::Attributes::name[] = "name";
const char AudioProfileTraits::Attributes::samplingRates[] = "samplingRates";
const char AudioProfileTraits::Attributes::format[] = "format";
const char AudioProfileTraits::Attributes::channelMasks[] = "channelMasks";
audio_policy_configuration.xml是在AudioPolicyManager的构造函数中被加载解析的
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
:
#ifdef AUDIO_POLICY_TEST
Thread(false),
#endif //AUDIO_POLICY_TEST
mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
mA2dpSuspended(false),
mAudioPortGeneration(1),
mBeaconMuteRefCount(0),
mBeaconPlayingRefCount(0),
mBeaconMuted(false),
mTtsOutputAvailable(false),
mMasterMono(false)
{
mUidCached = getuid();
mpClientInterface = clientInterface;
// TODO: remove when legacy conf file is removed. true on devices that use DRC on the
// DEVICE_CATEGORY_SPEAKER path to boost soft sounds, used to adjust volume curves accordingly.
// Note: remove also speaker_drc_enabled from global configuration of XML config file.
bool speakerDrcEnabled = false;
#ifdef USE_XML_AUDIO_POLICY_CONF
mVolumeCurves = new VolumeCurvesCollection();
AudioPolicyConfig config(mHwModules, mAvailableOutputDevices, mAvailableInputDevices, //新建AudioPolicyConfig对象
mDefaultOutputDevice, speakerDrcEnabled,
static_cast(mVolumeCurves));
PolicySerializer serializer; //新建PolicySerializer对象用于解析xml
if (serializer.deserialize(AUDIO_POLICY_XML_CONFIG_FILE, config) != NO_ERROR) { //#define AUDIO_POLICY_XML_CONFIG_FILE "/system/etc/audio_policy_configuration.xml"
///省略
在安卓7.0中,xml的文件位置是写死的,但是在后续的安卓版本中会在多个目录下搜寻xml文件。
在解析xml文件之前,先创建了一个AudioPolicyConfig对象config,并传入了几个对象用于类的初始化,这几个对象都是在AudioPolicyManager类中定义的。
接着创建了PolicySerializer对象,解析xml就是用的这个类的deserialize函数,同时传入了xml文件的路径字串和config对象,在PolicySerializer中解析xml文件用的是libxml2库,代码位于external/tinyxml2/
status_t PolicySerializer::deserialize(const char *configFile, AudioPolicyConfig &config)
{
xmlDocPtr doc;
doc = xmlParseFile(configFile); //调用libxml2库读取xml文件,相当于是文件句柄
if (doc == NULL) {
ALOGE("%s: Could not parse %s document.", __FUNCTION__, configFile);
return BAD_VALUE;
}
xmlNodePtr cur = xmlDocGetRootElement(doc); //获取根节点
if (cur == NULL) {
ALOGE("%s: Could not parse %s document: empty.", __FUNCTION__, configFile);
xmlFreeDoc(doc);
return BAD_VALUE;
}
if (xmlXIncludeProcess(doc) < 0) { //解析xml的包含关系,我的开发板比较简单,没有包含其他xml
ALOGE("%s: libxml failed to resolve XIncludes on %s document.", __FUNCTION__, configFile);
}
if (xmlStrcmp(cur->name, (const xmlChar *) mRootElementName.c_str())) { //判断根节点名字是不是"audioPolicyConfiguration"
ALOGE("%s: No %s root element found in xml data %s.", __FUNCTION__, mRootElementName.c_str(),
(const char *)cur->name);
xmlFreeDoc(doc);
return BAD_VALUE;
}
string version = getXmlAttribute(cur, versionAttribute); //获取xml的版本
if (version.empty()) {
ALOGE("%s: No version found in root node %s", __FUNCTION__, mRootElementName.c_str());
return BAD_VALUE;
}
if (version != mVersion) {
ALOGE("%s: Version does not match; expect %s got %s", __FUNCTION__, mVersion.c_str(),
version.c_str());
return BAD_VALUE;
}
// Lets deserialize children
// Modules
ModuleTraits::Collection modules; //创建HwModuleCollection对象,开始解析modules节点
deserializeCollection<ModuleTraits>(doc, cur, modules, &config); //解析modules节点
config.setHwModules(modules); //将存储了解析数据的modules赋值给AudioPolicyConfig里面的HwModules
// deserialize volume section
VolumeTraits::Collection volumes; //解析音量相关,但是我的这个xml文件里面并没有包含音量的xml
deserializeCollection<VolumeTraits>(doc, cur, volumes, &config);
config.setVolumes(volumes);
// Global Configuration
GlobalConfigTraits::deserialize(cur, config);
xmlFreeDoc(doc);
return android::OK;
}
}; // namespace android
deserializeCollection
这个函数比较重要,在解析各种节点时都会被调用到。
主要作用是根据当前传入的模板类型,找到当前模板类型所对应的节点,并最终调用当前模板类型的deserialize函数来对节点进行解析
在xml中的各种属性都是先包含在一个Attributes节点下,该节点下再存放具体的Attribute节点
所以各种属性的collectionTag就是对应Attributes节点名称,tag对应Attribute节点
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; //进入根节点节点下的第一个节点,这个cur只有在传入ModuleTraits类时是根节点
while (root != NULL) { //遍历当前所有节点
if (xmlStrcmp(root->name, (const xmlChar *)Trait::collectionTag) && //根据name来判断当前节点是不是要解析的
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; //HwModule
status_t status = Trait::deserialize(doc, child, element, serializingContext); //调用HwModuleCollection::deserialize
if (status != NO_ERROR) {
return status;
}
if (collection.add(element) < 0) { //解析成功则把HwModule插入到HwModuleCollection这个容器中
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;
}
ModuleTraits::deserialize
解析module节点的函数,解析出来的数据会保存在一个HwModule对象中
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 version = AUDIO_DEVICE_API_VERSION_MIN;
string versionLiteral = getXmlAttribute(root, Attributes::version);
if (!versionLiteral.empty()) {
uint32_t major, minor;
sscanf(versionLiteral.c_str(), "%u.%u", &major, &minor);
version = HARDWARE_DEVICE_API_VERSION(major, minor);
ALOGV("%s: mHalVersion = %04x major %u minor %u", __FUNCTION__,
version, major, minor);
}
ALOGV("%s: %s %s=%s", __FUNCTION__, tag, Attributes::name, name.c_str());
module = new Element(name.c_str(), version); //根据名字和版本号创建对象 对应第一个module名字为"primary"
// Deserialize childrens: Audio Mix Port, Audio Device Ports (Source/Sink), Audio Routes
MixPortTraits::Collection mixPorts; //解析mixPorts
deserializeCollection<MixPortTraits>(doc, root, mixPorts, NULL); //又是这个函数
module->setProfiles(mixPorts);
DevicePortTraits::Collection devicePorts; //解析devicePorts
deserializeCollection<DevicePortTraits>(doc, root, devicePorts, NULL);
module->setDeclaredDevices(devicePorts);
RouteTraits::Collection routes; //解析routes
deserializeCollection<RouteTraits>(doc, root, routes, module.get());
module->setRoutes(routes);
const xmlNode *children = root->xmlChildrenNode; //解析AttachedDevices和DefaultOutputDevice
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;
}
deserializeCollection(doc, root, mixPorts, NULL);传入的mixPorts是一个IOProfile容器,里面会存放解析出来的mixPort
deserializeCollection函数会创建Trait::Element,并调用Trait::deserialize(doc, child, element, serializingContext)函数进一步解析子节点,在解析相关节点成功后将插入参数中传入的容器中
status_t MixPortTraits::deserialize(_xmlDoc *doc, const _xmlNode *child, PtrElement &mixPort,
PtrSerializingCtx /*serializingContext*/)
{
string name = getXmlAttribute(child, Attributes::name); //"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); //"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; //AUDIO_PORT_ROLE_SOURCE = 1, AUDIO_PORT_ROLE_SINK = 2
mixPort = new Element(String8(name.c_str()), portRole); //IOProfile
AudioProfileTraits::Collection profiles;
deserializeCollection<AudioProfileTraits>(doc, child, profiles, NULL); //解析profile节点的属性
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));
}
}
// Deserialize children
AudioGainTraits::Collection gains;
deserializeCollection<AudioGainTraits>(doc, child, gains, NULL);
mixPort->setGains(gains);
return NO_ERROR;
}
//解析profile节点
status_t AudioProfileTraits::deserialize(_xmlDoc */*doc*/, const _xmlNode *root, PtrElement &profile,
PtrSerializingCtx /*serializingContext*/)
{
string samplingRates = getXmlAttribute(root, Attributes::samplingRates); //samplingRates
string format = getXmlAttribute(root, Attributes::format); //format
string channels = getXmlAttribute(root, Attributes::channelMasks); //channelMasks
profile = new Element(formatFromString(format), channelMasksFromString(channels, ","),
samplingRatesFromString(samplingRates, ","));
profile->setDynamicFormat(profile->getFormat() == gDynamicFormat);
profile->setDynamicChannels(profile->getChannels().isEmpty());
profile->setDynamicRate(profile->getSampleRates().isEmpty());
return NO_ERROR;
}
只列出mixPorts
modules //class HwModuleCollection:public Vector >
{
module1 //class HwModule
{
mixPorts //typedef Vector > IOProfileCollection; IOProfile容器
{
mixPort1 //class IOProfile : public AudioPort
{
name = "primary output";
role = AUDIO_PORT_ROLE_SOURCE
mProfiles class AudioProfileVector : public Vector<sp<AudioProfile> > 相当于AudioProfile容器
{
AudioProfile //class AudioProfile : public virtual RefBase
{
format = AUDIO_FORMAT_PCM_16_BIT;
channelMasks = AUDIO_CHANNEL_OUT_STEREO;
samplingRates = 48000;
}
}
}
mixPort2 //
{
name = "hdmi output"
role = AUDIO_PORT_ROLE_SOURCE
mProfiles //class AudioProfileVector : public Vector > 相当于AudioProfile容器
{
AudioProfile //class AudioProfile : public virtual RefBase
{
format = AUDIO_FORMAT_PCM_16_BIT;
channelMasks = AUDIO_CHANNEL_OUT_STEREO;
samplingRates = 48000;
}
}
}
mixPort3 //
{
name = "primary input"
role = AUDIO_PORT_ROLE_SOURCE
mProfiles //class AudioProfileVector : public Vector > 相当于AudioProfile容器
{
AudioProfile //class AudioProfile : public virtual RefBase
{
format = AUDIO_FORMAT_PCM_16_BIT;
channelMasks = AUDIO_CHANNEL_IN_STEREO;
samplingRates = 48000;
}
}
}
}
devicePorts //
{
...;
}
routes //
{
...;
}
}
}