背景:
在讲解音频的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实战干货内容,请关注下面“千里马学框架”

被折叠的 条评论
为什么被折叠?



