aosp15音频AudioPolicy部分Product Strategies和Volume Groups剖析

背景:

在讲解音频的AudioTrack如何设置了StreamType,就可以顺利选择到Output Thread。这里其实是需要经过很多转换的:
StreamType —> Auttributes —>Strategy —>Devcie —>Ouput Thread.
那么这里就需要剖析Auttributes —>Strategy这块的转换,所以需要深入Auttributes和Strategy的剖析。

寻找一个好的切入点分析这块内容,可以让整个剖析变得更加简单高效,而不是一上来分析源码,可能大家容易晕。在使用dumpsys media.audio_policy时候经常会看到关于Engine的2个重要输出:Product Strategies和Volume Groups相关。
涉及这块的全面视频讲解:
https://mp.weixin.qq.com/s/pQr-HrW0EUoi5QHTMOGEBQ

1、Product Strategies相关

在这里插入图片描述

2、Volume Groups相关

在这里插入图片描述
那么大家是否有考虑这个两个输出具体是怎么来的,还有Product Strategies和Volume Groups二者有什么联系么?二者又是桌面联系到一起的,带着这些疑问下面来进行详细剖析

输出内容代码剖析

一般寻找dump相关源码时候,一般可以采用grep关键字,具体如下:

test@test:~/aosp15/frameworks/av$ grep "Policy Engine dump" ./ -rn
./services/audiopolicy/managerdefault/AudioPolicyManager.cpp:4517:    dst->appendFormat("\nPolicy Engine dump:\n");

找到代码时间上在AudioPolicyManager.cpp:4517。

在这里插入图片描述可以看出这里调用到了EngineBase::dump方法中:

void EngineBase::dump(String8 *dst) const
{
    mProductStrategies.dump(dst, 2);
    dumpProductStrategyDevicesRoleMap(mProductStrategyDeviceRoleMap, dst, 2);
    dumpCapturePresetDevicesRoleMap(dst, 2);
    mVolumeGroups.dump(dst, 2);
}

对于ProductStrategies部分

void ProductStrategyMap::dump(String8 *dst, int spaces) const
{
    dst->appendFormat("%*sProduct Strategies dump:", spaces, "");
    for (const auto &iter : *this) {
        iter.second->dump(dst, spaces + 2);
    }
}

这里的mProductStrategies是ProductStrategyMap

ProductStrategyMap mProductStrategies;

而实际ProductStrategyMap是一个map类型

class ProductStrategyMap : public std::map<product_strategy_t, sp<ProductStrategy> >

key也叫first其实是product_strategy_t,value也叫second其实是ProductStrategy,所以是调用到了ProductStrategy的dump方法

void ProductStrategy::dump(String8 *dst, int spaces) const
{
    dst->appendFormat("\n%*s-%s (id: %d)\n", spaces, "", mName.c_str(), mId);
    std::string deviceLiteral = deviceTypesToString(mApplicableDevices);
    dst->appendFormat("%*sSelected Device: {%s, @:%s}\n", spaces + 2, "",
                       deviceLiteral.c_str(), mDeviceAddress.c_str());

    for (const auto &attr : mAttributesVector) {
        dst->appendFormat("%*sGroup: %d stream: %s\n", spaces + 3, "", attr.getGroupId(),
                          android::toString(attr.getStreamType()).c_str());
        dst->appendFormat("%*s Attributes: ", spaces + 3, "");
        std::string attStr = attr.getAttributes() == defaultAttr ?
                "{ Any }" : android::toString(attr.getAttributes());
        dst->appendFormat("%s\n", attStr.c_str());
    }
}

这可以看到ProductStrategy的dump部分其实主要有3部分:

1、ProductStrategy的 mName

2、ProductStrategy的mApplicableDevices

3、mAttributesVector集合,这个也是最核心部分,代表这个ProductStrategy支持哪些VolumeGroupAttributes。

下面来详细看看ProductStrategy相关结构和dump输出的信息进行对应
在这里插入图片描述那么请问这个ProductStrategy对象是哪里来的呢?
其实是在模拟器上ProductStrategy数据来源其实代码写死的硬编码
代码路径如下:

frameworks/av/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
/**
 * @brief AudioProductStrategies hard coded array of strategies to fill new engine API contract.
 */
const engineConfig::ProductStrategies gOrderedStrategies = {
    {"STRATEGY_PHONE",
     {
         {AUDIO_STREAM_VOICE_CALL, "AUDIO_STREAM_VOICE_CALL",
          {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_SOURCE_DEFAULT,
            AUDIO_FLAG_NONE, ""}},
         },
         {AUDIO_STREAM_BLUETOOTH_SCO, "AUDIO_STREAM_BLUETOOTH_SCO",
          {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_SCO,
            ""}},
         }
     },
    },
    {"STRATEGY_SONIFICATION",
     {
         {AUDIO_STREAM_RING, "AUDIO_STREAM_RING",
          {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
            AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}}
         },
         {AUDIO_STREAM_ALARM, "AUDIO_STREAM_ALARM",
          {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ALARM, AUDIO_SOURCE_DEFAULT,
            AUDIO_FLAG_NONE, ""}},
         }
     },
    },
    {"STRATEGY_ENFORCED_AUDIBLE",
     {
         {AUDIO_STREAM_ENFORCED_AUDIBLE, "AUDIO_STREAM_ENFORCED_AUDIBLE",
          {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT,
            AUDIO_FLAG_AUDIBILITY_ENFORCED, ""}}
         }
     },
    },
    {"STRATEGY_ACCESSIBILITY",
     {
         {AUDIO_STREAM_ACCESSIBILITY, "AUDIO_STREAM_ACCESSIBILITY",
          {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY,
            AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}}
         }
     },
    },
    {"STRATEGY_SONIFICATION_RESPECTFUL",
     {
         {AUDIO_STREAM_NOTIFICATION, "AUDIO_STREAM_NOTIFICATION",
          {
              {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION, AUDIO_SOURCE_DEFAULT,
               AUDIO_FLAG_NONE, ""},
              {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_EVENT,
               AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}
          }
         }
     },
    },
    {"STRATEGY_MEDIA",
     {
         {AUDIO_STREAM_ASSISTANT, "AUDIO_STREAM_ASSISTANT",
          {{AUDIO_CONTENT_TYPE_SPEECH, AUDIO_USAGE_ASSISTANT,
            AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}}
         },
         {AUDIO_STREAM_MUSIC, "AUDIO_STREAM_MUSIC",
          {
              {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_MEDIA, AUDIO_SOURCE_DEFAULT,
               AUDIO_FLAG_NONE, ""},
              {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_GAME, AUDIO_SOURCE_DEFAULT,
               AUDIO_FLAG_NONE, ""},
              {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANT, AUDIO_SOURCE_DEFAULT,
               AUDIO_FLAG_NONE, ""},
              {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
               AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""},
              {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT,
               AUDIO_FLAG_NONE, ""}
          },
         },
         {AUDIO_STREAM_SYSTEM, "AUDIO_STREAM_SYSTEM",
          {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_SONIFICATION,
            AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}}
         }
     },
    },
    {"STRATEGY_DTMF",
     {
         {AUDIO_STREAM_DTMF, "AUDIO_STREAM_DTMF",
          {
              {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING,
               AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}
          }
         }
     },
    },
    {"STRATEGY_CALL_ASSISTANT",
     {
         {AUDIO_STREAM_CALL_ASSISTANT, "AUDIO_STREAM_CALL_ASSISTANT",
          {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_CALL_ASSISTANT, AUDIO_SOURCE_DEFAULT,
            AUDIO_FLAG_NONE, ""}}
         }
     },
    },
    {"STRATEGY_TRANSMITTED_THROUGH_SPEAKER",
     {
         {AUDIO_STREAM_TTS, "AUDIO_STREAM_TTS",
          {
              {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT,
                AUDIO_FLAG_BEACON, ""},
              {AUDIO_CONTENT_TYPE_ULTRASOUND, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT,
                AUDIO_FLAG_NONE, ""}
          }
         }
     },
    }
};


看到上面这个代码后,大家是不是就感觉和dump输出的基本上可以对上了,但是这个其实和dump输出ProductStrategy还是有差异的。
这里其实是struct ProductStrategy类型,即是一个结构体

struct ProductStrategy {
    std::string name;
    AttributesGroups attributesGroups;
};
using AttributesGroups = std::vector<AttributesGroup>;

struct AttributesGroup {
    audio_stream_type_t stream;
    std::string volumeGroup;
    AttributesVector attributesVect;
};
using AttributesVector = std::vector<audio_attributes_t>;

拿出一小部分进行举例子:

{"STRATEGY_ACCESSIBILITY", //---代表name
     { //---整体代表AttributesGroups
         {AUDIO_STREAM_ACCESSIBILITY, //代表stream类型audio_stream_type_t
         "AUDIO_STREAM_ACCESSIBILITY",//代表volumeGroup类型string
          {
          {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY,
            AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}//代表attributesVect的一个,类型audio_attributes_t
            }
         }
     },
    },

那么这个总结构体gOrderedStrategies到底是如何变成class ProductStrategyMap这样的类集合和class ProductStrategy类对象呢?

这里就需要看如下的源码剖析出他们转换关系:
frameworks/av/services/audiopolicy/engine/common/src/EngineBase.cpp

engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig(const std::string& xmlFilePath)
{
//如果传递xmlFilePath则使用默认路径/vendor/etc/audio_policy_engine_configuration.xml赋值给filePath
    const std::string filePath = xmlFilePath.empty() ? engineConfig::DEFAULT_PATH : xmlFilePath;
    //文件存在则调用engineConfig::parse进行解析,如果不存在则直接赋值空的result
    engineConfig::ParsingResult result =
            fileExists(filePath.c_str()) ?
            engineConfig::parse(filePath.c_str()) : engineConfig::ParsingResult{};
    //如何赋值result为null
    if (result.parsedConfig == nullptr) {
    		 //使用gDefaultEngineConfig赋值给config
        engineConfig::Config config = gDefaultEngineConfig;
        //调用parseLegacyVolumes方法给config.volumeGroups进行赋值
        android::status_t ret = engineConfig::parseLegacyVolumes(config.volumeGroups);
        //利用新创建config构造成新的result
        result = {std::make_unique<engineConfig::Config>(config),
                  static_cast<size_t>(ret == NO_ERROR ? 0 : 1)};
    } else {
  
    }
    //调用processParsingResult来进行真正转换
    return processParsingResult(std::move(result));
}

struct ParsingResult {
    /** Parsed config, nullptr if the xml lib could not load the file */
    std::unique_ptr<Config> parsedConfig;
    size_t nbSkippedElement; //< Number of skipped invalid product strategies
};

上面可以看到最核心的是gDefaultEngineConfig赋值,那么gDefaultEngineConfig和gOrderedStrategies又有啥关系呢?
看下面源码
frameworks/av/services/audiopolicy/engine/config/include/EngineConfig.h

struct Config {
    float version;
    ProductStrategies productStrategies;
    Criteria criteria;
    CriterionTypes criterionTypes;
    VolumeGroups volumeGroups;
};

//明显gOrderedStrategies成为了gDefaultEngineConfig的成员
const engineConfig::Config gDefaultEngineConfig = {
    1.0,
    gOrderedStrategies,
    {},
    {},
    {}
};

那现在回到processParsingResult方法,看看是如何进行转换的


engineConfig::ParsingResult EngineBase::processParsingResult(
        engineConfig::ParsingResult&& rawResult)
{
//下面省略部分非本次分析核心代码
   
    auto addSupportedAttributesToGroup = [](auto &group, auto &volumeGroup, auto &strategy) {
        for (const auto &attr : group.attributesVect) {
           //strategy会关联volumeGroup
            strategy->addAttributes({volumeGroup->getId(), group.stream, attr});
           //volumeGroup会关联到对应的attr
            volumeGroup->addSupportedAttributes(attr);
        }
    };
 
    auto result = std::move(rawResult);
    
   //开始转换上面默认声明的productStrategies结构体,转换成ProductStrategyMap
    for (auto& strategyConfig : result.parsedConfig->productStrategies) {
    //strategyConfig就是struct ProductStrategy对象,利用name构造出ProductStrategy类对象,注意一个是config的struct一个是class,注意区分
        sp<ProductStrategy> strategy = new ProductStrategy(strategyConfig.name);
        //strategyConfig获取他的attributesGroups中的一个个AttributesGroup
        for (const auto &group : strategyConfig.attributesGroups) {
        //这里会用strategy的AttributesGroup与mVolumeGroups的中的进行名字比较,其实就是对stream type名字进行比较
            const auto &iter = std::find_if(begin(mVolumeGroups), end(mVolumeGroups),
                                         [&group](const auto &volumeGroup) {
                    return group.volumeGroup == volumeGroup.second->getName(); });
           
            if (iter == end(mVolumeGroups)) {
     				//没找到
            } else {
            //找打直接赋值
                volumeGroup = iter->second;
            }
            if (group.stream != AUDIO_STREAM_DEFAULT) {
                // A legacy stream can be assigned once to a volume group
        				//把stream type赋值给volumeGroup
                volumeGroup->addSupportedStream(group.stream);
            }
            //这里会把strategy和volumeGroup相互进行关联一些属性
            addSupportedAttributesToGroup(group, volumeGroup, strategy);
        }
        product_strategy_t strategyId = strategy->getId();
        //这里对上面构造strategy对象放到mProductStrategies这个map中
        mProductStrategies[strategyId] = strategy;
    }
    mProductStrategies.initialize();
    return result;
}

上面方法就完成了以下几个任务

1、把原来的struct类型的strategy变成class类型的strategy,并且对象放入到mProductStrategies集合中

2、把strategy相关的attribute数据通过StreamType与volumeGroup进行关联,这样二者就可以用共同的数据attribute数据

那么接下来看看Volume Groups相关数据。

Volume Groups数据部分

同样也是在EngineBase::loadAudioPolicyEngineConfig进行的数据加载

engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig(const std::string& xmlFilePath)
{
   //省略部分和本次分析无关代码
    if (result.parsedConfig == nullptr) {
        engineConfig::Config config = gDefaultEngineConfig;
        //这里调用parseLegacyVolumes方法,参数是config.volumeGroups,这里config.volumeGroups其实空的
        android::status_t ret = engineConfig::parseLegacyVolumes(config.volumeGroups);
        result = {std::make_unique<engineConfig::Config>(config),
                  static_cast<size_t>(ret == NO_ERROR ? 0 : 1)};
    } else {
   
    }
    //和上面分析strategy一样都会执行processParsingResult
    return processParsingResult(std::move(result));
}

这里重点是parseLegacyVolumes方法:

android::status_t parseLegacyVolumes(VolumeGroups &volumeGroups) {
    if (std::string audioPolicyXmlConfigFile = audio_get_audio_policy_config_file();
            !audioPolicyXmlConfigFile.empty()) {
        return parseLegacyVolumeFile(audioPolicyXmlConfigFile.c_str(), volumeGroups);
    } else {
        
        return BAD_VALUE;
    }
}

这里一般audio_get_audio_policy_config_file获取的是/vendor/etc/audio_policy_configuration.xml,在调用parseLegacyVolumeFile方法,主要对这个xml文件进行解析,赋值给VolumeGroups这个参数

android::status_t parseLegacyVolumeFile(const char* path, VolumeGroups &volumeGroups) {
    XmlErrorHandler errorHandler;
    auto doc = make_xmlUnique(xmlParseFile(path));
//最后调用到deserializeLegacyVolumeCollection方法
    return deserializeLegacyVolumeCollection(doc.get(), cur, volumeGroups, nbSkippedElements);
}
static status_t deserializeLegacyVolumeCollection(_xmlDoc *doc, const _xmlNode *cur,
                                                  VolumeGroups &volumeGroups,
                                                  size_t &nbSkippedElement)
{
    //第一部分主要是遍历xml解析成struct类型的legacyVolumeMap集合
    //legacyVolumeMap其实就是一个StreamType对应VolumeCurves,VolumeCurves就是对应多个VolumeCurve,VolumeCurve就是具体device和音量曲线的几个点
    std::map<std::string, VolumeCurves> legacyVolumeMap;
    for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
        if (xmlStrcmp(cur->name, (const xmlChar *)legacyVolumecollectionTag)) {
            continue;
        }
        const xmlNode *child = cur->xmlChildrenNode;
        for (; child != NULL; child = child->next) {
            if (!xmlStrcmp(child->name, (const xmlChar *)legacyVolumeTag)) {

                status_t status = deserializeLegacyVolume(doc, child, legacyVolumeMap);
                if (status != NO_ERROR) {
                    nbSkippedElement += 1;
                }
            }
        }
    }
    //接下来就是把上面的legacyVolumeMap转换成VolumeGroups对象,相比原区别就是多了两个最大最小参数
    VolumeGroups tempVolumeGroups = volumeGroups;
    for (const auto &volumeMapIter : legacyVolumeMap) {
        // In order to let AudioService setting the min and max (compatibility), set Min and Max
        // to -1 except for private streams
        audio_stream_type_t streamType;
        if (!StreamTypeConverter::fromString(volumeMapIter.first, streamType)) {
            ALOGE("%s: Invalid stream %s", __func__, volumeMapIter.first.c_str());
            return BAD_VALUE;
        }
        int indexMin = streamType >= AUDIO_STREAM_PUBLIC_CNT ? 0 : -1;
        int indexMax = streamType >= AUDIO_STREAM_PUBLIC_CNT ? 100 : -1;
        tempVolumeGroups.push_back(
                { volumeMapIter.first, indexMin, indexMax, volumeMapIter.second });
    }
    //把tempVolumeGroups赋给volumeGroups这个入参,因为它是引用类型
    std::swap(tempVolumeGroups, volumeGroups);
    return NO_ERROR;
}

这个方法本质就是来进行解析xml的,具体xml是audio_policy_configuration.xml
来看看它的内容:

<audioPolicyConfiguration version="7.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="false"/>

    <modules>
        <!-- Primary Audio HAL -->
        <xi:include href="primary_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_7_0.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 -->

</audioPolicyConfiguration>

这里可以看出实际volume相关是靠include了audio_policy_volumes.xml和default_volume_tables.xml了个文件
audio_policy_volumes.xml内容如下,它主要是顶一个每个StreamType对每个设备使用哪个音量曲线:

<volumes>
    <volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_HEADSET">
        <point>0,-4200</point>
        <point>33,-2800</point>
        <point>66,-1400</point>
        <point>100,0</point>
    </volume>
    <volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_SPEAKER">
        <point>0,-2400</point>
        <point>33,-1600</point>
        <point>66,-800</point>
        <point>100,0</point>
    </volume>
    <volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_EARPIECE">
        <point>0,-2400</point>
        <point>33,-1600</point>
        <point>66,-800</point>
        <point>100,0</point>
    </volume>
    <volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_EXT_MEDIA"
                                             ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
    <volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_HEARING_AID"
                                             ref="DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE"/>
    <volume stream="AUDIO_STREAM_SYSTEM" deviceCategory="DEVICE_CATEGORY_HEADSET">
        <point>1,-3000</point>
        <point>33,-2600</point>
        <point>66,-2200</point>
        <point>100,-1800</point>
    </volume>
    <volume stream="AUDIO_STREAM_SYSTEM" deviceCategory="DEVICE_CATEGORY_SPEAKER"
                                         ref="DEFAULT_SYSTEM_VOLUME_CURVE"/>
    <volume stream="AUDIO_STREAM_SYSTEM" deviceCategory="DEVICE_CATEGORY_EARPIECE"
                                         ref="DEFAULT_SYSTEM_VOLUME_CURVE"/>
    <volume stream="AUDIO_STREAM_SYSTEM" deviceCategory="DEVICE_CATEGORY_EXT_MEDIA"
                                         ref="DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE"/>
    <volume stream="AUDIO_STREAM_SYSTEM" deviceCategory="DEVICE_CATEGORY_HEARING_AID"
                                         ref="DEFAULT_HEARING_AID_VOLUME_CURVE"/>
    <volume stream="AUDIO_STREAM_RING" deviceCategory="DEVICE_CATEGORY_HEADSET"
                                       ref="DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE"/>
    <volume stream="AUDIO_STREAM_RING" deviceCategory="DEVICE_CATEGORY_SPEAKER">
        <point>1,-2970</point>
        <point>33,-2010</point>
        <point>66,-1020</point>
        <point>100,0</point>
    </volume>

在这个xml中,stream就是代表对应的StreamType的字符,后面靠这个字符会与strategy中的attribute数据进行匹配,
deviceCategory就代表具体哪个设备使用该音量曲线,最后的就是具体音量曲线,这里有2中形式,一种是直接写出曲线的几个坐标点point标签:

       <point>1,-2970</point>
        <point>33,-2010</point>
        <point>66,-1020</point>
        <point>100,0</point>

另一种是通过的 ref="DEFAULT_NON_MUTABLE_HEARING_AID_VOLUME_CURVE"这种引用一个曲线名字的方式,那么这个引用的这个曲线名字到底在哪呢?其实就是在default_volume_tables.xml中,这里面会有一个个的曲线名字声明。

default_volume_tables.xml内容如下,它主要是定义一个个的音量曲线

<volumes>
    <reference name="FULL_SCALE_VOLUME_CURVE">
    <!-- Full Scale reference Volume Curve -->
        <point>0,0</point>
        <point>100,0</point>
    </reference>
    <reference name="SILENT_VOLUME_CURVE">
        <point>0,-9600</point>
        <point>100,-9600</point>
    </reference>
    <reference name="DEFAULT_SYSTEM_VOLUME_CURVE">
    <!-- Default System reference Volume Curve -->
        <point>1,-2400</point>
        <point>33,-1800</point>
        <point>66,-1200</point>
        <point>100,-600</point>
    </reference>
    <reference name="DEFAULT_MEDIA_VOLUME_CURVE">
    <!-- Default Media reference Volume Curve -->
        <point>1,-5800</point>
        <point>20,-4000</point>
        <point>60,-1700</point>
        <point>100,0</point>
    </reference>
    <reference name="DEFAULT_DEVICE_CATEGORY_HEADSET_VOLUME_CURVE">
    <!--Default Volume Curve -->
        <point>1,-4950</point>
        <point>33,-3350</point>
        <point>66,-1700</point>
        <point>100,0</point>
    </reference>
    <reference name="DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE">
    <!-- Default is Speaker Media Volume Curve -->
        <point>1,-5800</point>
        <point>20,-4000</point>
        <point>60,-1700</point>
        <point>100,0</point>
    </reference>
    <reference name="DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE">
    <!--Default Volume Curve -->
        <point>1,-4950</point>
        <point>33,-3350</point>
        <point>66,-1700</point>
        <point>100,0</point>
    </reference>

上面这些xml解析完成后就会构成对应的VolumeGroups的一个一个个VolumeGroup。

xml变成struct VolumeGroup部分总结图如下:
在这里插入图片描述

其实和Strategy一样,VolumeGroup也需要从struct结构变成class对象,具体是在loadAudioPolicyEngineConfig的processParsingResult方法中,又回到和Strategy分析一样了

engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig(const std::string& xmlFilePath)
{
   //省略
    return processParsingResult(std::move(result));
}

engineConfig::ParsingResult EngineBase::processParsingResult(
        engineConfig::ParsingResult&& rawResult)
{
//真正struct的VolumeGroup变成class的VolumeGroup
    auto loadVolumeConfig = [](auto &volumeGroups, auto &volumeConfig) {
        // Ensure volume group name uniqueness.
      //基于struct的VolumeGroup属性作为来源构造class的VolumeGroup
        sp<VolumeGroup> volumeGroup = new VolumeGroup(volumeConfig.name, volumeConfig.indexMin,
                                                      volumeConfig.indexMax);
        volumeGroups[volumeGroup->getId()] = volumeGroup;

        for (auto &configCurve : volumeConfig.volumeCurves) {
            device_category deviceCat = DEVICE_CATEGORY_SPEAKER;
            if (!DeviceCategoryConverter::fromString(configCurve.deviceCategory, deviceCat)) {
                ALOGE("%s: Invalid %s", __FUNCTION__, configCurve.deviceCategory.c_str());
                continue;
            }
            if (!com::android::media::audio::alarm_min_volume_zero()) {
                /*
                 * This special handling is done because the audio_policy_volumes.xml
                 * is updated but if the flag is disabled, the min alarm volume can
                 * still be zero because the audio_policy_volumes.xml is the source of
                 * truth. So the index value is modified here to match the flag setting
                 */
                if (volumeConfig.name.compare(audio_stream_type_to_string(AUDIO_STREAM_ALARM)) == 0
                      && deviceCat == DEVICE_CATEGORY_SPEAKER) {
                    for (auto &point : configCurve.curvePoints) {
                        if (point.index == 1) {
                            ALOGD("Mute alarm disabled: Found point with index 1. setting it to 0");
                            point.index = 0;
                            break;
                        }
                    }
                }
            }
            sp<VolumeCurve> curve = new VolumeCurve(deviceCat);
            for (auto &point : configCurve.curvePoints) {
                curve->add({point.index, point.attenuationInMb});
            }
            volumeGroup->add(curve);
        }
        return volumeGroup;
    };
    
//把VolumeGroup和Strategy对象进行相互关联
    auto addSupportedAttributesToGroup = [](auto &group, auto &volumeGroup, auto &strategy) {
        for (const auto &attr : group.attributesVect) {
            strategy->addAttributes({volumeGroup->getId(), group.stream, attr});
            volumeGroup->addSupportedAttributes(attr);
        }
    };
    auto checkStreamForGroups = [](auto streamType, const auto &volumeGroups) {
        const auto &iter = std::find_if(std::begin(volumeGroups), std::end(volumeGroups),
                                     [&streamType](const auto &volumeGroup) {
            const auto& streams = volumeGroup.second->getStreamTypes();
            return std::find(std::begin(streams), std::end(streams), streamType) !=
                    std::end(streams);
        });
        return iter != end(volumeGroups);
    };

    for (auto &volumeConfig : result.parsedConfig->volumeGroups) {
        // save default volume config for streams not defined in configuration
        if (volumeConfig.name.compare(audio_stream_type_to_string(AUDIO_STREAM_MUSIC)) == 0) {
            defaultVolumeConfig = volumeConfig;
        }
        if (volumeConfig.name.compare(audio_stream_type_to_string(AUDIO_STREAM_PATCH)) == 0) {
            defaultSystemVolumeConfig = volumeConfig;
        }
        //调用上面的loadVolumeConfig实现转换,VolumeGroup都放到mVolumeGroups集合中
        loadVolumeConfig(mVolumeGroups, volumeConfig);
    }
    for (auto& strategyConfig : result.parsedConfig->productStrategies) {
        sp<ProductStrategy> strategy = new ProductStrategy(strategyConfig.name);
        //遍历么个Strategy的attributeGroup
        for (const auto &group : strategyConfig.attributesGroups) {
          //mVolumeGroups寻找到StreamType和attributesGroup一样的
            const auto &iter = std::find_if(begin(mVolumeGroups), end(mVolumeGroups),
                                         [&group](const auto &volumeGroup) {
                                 //主要靠StreamType这个字符名字比较
                    return group.volumeGroup == volumeGroup.second->getName(); });
            sp<VolumeGroup> volumeGroup = nullptr;
            if (iter == end(mVolumeGroups)) {
     
            } else {
            //获取volumeGroups集合中StreamType和当前attributeGroup一样的VolumeGroup
                volumeGroup = iter->second;
            }
            if (group.stream != AUDIO_STREAM_DEFAULT) {
                // A legacy stream can be assigned once to a volume group
                LOG_ALWAYS_FATAL_IF(checkStreamForGroups(group.stream, mVolumeGroups),
                                    "stream %s already assigned to a volume group, "
                                    "review the configuration", toString(group.stream).c_str());
                volumeGroup->addSupportedStream(group.stream);
            }
            //这里就实现了VolumeGroup和Strategy的attributeGroup相互连接持有
            addSupportedAttributesToGroup(group, volumeGroup, strategy);
        }
        product_strategy_t strategyId = strategy->getId();
        mProductStrategies[strategyId] = strategy;
        ALOGE("processParsingResult %d",(int)strategyId);
    }
    mProductStrategies.initialize();
    return result;
}

对应的class的VolumeGroup构造方法

frameworks/av/services/audiopolicy/engine/common/src/VolumeGroup.cpp

//注意这里getNextHandle还会生成对应的id
VolumeGroup::VolumeGroup(const std::string &name, int indexMin, int indexMax) :
    mName(name), mId(static_cast<volume_group_t>(HandleGenerator<uint32_t>::getNextHandle())),
    mGroupVolumeCurves(VolumeCurves(indexMin, indexMax))
{
}

Strategy和VolumeGroup之间相关连接关键,前面也有讲解就是如下核心方法

auto addSupportedAttributesToGroup = [](auto &group, auto &volumeGroup, auto &strategy) {
    for (const auto &attr : group.attributesVect) {
       //strategy会关联volumeGroup
        strategy->addAttributes({volumeGroup->getId(), group.stream, attr});
       //volumeGroup会关联到对应的attr
        volumeGroup->addSupportedAttributes(attr);
    }
};

更多framework实战干货内容,请关注下面“千里马学框架”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值