audio_policy_configuration.xml加载过程

介绍

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

XML文件一览

以下是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中有以下节点:

  • attachedDevices:描述了在当前的设备上连接了哪些音频设备,
  • defaultOutputDevice:默认音频设备
  • mixPorts:描述了可以在音频 HAL 播放和输入的流的配置信息
  • devicePorts:描述了什么type的音频流可以传输到什么设备中
  • routes:描述音频路径,什么样的源(sources)连接到什么样的端点(sink)

这个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      //
        {
            ...;
        }
    }
}

你可能感兴趣的:(安卓音频)