摘要:本文深入解析 Android Audio 系统中 Parameter-Framework(PFW)的实现原理与运行时机制。从程序模块关系出发,逐步拆解 AudioPolicyEngine 初始化、PFW 配置加载(ElementLibrary、Subsystem 插件、参数结构树、Domain 配置)的完整流程,并以 UsbHeadset 设备接入和录音请求路由两个场景为例,串联展示 PFW 如何通过 SelectionCriterion → ConfigurableDomain → AreaConfiguration → Blackboard → Syncer 的链路实现数据驱动的策略决策。全文以源码调用栈、PlantUML 类图和内存布局图辅助说明,适合有一定 Android Audio 底层基础的读者。
提示:建议在浏览器中打开原文链接,即可无损缩放查看文中 PlantUML 类图的细节。
1. 程序模块关系
程序模块简介
-
AudioPolicyManager (libaudiopolicymanagerdefault.so): Android 音频系统的策略中心,负责管理音频设备连接状态、处理音频路由请求并控制音量策略。
-
AudioPolicyEngine (libaudiopolicyengineconfigurable.so): AudioPolicyManager 内部的核心决策模块,根据当前系统状态(如设备连接情况、强制使用配置),通过 parameter-framework 读取 XML 配置来动态决定路由、音量、设备选择等策略。其核心特点是行为由数据驱动。
-
parameter-framework (libparameter.so): 参数管理与配置框架,提供一套层级化的参数结构(域、子系统、准则等),支持从 XML 加载配置、运行时参数变更与插件式扩展。在此它被用作 AudioPolicyEngine 的策略配置后端。
-
libpolicy-subsystem.so: 策略子系统插件,用于扩展 parameter-framework 的策略能力。它动态注册到 parameter-framework 中,提供具体的策略逻辑实现(如
InputSource路由规则)。本质上是一个可插拔的策略行为库。
2. 初始化流程
AudioPolicyManager::initialize()
│
│ //init_1: AudioPolicyEngine 库的选择和加载
├── engLib = EngineLibrary::load()
│
│ //init_2: 创建 AudioPolicyEngine 实例
├── mEngine = engLib->createEngine()
│ └── Engine::Engine()
│ │
│ │ //init_2.1: 创建 CParameterMgr 实例
│ ├── new ParameterManagerWrapper()
│ │ │ //strConfigurationFilePath :PFW 配置文件路径
│ │ └── new CParameterMgrPlatformConnector(strConfigurationFilePath)
│ │ └── new CParameterMgr(strConfigurationFilePath)
│ │
│ │ //init_2.2: 加载配置文件创建 CSelectionCriterionType 实例
│ │ // 和 CSelectionCriterion 实例
│ └── Engine::loadAudioPolicyEngineConfig()
│ ├── EngineBase::loadAudioPolicyEngineConfig()
│ └── ParameterManagerWrapper::addCriterion()
│ ├── CParameterMgr::createSelectionCriterionType()
│ │ //遍历 ValuePairs
│ ├── CSelectionCriterionType::addValuePair()
│ └── CParameterMgr::createSelectionCriterion()
│
│ // init_3. 加载 PFW 配置文件
└── mEngine->initCheck()
└── ParameterManagerWrapper::start()
└── CParameterMgrPlatformConnector::start()
└── CParameterMgr::load()
主要工作:
- 加载 AudioPolicyEngine 库
- 创建 AudioPolicyEngine 实例
- 2.1: 创建 CParameterMgr 实例
- 2.2: 加载配置文件,创建 CSelectionCriterionType 和 CSelectionCriterion 实例
- 加载 PFW 配置文件
PFW 初始化过程中会加载多种配置文件,理解这些配置内容在程序中的实例化方式,对掌握 PFW 很有帮助。
2.1. AudioPolicyEngine 库的选择和加载
在 AOSP 源码中,AudioPolicyEngine 有两个实现库:libaudiopolicyengineconfigurable.so 和 libaudiopolicyenginedefault.so。选用哪个取决于 audioPolicyConfiguration 配置,例如: /odm/etc/audio/audio_policy_configuration_a2dp_offload_disabled.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" engine_library="configurable" call_screen_mode_supported="true" />
</audioPolicyConfiguration>
配置文件里的 engine_library="configurable" 指定 AudioPolicyEngine 拼接为 libaudiopolicyengine + configurable + .so = libaudiopolicyengineconfigurable.so。
代码逻辑
Step 1: 加载配置 frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
AudioPolicyManager::AudioPolicyManager()
↓
AudioPolicyManager::loadConfig()
loadConfig()
└─ deserializeAudioPolicyXmlConfig()
│ // 获取配置文件路径,如
│ // /odm/etc/audio/audio_policy_configuration_a2dp_offload_disabled.xml
├─ audio_get_audio_policy_config_file()
│ // 解析 XML,构建 AudioPolicyConfig 实例
└─ deserializeAudioPolicyFile()
最终设置 AudioPolicyConfig.mEngineLibraryNameSuffix = "configurable"
Step 2: 加载 AudioPolicyEngine 库 frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
status_t AudioPolicyManager::initialize() {
{
auto engLib = EngineLibrary::load(
"libaudiopolicyengine" + getConfig().getEngineLibraryNameSuffix() + ".so");
mEngine = engLib->createEngine();
}
}
将 "libaudiopolicyengine" 与 AudioPolicyConfig.mEngineLibraryNameSuffix 的值拼接为文件名,然后加载 .so 文件并创建 Engine 实例。
2.2: 创建 AudioPolicyEngine 实例
代码调用栈如下:
AudioPolicyManager::initialize()
│
│ //init_1: AudioPolicyEngine 库的选择和加载
├── engLib = EngineLibrary::load()
│
│ //init_2: 创建 AudioPolicyEngine 实例
└── mEngine = engLib->createEngine()
||
EngineLibrary::createEngine()
└── EngineInterface* createEngineInstance()
└── EngineInstance::getInstance()
└── EngineInstance::queryInterface()
└── EngineInstance::getEngine()
└── static Engine engine
最终创建了一个 Engine 实例赋值给 AudioPolicyManager.mEngine
2.2.1: 创建 CParameterMgr 实例
Engine::Engine()
│
│ //init_2.1: 创建 CParameterMgr 实例
└── new ParameterManagerWrapper()
│ //strConfigurationFilePath : PFW 配置文件路径
└── new CParameterMgrPlatformConnector(strConfigurationFilePath)
└── new CParameterMgr(strConfigurationFilePath)
frameworks/av/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp
const char *const ParameterManagerWrapper::mPolicyPfwDefaultConfFileName =
"/etc/parameter-framework/ParameterFrameworkConfigurationPolicy.xml";
const char *const ParameterManagerWrapper::mPolicyPfwVendorConfFileName =
"/vendor/etc/parameter-framework/ParameterFrameworkConfigurationPolicy.xml";
ParameterManagerWrapper::ParameterManagerWrapper()
{
// Connector
if (access(mPolicyPfwVendorConfFileName, R_OK) == 0) {
mPfwConnector = new CParameterMgrPlatformConnector(mPolicyPfwVendorConfFileName);
} else {
mPfwConnector = new CParameterMgrPlatformConnector(mPolicyPfwDefaultConfFileName);
}
}
external/parameter-framework/upstream/parameter/ParameterMgr.cpp:
CParameterMgr::CParameterMgr(const string &strConfigurationFilePath, log::ILogger &logger)
: _pMainParameterBlackboard(new CParameterBlackboard),
_pElementLibrarySet(new CElementLibrarySet),
_xmlConfigurationUri(CXmlDocSource::mkUri(strConfigurationFilePath, "")), _logger(logger)
{
// Deal with children
addChild(new CParameterFrameworkConfiguration);
addChild(new CSelectionCriteria);
addChild(new CSystemClass(_logger));
addChild(new CConfigurableDomains);
}
PFW 配置文件路径 "/vendor/etc/parameter-framework/ParameterFrameworkConfigurationPolicy.xml" 被保存在 _xmlConfigurationUri。
CParameterMgr::CParameterMgr() 中创建了四大核心模块,构建 PFW 的层级结构:
- CParameterFrameworkConfiguration
- CSelectionCriteria
- CSystemClass
- CConfigurableDomains
这四个类对应 PFW 的四个维度:
| 类 | 代表 | 职责 |
|---|---|---|
| CParameterFrameworkConfiguration | 框架配置 | PFW 自身的运行参数 |
| CSelectionCriteria | 选择条件 | 定义配置选择的条件(如通话状态、设备类型),用于决定在何种场景下应用哪套配置 |
| CSystemClass | 系统类 | 参数结构的定义,包含所有 Subsystem 及其参数树,是 Blackboard 内存布局的依据 |
| CConfigurableDomains | 可配置域 | 参数配置的值集合,每个 Domain 包含多套 Configuration,由 SelectionCriteria 决定哪套生效 |
层级关系
CParameterMgr (根)
├── CParameterFrameworkConfiguration ← "框架怎么运行"
├── CSelectionCriteria ← "什么条件下选哪套配置"
├── CSystemClass ← "有哪些参数" (结构定义)
│ └── CSubsystem "Policy"
│ └── 参数树...
└── CConfigurableDomains ← "参数的值是什么" (配置数据)
└── CConfigurableDomain "Phone"
├── CDomainConfiguration "Normal" ← 非通话时的参数值
└── CDomainConfiguration "InCall" ← 通话时的参数值
CParameterFrameworkConfiguration 管理框架行为,CSelectionCriteria 管理条件选择,CSystemClass 管理参数结构(有什么参数),CConfigurableDomains 管理参数取值(参数值是什么),四者共同实现"在什么条件下,给哪些参数,赋什么值"的功能。
2.2.2: 加载配置文件创建 CSelectionCriterionType 实例和 CSelectionCriterion 实例
Engine::loadAudioPolicyEngineConfig()
├── EngineBase::loadAudioPolicyEngineConfig()
└── ParameterManagerWrapper::addCriterion()
├── CParameterMgr::createSelectionCriterionType()
│ //遍历 ValuePairs
├── CSelectionCriterionType::addValuePair()
└── CParameterMgr::createSelectionCriterion()
配置文件路径的定义: frameworks/av/services/audiopolicy/engine/config/include/EngineConfig.h
constexpr char DEFAULT_PATH[] = "/vendor/etc/audio_policy_engine_configuration.xml";
配置文件内容: audio_policy_engine_configuration.xml
<configuration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="audio_policy_engine_product_strategies.xml"/>
<xi:include href="audio_policy_engine_criterion_types.xml"/>
<xi:include href="vendor_audio_policy_engine_criteria.xml"/>
<xi:include href="audio_policy_engine_stream_volumes.xml"/>
<xi:include href="audio_policy_engine_default_stream_volumes.xml"/>
</configuration>
audio_policy_engine_criterion_types.xml:
<criterion_types>
<criterion_type name="OutputDevicesMaskType" type="inclusive">
<values>
<value numerical="1" literal="Earpiece" android_type="0x1"/>
...
<criterion_type name="InputDevicesMaskType" type="inclusive">
<values>
<value numerical="1" literal="Communication" android_type="0x80000001"/>
<value numerical="2" literal="Ambient" android_type="0x80000002"/>
...
<value numerical="33554432" literal="UsbHeadset" android_type="0x82000000"/>
...
vendor_audio_policy_engine_criteria.xml:
<criteria>
<criterion name="AvailableInputDevices" type="InputDevicesMaskType" default="none"/>
<criterion name="AvailableOutputDevices" type="OutputDevicesMaskType" default="none"/>
...
</criteria>
配置实例化结果:
3. 加载 PFW 配置文件
Engine 和 CParameterMgr 创建完成后,程序来到了 mEngine->initCheck() ---> CParameterMgr::load()。
CParameterMgr::load()
├── feedElementLibraries()
├── loadFrameworkConfiguration()
├── loadSubsystems()
├── loadStructure()
└── loadSettings()
3.1 feedElementLibraries()
PFW 定义了三种元素库(ElementLibrary)枚举,分别对应三个 XML 解析阶段,每个阶段用不同的 Builder 映射表来创建元素: external/parameter-framework/upstream/parameter/ParameterMgr.h
class CParameterMgr : private CElement
{
enum ElementLibrary
{
EFrameworkConfigurationLibrary, //0
EParameterCreationLibrary, //1
EParameterConfigurationLibrary //2
};
private:
CElementLibrarySet *_pElementLibrarySet;
}
_pElementLibrarySet 是三种 ElementLibrary 的容器,通过 vector<CElementLibrary*> 按索引存储,与前面的枚举一一对应:
_pElementLibrarySet[0] = EFrameworkConfigurationLibrary → 框架配置 Builder 映射表
_pElementLibrarySet[1] = EParameterCreationLibrary → 参数结构 Builder 映射表
_pElementLibrarySet[2] = EParameterConfigurationLibrary → 参数配置 Builder 映射表
| 枚举值 | 对应解析阶段 | 用途 |
|---|---|---|
EFrameworkConfigurationLibrary | 解析框架配置 XML | 创建 CParameterFrameworkConfiguration、CSubsystemPlugins 等顶层配置元素 |
EParameterCreationLibrary | 解析结构定义 XML(Subsystem 结构) | 创建 CSubsystem、CComponentType、CComponentInstance 等参数结构元素 |
EParameterConfigurationLibrary | 解析参数配置 XML(Domain 配置) | 创建 CConfigurableDomain、CDomainConfiguration 等可配置域元素 |
针对 XML 标签,将 Builder 注册到三种 CElementLibrary 中。
external/parameter-framework/upstream/parameter/ParameterMgr.cpp
// Dynamic creation library feeding
void CParameterMgr::feedElementLibraries()
{
// Global Configuration handling
auto pFrameworkConfigurationLibrary = new CElementLibrary;
pFrameworkConfigurationLibrary->addElementBuilder(
"ParameterFrameworkConfiguration",
new TElementBuilderTemplate<CParameterFrameworkConfiguration>());
pFrameworkConfigurationLibrary->addElementBuilder(
"SubsystemPlugins", new TKindElementBuilderTemplate<CSubsystemPlugins>());
......
/* 加入到 _pElementLibrarySet[0] */
_pElementLibrarySet->addElementLibrary(pFrameworkConfigurationLibrary);
// Parameter creation
auto pParameterCreationLibrary = new CElementLibrary;
pParameterCreationLibrary->addElementBuilder(
"Subsystem", new CSubsystemElementBuilder(getSystemClass()->getSubsystemLibrary()));
pParameterCreationLibrary->addElementBuilder(
"ComponentType", new TNamedElementBuilderTemplate<CComponentType>());
pParameterCreationLibrary->addElementBuilder(
"Component", new TNamedElementBuilderTemplate<CComponentInstance>());
......
/* 加入到 _pElementLibrarySet[1] */
_pElementLibrarySet->addElementLibrary(pParameterCreationLibrary);
// Parameter Configuration Domains creation
auto pParameterConfigurationLibrary = new CElementLibrary;
pParameterConfigurationLibrary->addElementBuilder(
"ConfigurableDomain", new TElementBuilderTemplate<CConfigurableDomain>());
pParameterConfigurationLibrary->addElementBuilder(
"Configuration", new TNamedElementBuilderTemplate<CDomainConfiguration>());
pParameterConfigurationLibrary->addElementBuilder("CompoundRule",
new TElementBuilderTemplate<CCompoundRule>());
pParameterConfigurationLibrary->addElementBuilder(
"SelectionCriterionRule", new TElementBuilderTemplate<CSelectionCriterionRule>());
/* 加入到 _pElementLibrarySet[2] */
_pElementLibrarySet->addElementLibrary(pParameterConfigurationLibrary);
}
注册结果示例:
_pElementLibrarySet[EParameterCreationLibrary] 中增加了 CElementLibrary::_elementBuilderMap["Subsystem"] = CSubsystemElementBuilder。
使用时:
- 将 ElementLibrary 枚举值传入 xmlParse()
bool CParameterMgr::loadStructure(string &strError)
{
xmlParse(parameterBuildContext, pSystemClass, doc, structureUri,
EParameterCreationLibrary)
}
- xmlParse() 中按枚举值取出对应的 Library
bool CParameterMgr::xmlParse(CXmlElementSerializingContext &elementSerializingContext,
CElement *pRootElement, _xmlDoc *doc, const string &baseUri,
CParameterMgr::ElementLibrary eElementLibrary, bool replace,
const string &strNameAttributeName)
{
// Init serializing context
elementSerializingContext.set(_pElementLibrarySet->getElementLibrary(eElementLibrary), baseUri);
}
3.2 loadFrameworkConfiguration()
loadFrameworkConfiguration() 主要做了以下工作:
- 解析 ParameterFrameworkConfigurationPolicy.xml
<?xml version="1.0" encoding="UTF-8"?>
<ParameterFrameworkConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
SystemClassName="Policy" TuningAllowed="false">
<SubsystemPlugins>
<Location Folder="">
<Plugin Name="libpolicy-subsystem.so"/>
</Location>
</SubsystemPlugins>
<StructureDescriptionFileLocation Path="Structure/Policy/audio_PolicyClass.xml"/>
<SettingsConfiguration>
<ConfigurableDomainsFileLocation Path="Settings/Policy/PolicyConfigurableDomains.xml"/>
</SettingsConfiguration>
</ParameterFrameworkConfiguration>
- 将 CSystemClass 和 CConfigurableDomains 的名称设为配置文件中的
SystemClassName属性值 - 将 SubsystemPlugins 配置的解析结果存入
_pSubsystemPlugins,供后续加载插件库使用
3.3 loadSubsystems()
loadSubsystems() 的主要作用是加载插件 .so,并注册插件中的 PolicySubsystemBuilder:
CParameterMgr::loadSubsystems()
│
└── CSystemClass::loadSubsystems(_pSubsystemPlugins) //见⓵
│
└── CSystemClass::loadSubsystemsFromSharedLibraries()
│
└── CSystemClass::loadPlugins() //见⓶
└── 加载插件 .so,注册插件里的 PolicySubsystemBuilder
⓵ _pSubsystemPlugins 的来源
_pSubsystemPlugins 的值来自配置文件 ParameterFrameworkConfigurationPolicy.xml 中的 SubsystemPlugins:
<SubsystemPlugins>
<Location Folder="">
<Plugin Name="libpolicy-subsystem.so"/>
</Location>
</SubsystemPlugins>
即 _pSubsystemPlugins = "libpolicy-subsystem.so"
⓶ 注册 PolicySubsystemBuilder
PolicySubsystem 作为扩展 Subsystem,在动态加载的插件 libpolicy-subsystem.so 中实现。
external/parameter-framework/upstream/parameter/SystemClass.cpp
const char CSystemClass::entryPointSymbol[] =
MACRO_TO_STR(PARAMETER_FRAMEWORK_PLUGIN_ENTRYPOINT_V1);
using PluginEntryPointV1 = void (*)(CSubsystemLibrary *, core::log::Logger &);
bool CSystemClass::loadPlugins(list<string> &lstrPluginFiles, core::Results &errors)
{
try {
auto library = utility::make_unique<DynamicLibrary>(strPluginFileName);
// Load symbol from library
auto subSystemBuilder = library->getSymbol<PluginEntryPointV1>(entryPointSymbol);
// Store libraries handles
_subsystemLibraryHandleList.push_back(std::move(library));
// Fill library
subSystemBuilder(_pSubsystemLibrary, _logger);
}
}
- 使用
libpolicy-subsystem.so创建DynamicLibrary实例 - 从
libpolicy-subsystem.so获取名为PARAMETER_FRAMEWORK_PLUGIN_ENTRYPOINT_V1的函数地址,赋值给subSystemBuilder - 调用
subSystemBuilder函数,即libpolicy-subsystem.so中的PARAMETER_FRAMEWORK_PLUGIN_ENTRYPOINT_V1
libpolicy-subsystem.so 中 PARAMETER_FRAMEWORK_PLUGIN_ENTRYPOINT_V1 函数的实现:
frameworks/av/services/audiopolicy/engineconfigurable/parameter-framework/plugin/PolicySubsystemBuilder.cpp
static const char *const POLICY_SUBSYSTEM_NAME = "Policy";
void PARAMETER_FRAMEWORK_PLUGIN_ENTRYPOINT_V1(CSubsystemLibrary *subsystemLibrary, core::log::Logger& logger)
{
subsystemLibrary->addElementBuilder(POLICY_SUBSYSTEM_NAME,
new TLoggingElementBuilderTemplate<PolicySubsystem>(logger));
}
将 PolicySubsystem 的 Builder 以 "Policy" 为 key 注册到 CSubsystemLibrary(即 CSystemClass._pSubsystemLibrary),使得 XML 中 <Subsystem Type="Policy"> 能被创建为 PolicySubsystem 对象。
对应的 XML 配置:
vendor/etc/parameter-framework/Structure/Policy/audio_PolicySubsystem.xml
<Subsystem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xi="http://www.w3.org/2001/XInclude"
xsi:noNamespaceSchemaLocation="Schemas/Subsystem.xsd"
Name="policy" Type="Policy">
CSubsystemLibrary 继承于 CElementLibrary,CElementLibrary 默认是按 XML 标签名匹配:
external/parameter-framework/upstream/parameter/ElementLibrary.cpp
std::string CElementLibrary::getBuilderType(const CXmlElement &xmlElement) const
{
return xmlElement.getType(); // 返回 XML 标签名
}
其他 Builder 都是按标签名注册的,例如:
| addElementBuilder 的第一个参数 | XML 标签 | 对应的 Builder |
|---|---|---|
"Subsystem" | <Subsystem ...> | CSubsystemElementBuilder |
"SubsystemInclude" | <SubsystemInclude ...> | CFileIncluderElementBuilder |
当解析器遇到 <SubsystemInclude Path="..."> 时,getBuilderType() 返回 "SubsystemInclude",直接命中 CFileIncluderElementBuilder。
但是,CSubsystemLibrary 重写了 getBuilderType():
std::string getBuilderType(const CXmlElement &xmlElement) const override
{
std::string type;
xmlElement.getAttribute("Type", type); // 读取 Type 属性,而不是标签名
return type;
}
所以 CSubsystemLibrary 按 Type 属性值匹配。
CSystemClass._pSubsystemLibrary 中注册的是:
| 注册的 key | XML Type 属性值 | 对应的 Builder |
|---|---|---|
"Virtual" | Type="Virtual" | TLoggingElementBuilderTemplate<CVirtualSubsystem> |
"Policy" | Type="Policy" | TLoggingElementBuilderTemplate<PolicySubsystem> |
注:
"Virtual"是CSubsystemLibrary的默认 fallback builder(通过CDefaultElementLibrary的模板参数指定),"Policy"由插件libpolicy-subsystem.so注册。
3.4 loadStructure()
调用栈:
CParameterMgr::loadStructure()
│
└─ CParameterMgr::xmlParse(pSystemClass, EParameterCreationLibrary)
│
├─ 设置上下文
│ elementSerializingContext.set(pParameterCreationLibrary, baseUri)
│ pRootElement = pSystemClass
│
└─ CXmlMemoryDocSink::doProcess()
│
└─ CConfigurableElement::fromXml() // CSystemClass 继承自 CConfigurableElement
│
└─ CElement::fromXml() // 递归遍历XML子节点
│
│ CSystemClass::childrenAreDynamic() → true // 走 createChild 分支
│
├─ CElement::createChild(childElement) // 遇到 <Subsystem> 标签
│ │
│ ├─ 从上下文获取 ElementLibrary
│ │ elementSerializingContext.getElementLibrary()
│ │ → EParameterCreationLibrary
│ │
│ ├─ pParameterCreationLibrary->createElement(childElement)
│ │ │
│ │ └─ CElementLibrary::createElement()
│ │ │ _elementBuilderMap["Subsystem"] → CSubsystemElementBuilder
│ │ │
│ │ └─ CSubsystemElementBuilder::createElement()
│ │ │
│ │ └─ CSubsystemLibrary::createElement() // 委托给子系统库
│ │ │
│ │ └─ CElementLibrary::createElement()
│ │ │
│ │ ├─ CSubsystemLibrary::getBuilderType()
│ │ │ 从 Type 属性获取 → "Policy"
│ │ │
│ │ ├─ _elementBuilderMap["Policy"] → PolicySubsystemBuilder
│ │ │
│ │ └─ TLoggingElementBuilderTemplate::createElement()
│ │ new PolicySubsystem(name, logger)
│ │
│ └─ CSystemClass::addChild(PolicySubsystem*) // 挂到 CSystemClass 下
│
└─ pChild->fromXml()
||
PolicySubsystem::fromXml()
||
CConfigurableElement::fromXml()
└─ CSubsystem::structureFromXml() //解析 XML,构建参数树填充 PolicySubsystem
loadStructure() 里主要功能是:
- 创建"libpolicy-subsystem.so"里的 PolicySubsystem 实例,并作为一个 child CElement 挂到 CSystemClass 下
- CSubsystem::structureFromXml() 解析 XML,构建参数树填充 PolicySubsystem
CSubsystem::structureFromXml()
CSubsystem::structureFromXml() 对应的配置文件是: audio_PolicySubsystem.xml
audio_PolicySubsystem.xml 包含了(include) 另外两个配置文件:
- audio_PolicySubsystem-CommonTypes.xml
- ProductStrategies.xml
配置文件内容节选: audio_PolicySubsystem-CommonTypes.xml:
<ComponentTypeSet
<ComponentType Name="InputDevicesMask" Description="64bit representation of devices">
<BitParameterBlock Name="mask" Size="64">
<BitParameter Name="communication" Size="1" Pos="0"/>
<BitParameter Name="usb_headset" Size="1" Pos="25"/>
...
</BitParameterBlock>
</ComponentType>
<ComponentType Name="InputSource">
<Component Name="applicable_input_device" Type="InputDevicesMask" Mapping="InputSource" Description="Selected Input device"/>
</ComponentType>
</ComponentTypeSet>
audio_PolicySubsystem.xml:
<Subsystem Name="policy" Type="Policy">
<!-- 第一步:定义 ComponentType(类型模板) -->
<ComponentLibrary>
<ComponentType Name="InputSources">
<Component Name="mic" Type="InputSource" Mapping="Name:AUDIO_SOURCE_MIC"/>
...
</ComponentType>
</ComponentLibrary>
<!-- 第二步:声明要实例化哪些 ComponentType -->
<InstanceDefinition>
<Component Name="streams" Type="Streams"/>
<Component Name="input_sources" Type="InputSources"/>
<Component Name="product_strategies" Type="ProductStrategies"/>
</InstanceDefinition>
</Subsystem>
CSubsystem::structureFromXml() 主要做了三件事:
CSubsystem::structureFromXml()
│
│ //Step 1: 解析 XML,构建类型树(ComponentLibrary/InstanceDefinition)
├─ _pComponentLibrary->fromXml()
├─ _pInstanceDefinition->fromXml()
│
│ //Step 2: 调用 createInstances(),构建 component 实例树
├─ _pInstanceDefinition->createInstances(this)
│
│ //Step 3: 将 XML 中的 Mapping 属性关联到 CInstanceConfigurableElement,
│ //创建对应的 CSubsystemObject,建立
│ //CInstanceConfigurableElement ↔ CSubsystemObject 的关联
└── CSubsystem::mapSubsystemElements()
流程示意图和实例化后参数树:
其中 CSubsystem::mapSubsystemElements() 的详细流程如下:
CSubsystem::structureFromXml
│ // 解析完所有xml配置后
└── CSubsystem::mapSubsystemElements()
│ // 遍历 CInstanceConfigurableElement, 传入 CSubsystem : IMapper
└── CInstanceConfigurableElement::map(IMapper &mapper)
├── CSubsystem::mapBegin(CInstanceConfigurableElement *)
└── CSubsystem::handleSubsystemObjectCreation(CInstanceConfigurableElement *)
│ // 遍历 _subsystemObjectCreatorArray 里的 CSubsystemObjectCreator
├── string strKey = pSubsystemObjectCreator->getMappingKey()
│ // 判断当前CInstanceConfigurableElement实例节点的 Mapping 数据是否包含
│ // CSubsystemObjectCreator 的 _strMappingKey, 有则匹配成功,准备创建
│ // 对应的CSubsystemObject对象
├── pInstanceConfigurableElement->getMappingData(strKey, pStrValue)
├── TSubsystemObjectFactory::objectCreate
├── new InputSource(strMappingValue, pInstanceConfigurableElement, context, logger)
│ ├── CFormattedSubsystemObject::CFormattedSubsystemObject()
│ └── CSubsystemObject::CSubsystemObject(pInstanceConfigurableElement)
│ // 将 InputSource 实例传入
│ CInstanceConfigurableElement::setSyncer(ISyncer *pSyncer)
└── _subsystemObjectList.push_back()
对应示意图:
3.5 loadSettings()
loadSettings() 加载的是 PolicyConfigurableDomains.xml。这是 PFW 的 Settings 层,是整个音频路由决策的核心规则库。文件定义了所有 ConfigurableDomain,每个 domain 是一组互斥的路由规则。
3.5.1 文件整体结构
PolicyConfigurableDomains.xml
│
├── DeviceForInputSource.* 输入源 → 输入设备 路由规则
│ ├── Calibration 永久清零不可用的 bits("黑名单")
│ ├── DefaultAndMic default / mic 的设备选择
│ ├── VoiceUplinkAndVoiceDownlinkAndVoiceCall
│ ├── VoicePerformance
│ ├── Camcorder
│ ├── VoiceRecognitionAndUnprocessedAndHotword
│ ├── VoiceCommunication ← 关键域,下文详细分析
│ ├── RemoteSubmix
│ └── FmTuner
│
├── VolumeProfilesForStream.* stream → 音量曲线映射
│
└── DeviceForProductStrategy.* 输出策略 → 输出设备 路由规则
├── Media.*
├── Accessibility.*
├── Dtmf.*
├── EnforcedAudible.*
├── Phone.*
├── Sonification.*
├── SonificationRespectful.*
└── TransmittedThroughSpeaker.*
文件内容节选:
<ConfigurableDomains SystemClassName="Policy">
<ConfigurableDomain Name="DeviceForInputSource.VoiceCommunication" SequenceAware="false">
<Configurations>
<Configuration Name="UsbHeadset">
<CompoundRule Type="All">
<SelectionCriterionRule SelectionCriterion="ForceUseForCommunication" MatchesWhen="Is" Value="ForceNone"/>
<SelectionCriterionRule SelectionCriterion="AvailableInputDevices" MatchesWhen="Includes" Value="UsbHeadset"/>
</CompoundRule>
</Configuration>
...
<ConfigurableElements>
<ConfigurableElement Path="/Policy/policy/input_sources/voice_communication/applicable_input_device/mask/bluetooth_sco_headset"/>
<ConfigurableElement Path="/Policy/policy/input_sources/voice_communication/applicable_input_device/mask/wired_headset"/>
<ConfigurableElement Path="/Policy/policy/input_sources/voice_communication/applicable_input_device/mask/usb_headset"/>
<ConfigurableElement Path="/Policy/policy/input_sources/voice_communication/applicable_input_device/mask/builtin_mic"/>
<ConfigurableElement Path="/Policy/policy/input_sources/voice_communication/applicable_input_device/mask/back_mic"/>
</ConfigurableElements>
<Settings>
<Configuration Name="UsbHeadset">
<ConfigurableElement Path="/Policy/policy/input_sources/voice_communication/applicable_input_device/mask/bluetooth_sco_headset">
<BitParameter Name="bluetooth_sco_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/input_sources/voice_communication/applicable_input_device/mask/wired_headset">
<BitParameter Name="wired_headset">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/input_sources/voice_communication/applicable_input_device/mask/usb_headset">
<BitParameter Name="usb_headset">1</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/input_sources/voice_communication/applicable_input_device/mask/builtin_mic">
<BitParameter Name="builtin_mic">0</BitParameter>
</ConfigurableElement>
<ConfigurableElement Path="/Policy/policy/input_sources/voice_communication/applicable_input_device/mask/back_mic">
<BitParameter Name="back_mic">0</BitParameter>
</ConfigurableElement>
</Configuration>
...
</Settings>
</ConfigurableDomain>
</ConfigurableDomains>
3.5.2 配置实例化
配置实例化后的结果如图:
3.5.3 CParameterMgr._pMainParameterBlackboard 和 AreaConfiguration
3.5.3.1 CParameterMgr._pMainParameterBlackboard
CParameterMgr._pMainParameterBlackboard 是 CParameterBlackboard * 类型的全局唯一变量,存储当前生效的全部参数值。
CParameterBlackboard 字面意思是参数存储的"黑板",本质是一个字节缓冲区,存储参数的原始二进制数据:
std::vector<uint8_t> mBlackboard; // 内部就是一个 byte 数组
CParameterMgr::loadStructure() 解析完 XML 后对 _pMainParameterBlackboard 进行初始化:
CParameterMgr::loadStructure() {
xmlParse();
// Initialize offsets
pSystemClass->setOffset(0);
// Initialize main blackboard's size
_pMainParameterBlackboard->setSize(pSystemClass->getFootPrint());
}
先看个类关系图:
Level 1 (顶层): CConfigurableElement
│
┌─────────────────┴─────────────────┐
│ │
Level 2: CSystemClass [final] CInstanceConfigurableElement
(终点,无法继续派生) │
┌──────────┴──────────┐
│ │
Level 3: CComponent CBitParameterBlock
└── getFootPrint() = CBitParameterBlockType::getSize()
pSystemClass->setOffset(0)
CConfigurableElement::setOffset(0)
所以 pSystemClass->setOffset(0) 实际调用的是 CConfigurableElement::setOffset(),内部递归给所有子元素分配偏移:
void CConfigurableElement::setOffset(size_t offset)
{
_offset = offset; // 设置自身偏移
for (size_t index = 0; index < getNbChildren(); index++) {
pConfigurableElement->setOffset(offset); // 子元素偏移 = 当前offset
offset += pConfigurableElement->getFootPrint(); // offset 累加子元素占用的字节数
}
}
size_t CConfigurableElement::getFootPrint() const
{
size_t uiSize = 0;
size_t uiNbChildren = getNbChildren();
for (size_t index = 0; index < uiNbChildren; index++) {
const CConfigurableElement *pConfigurableElement =
static_cast<const CConfigurableElement *>(getChild(index));
uiSize += pConfigurableElement->getFootPrint();
}
return uiSize;
}
其中,getFootPrint() 返回 CConfigurableElement 在 Blackboard(参数内存)中占用的字节数。
效果:从 SystemClass 开始,按深度优先顺序给整棵元素树分配连续的内存偏移,每个子元素的 offset = 前面所有兄弟的 Footprint 之和,形成一块连续的参数内存布局(Blackboard)。
分配完offset后,就可以得到所有子元素所占用的内存大小总和,然后通过
_pMainParameterBlackboard->setSize(pSystemClass->getFootPrint())
设置 _pMainParameterBlackboard 的 size。
最后,所有实例在_pMainParameterBlackboard中的offset和footprint:
SystemClass (offset=0)
└── PolicySubsystem (offset=0, footprint=x)
├── CComponent:streams (offset=0, footprint=a=14x4=56])
├── CComponent:input_sources (offset=a, footprint=14x8=112)
│ ├── CComponent:default (offset=a, footprint = 8)
│ │ └── CComponent:applicable_input_device (offset=a, footprint = 8)
│ │ └── CBitParameterBlock:mask (offset=a, footprint = 8)
│ │ ├── CBitParameter:communication (offset=a, footprint = 0)
│ │ ├── CBitParameter:ambient (offset=a, footprint = 0)
│ │ ├── CBitParameter:builtin_mic
│ │ └── ...
│ ├── CComponent:mic (offset=a + 8, footprint = 8)
│ │ └── CComponent:applicable_input_device (offset=a + 8, footprint = 8)
│ │ └── CBitParameterBlock:mask (offset=a + 8, footprint = 8)
│ │ ├── CBitParameter:communication (offset=a + 8, footprint = 0)
│ │ ├── CBitParameter:ambient (offset=a + 8, footprint = 0)
│ │ ├── CBitParameter:builtin_mic (offset=a + 8, footprint = 0)
│ │ └── ...
│ ├── ...
│ ├── CComponent:voice_communication
│ └── ...
└── CComponent:product_strategies (offset=a+112, footprint=?)
内存分布示意图:
0x00 ┌────────────────────┬──────────────────────────────────────────────────────────────┬─────────────────┐
│ │ streams │ ... │ │
│ ├────────────────────┼──────────────────────┬─────────────────────────┬─────────────┼─────────────────┤
│ │ │ default │ applicable_input_device │ mask 64 bit │0: communication │
│ │ │ │ │ ├─────────────────┤
│ │ │ │ │ │1: ambient │
│ │ │ │ │ ├─────────────────┤
│ │ │ │ │ │ ... │
│ │ │ │ │ ├─────────────────┤
│ │ │ │ │ │25: usb_headset │
│ │ │ │ │ ├─────────────────┤
│ │ │ │ │ │63: ... │
│ │ ├──────────────────────┼─────────────────────────┼─────────────┼─────────────────┤
│ │ │ mic │ applicable_input_device │ mask 64 bit │ │
│ │ ├──────────────────────┼─────────────────────────┼─────────────┼─────────────────┤
│ │ │ voice_uplink │ applicable_input_device │ mask 64 bit │ │
│ │ input_sources ├──────────────────────┼─────────────────────────┼─────────────┼─────────────────┤
│ │ │ ... │ ... │ ... │ │
│ │ ├──────────────────────┼─────────────────────────┼─────────────┼─────────────────┤
│ │ │ voice_communication │ applicable_input_device │ mask 64 bit │ │
│ │ ├──────────────────────┼─────────────────────────┼─────────────┼─────────────────┤
│ │ │ ... │ ... │ ... │ │
│ ├────────────────────┼──────────────────────┴─────────────────────────┴─────────────┼─────────────────┤
│ │ product_strategies │ ... │ │
↓ └────────────────────┴──────────────────────────────────────────────────────────────┴─────────────────┘
pSystemClass->getFootPrint()
3.5.3.2 AreaConfiguration
在 PFW 中,AreaConfiguration 表示一个"配置域" —— 一组参数值的预设方案。
参数值存于 _blackboard:
经过 CParameterMgr::loadSettings() 解析 PolicyConfigurableDomains.xml 后,得到 CBitwiseAreaConfiguration(即 AreaConfiguration),例如:
DeviceForInputSource.VoiceCommunication : CConfigurableDomain
UsbHeadset : CDomainConfiguration
mAreaConfigurationList:
bluetooth_sco_headset : CBitwiseAreaConfiguration = 0
wired_headset : CBitwiseAreaConfiguration = 0
usb_headset : CBitwiseAreaConfiguration = 0x2000000
builtin_mic : CBitwiseAreaConfiguration = 0
back_mic : CBitwiseAreaConfiguration = 0
在 VoiceCommunication 场景下,UsbHeadset 的 mAreaConfigurationList 中的 CBitwiseAreaConfiguration 表示当使用 USB 耳机作为输入设备时,各物理区域(麦克风位置)的使能状态。
3.5.3.3 两者关系
┌────────────────────────────────────────────────┐
│ CParameterMgr._pMainParameterBlackboard │
│ (全局唯一, 存储当前生效的参数值) │
│ ...[wired_headset][usb_headset][builtin_mic]...│
└────────────────────────────────────────────────┘
↓ save() ↑ restore()
│ │
┌────────┴─────────┐ ┌────────┴─────────┐
│ AreaConfiguration│ │ AreaConfiguration│
│ "usb_headset" │ │ "builtin_mic" │
│ ┌────────────┐ │ │ ┌────────────┐ │
│ │ _blackboard│ │ │ │ _blackboard│ │
│ └────────────┘ │ │ └────────────┘ │
└──────────────────┘ └──────────────────┘
save() 和 restore() 的源码:
// Save data from current
void CAreaConfiguration::save(const CParameterBlackboard *pMainBlackboard)
{
copyFrom(pMainBlackboard, _pConfigurableElement->getOffset());
}
// Apply data to current
bool CAreaConfiguration::restore(CParameterBlackboard *pMainBlackboard, bool bSync,
core::Results *errors) const
{
assert(_bValid);
copyTo(pMainBlackboard, _pConfigurableElement->getOffset());
// Synchronize if required
return !bSync || _pSyncerSet->sync(*pMainBlackboard, false, errors);
}
- CAreaConfiguration::save() 从
CParameterMgr._pMainParameterBlackboard拷贝数据到自己的_blackboard(保存当前配置) - CAreaConfiguration::restore() 把自己的
_blackboard数据拷回CParameterMgr._pMainParameterBlackboard(恢复配置)
其中使用的 offset 通过 _pConfigurableElement->getOffset() 获取,例如 usb_headset :CBitwiseAreaConfiguration 从 usb_headset : CBitParameter 获取 offset。
4. UsbHeadset 设备接入流程
如 3.5.2 节「PolicyConfigurableDomains 配置实例化」图中 Step 4 所示,当 UsbHeadset 设备接入时,调用栈如下:
AudioPolicyManager::setDeviceConnectionState()
↓
AudioPolicyManager::setEngineDeviceConnectionState()
↓
Engine::setDeviceConnectionState()
↓
ParameterManagerWrapper::setAvailableInputDevices()
↓
CSelectionCriterion::setCriterionState(1<<25)
将 AvailableInputDevices._iState 设置为 0x2000000 (1 << 25)
然后开始应用配置:
ParameterManagerWrapper::setAvailableInputDevices()
├── CSelectionCriterion::setCriterionState(1<<25)
│
└── ParameterManagerWrapper::applyPlatformConfiguration()
↓
CParameterMgr::applyConfigurations()
└── doApplyConfigurations(false)
├── CSystemClass::checkForSubsystemsToResync() ← 处理需要 resync 的子系统
└── CConfigurableDomains::apply(_pMainParameterBlackboard, syncerSet) ← 应用配置域
│ //遍历CConfigurableDomain实例
├── CConfigurableDomain::apply(pSyncerSet) ["DeviceForInputSource.VoiceCommunication"]
│ ├── CConfigurableDomain::findApplicableDomainConfiguration() 返回一个CDomainConfiguration
│ │ │
│ │ │ // Step 4.2 查找 Rule 匹配的 CDomainConfiguration
│ │ │
│ │ │ // 遍历所有DomainConfiguration,调用其isApplicable()方法判断是否适用
│ │ └── CDomainConfiguration::isApplicable() //UsbHeadset
│ │ └── CCompoundRule::matches() "All"
│ │ │ // 遍历所有 SelectionCriterionRule:
│ │ │ // ForceUseForCommunication, Is, ForceNone
│ │ │ // AvailableInputDevices, Includes, UsbHeadset
│ │ └── CSelectionCriterionRule::matches()
│ │ │ // 根据 MatchesWhen 的值,调用CSelectionCriterion
│ │ │ // 的is/isNot/includes/excludes方法判断是否满足规则
│ │ └── CSelectionCriterion::is/isNot/includes/excludes()
│ │ └── 根据_iState的值做判断
│ │ bool bSync = !pSyncerSet && _bSequenceAware = false
│ │
│ │ // Step 4.3: 将匹配到 CompoundRule 的 CDomainConfiguration 的配置值
│ │ // restore 到 CParameterMgr._pMainParameterBlackboard
│ │
│ ├── CDomainConfiguration::restore(pParameterBlackboard, false) //"UsbHeadset"
│ │ │ // 遍历 mAreaConfigurationList
│ │ └── CAreaConfiguration::restore(pParameterBlackboard, bSync=false) //"usb_headset"
│ │ ├── CBitParameter::getOffset() //"usb_headset"
│ │ ├── copyTo(mainBlackboard) ← 把配置值拷入CParameterMgr._pMainParameterBlackboard
│ │ └── 因为 bSync=false,所以不执行 SyncerSet::sync()
│ └── *pSyncerSet += _syncerSet //voice_communication:InputSource
│
│ // Step 4.4: 将状态同步到 Engine
│
│ // Synchronize those collected syncers
└── syncerSet.sync(*pParameterBlackboard, false, nullptr)
│ for each ISyncer:
└── ISyncer::sync() ← 虚函数调用
= CSubsystemObject::sync(pParameterBlackboard, bBack=false)
├── _blackboard = ¶meterBlackboard //赋值了CParameterMgr._pMainParameterBlackboard
└── CSubsystemObject::accessHW()
└── InputSource::sendToHW() //voice_communication:InputSource
│ // 从CParameterMgr._pMainParameterBlackboard读取可用设备
├── CSubsystemObject::blackboardRead()
└── Engine::setDeviceForInputSource(7, 0x2000000)
└── Engine::setPropertyForKey()
其中 Step 4.2 匹配到:
<SelectionCriterionRule SelectionCriterion="AvailableInputDevices" MatchesWhen="Includes" Value="UsbHeadset"/>
进而匹配到包含以上 SelectionCriterionRule 的CompoundRule 如:
<CompoundRule Type="All">
<SelectionCriterionRule SelectionCriterion="ForceUseForCommunication" MatchesWhen="Is" Value="ForceNone"/>
<SelectionCriterionRule SelectionCriterion="AvailableInputDevices" MatchesWhen="Includes" Value="UsbHeadset"/>
</CompoundRule>
最后,
Step 4.4: 将状态同步到 Engine,更新 Engine.mInputSourceCollection 中 AUDIO_SOURCE_VOICE_COMMUNICATION 对应的 mApplicableDevices 为 0x82000000
D APM::AudioPolicyEngine: setDeviceForInputSource: set deviceType 0x82000000 for input source 7, device = 0x2000000
5. 录音请求获取设备流程
如 3.5.2 节「PolicyConfigurableDomains 配置实例化」图中 Step 5 所示,当 App 发出录音请求时,代码会走 AudioPolicyManager 中的:
getInputForAttr(source, flags)
│
└── Engine::getInputDeviceForAttributes()
│ → 查 PFW 配置,返回设备 (如 USB_HEADSET)
└── Engine::getPropertyForKey(AUDIO_SOURCE_VOICE_COMMUNICATION)
└── Element<audio_source_t>::get(audio_devices_t devices)
Engine::getPropertyForKey() 以 AUDIO_SOURCE_VOICE_COMMUNICATION 为 key,获取到 Step 4.4 和 Step 4.5 中设置的 mApplicableDevices = 0x82000000,即 AUDIO_DEVICE_IN_USB_HEADSET。
6. 总结
本文围绕 Android Audio 的 Parameter-Framework,从源码层面梳理了其核心设计思想与实现链路:
- 数据驱动策略:PFW 将路由决策逻辑从代码中剥离,以 XML 配置(Criterion → ConfigurableDomain → AreaConfiguration)定义规则,运行时通过 SelectionCriterion 状态匹配生效。
- 四维模型:CParameterFrameworkConfiguration(框架配置)、CSelectionCriteria(条件选择)、CSystemClass(参数结构)、CConfigurableDomains(参数取值)四者共同构成 PFW 的层级结构。
- 插件化扩展:Subsystem 通过动态库(如
libpolicy-subsystem.so)注册到框架中,使 PFW 具备与具体平台解耦的可扩展性。 - Blackboard 机制:全局唯一的
CParameterMgr._pMainParameterBlackboard作为参数值的统一存储,通过 Offset + Footprint 实现线性内存布局,配合 AreaConfiguration 的 save/restore 完成配置切换。 - Syncer 同步链:配置生效后通过
ISyncer::sync()将 Blackboard 数据同步到 Engine 侧的具体策略对象(如 InputSource.mApplicableDevices),完成从配置到行为的闭环。
理解 PFW 的设计有助于深入掌握 Android 音频路由策略的动态决策过程,也为在 HAL 层或 Framework 层扩展自定义策略提供了参考。