AudioFlinger、AudioPolicyService和AudioPolicyManager之间的关系?
简介
AudioFlinger和AudioPolicyService属于binder服务,而AudioPolicyManager是AudioPolicyService服务进程下的一个独立功能模块,该模块可以由厂家自行实现(但必须遵循aosp的接口定义),最后提供so库,由AudioPolicyService服务load进来调用即可,它们的关系图如下所示:
从划分层次来说,它们均属于Framework层;从功能上来说,它们各自担任的使命不一样;首先,音频配置文件audio_policy_configuration.xml配置了音频的设备、流以及路由关系,而负责解析存储这些信息的工作是AudioPolicyManager完成的;其次,AudioFlinger上可以与应用层交互,下可以和HAL层交互,而中间的AudioPolicyService上可以与应用层交互,通过它中转去查询、选择音频设备信息,也就是AudioPolicyManager模块,选择完成后最终决定使用哪个设备时,又转给AudioFlinger去与下层HAL交互;最后,每个产商音频设备都是定制的,如小米、OPPO音频设备,他们可以实现自己的配置文件和AudioPolicyManager模块,这也是一个亮点。
三大类如何开始启动?
在/framework/av/media/audioserver/main_audioserver.cpp的main方法中,有他们的启动信息:
int main(int argc __unused, char **argv)
{
.....
AudioFlinger::instantiate();
AudioPolicyService::instantiate();
.....
上面源码可以看出,AudioFlinger和AudioPolicyService虽然是Binder服务类,但是他们启动都是在同一个进程中,也就是说他们交互表面上是binder IPC交互,其实实质底层也是指针相互调用的;
instantiate方法是发布自身服务到ServiceManager,了解其详情可以参考这篇文章
AudioPolicyManager的启动
下面是AudioFlinger启动过程,主要是AudioFlinger如何与HAL建立起连接的
AudioFlinger::AudioFlinger()
: BnAudioFlinger(),
....
{
......
//创建hal层音频访问hal引用
mDevicesFactoryHal = DevicesFactoryHalInterface::create();
//创建hal层音效访问hal层引用
mEffectsFactoryHal = EffectsFactoryHalInterface::create();
......
}
// static
sp<DevicesFactoryHalInterface> DevicesFactoryHalInterface::create() {
//IDevicesFactory是hal层的HIDL接口,这里先查询hal层支持的版本号,在针对性创建hidl的客户端
if (hardware::audio::V5_0::IDevicesFactory::getService() != nullptr) {
return V5_0::createDevicesFactoryHal();
}
if (hardware::audio::V4_0::IDevicesFactory::getService() != nullptr) {
return V4_0::createDevicesFactoryHal();
}
if (hardware::audio::V2_0::IDevicesFactory::getService() != nullptr) {
return V2_0::createDevicesFactoryHal();
}
return nullptr;
}
小知识点:如何找到IDeviceFactory的hidl接口定义在哪里?
查找当前源文件DevicesFactoryHalInterface的编译文件Android.mk或Android.bp, 发现其所属于libaudiohal@version模块,搜索模块名为libaudiohal@version模块,并在其编译源文件中发现:
using ::android::hardware::audio::CPP_VERSION::IDevicesFactory;
很明显这是在hardware目录,搜索发现是在/hardweare/interfaces/audio/目录下定义的hidl服务;其实了解
HIDL的开发过程的朋友,都会了解hidl定义的接口一般都在/hardware/interfaces或者/vendor/verndorname中,以后凡是遇到hidl接口,直接 去这两个文件夹中找即可
深入创建方法createDevicesFactoryHal:
sp<DevicesFactoryHalInterface> createDevicesFactoryHal() {
return new DevicesFactoryHalHybrid();
}
DevicesFactoryHalHybrid::DevicesFactoryHalHybrid()
: mLocalFactory(new DevicesFactoryHalLocal()),
//关注以下成员
mHidlFactory(new DevicesFactoryHalHidl()) {
}
DevicesFactoryHalHidl::DevicesFactoryHalHidl() {
//拿到hal层的hidl的服务端,相当于拿到了hal层的音频hidl服务接口
sp<IDevicesFactory> defaultFactory{IDevicesFactory::getService()};
if (!defaultFactory) {
ALOGE("Failed to obtain IDevicesFactory/default service, terminating process.");
exit(1);
}
//成员vector类型存储
mDeviceFactories.push_back(defaultFactory);
if (MAJOR_VERSION >= 4) {
// The MSD factory is optional and only available starting at HAL 4.0
sp<IDevicesFactory> msdFactory{IDevicesFactory::getService(AUDIO_HAL_SERVICE_NAME_MSD)};
if (msdFactory) {
mDeviceFactories.push_back(msdFactory);
}
}
//添加死亡回调
for (const auto& factory : mDeviceFactories) {
// It is assumed that the DevicesFactoryHalInterface instance is owned
// by AudioFlinger and thus have the same lifespan.
factory->linkToDeath(HalDeathHandler::getInstance(), 0 /*cookie*/);
}
}
以上代码主要是创建了DevicesFactoryHalHybrid混合接口类返回给AudioFlinger,这个混合接口里面包含本地hal和HIDL hal两种接口,这两种有什么区别,后面会讲解到。
到这里AudioFlinger与hal层的hidl交互就联通了,一图总结上面的引用关系.
AudioPolicyService的启动
在AudioPolicyService第一次被引用时,触发与AudioPolicyManager模块的关系引用绑定,如下:
void AudioPolicyService::onFirstRef()
{
{
Mutex::Autolock _l(mLock);
......
/** 将自身封装为一个客户端传递个Manager,好处是可以隐藏Service自身内部的逻辑,
* 只暴露给Manager需要的接口即可,同时也降低耦合
* */
mAudioPolicyClient = new AudioPolicyClient(this);
//createAudioPolicyManager方法创建Manager
mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient);
}
......
}
关键在于createAudioPolicyManager方法,它是一个外部函数,声明在AudioPolicyInterface.h中,AudioPolicyService包含这个头文件即可调用:
extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface);
extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface);
那这两个函数定义在什么地方呢?
分析一下,AudioPolicyService要调用这个函数,要么动态加载load定义代码的so库,并且编译时要作为shared共享库加入进来;要么在编译时就把函数定义代码就编译进来;最后查看bp和mk验证确实也是,Android 10源码直接使用Android.mk编译进来,Android 12则是二合一,先load动态so库,找得到createAudioPolicyManager就使用so库提供的,找不到就使用自身编译进来的函数createAudioPolicyManager
AudioPolicyManager启动
以Android 10源码为例,查找AudioPolicyService调用了哪个createAudioPolicyManager函数,就能够找到AudioPolicyManager启动信息了,首先查看AudioPolicyService的编译mk文件,发现关键地方:
LOCAL_SHARED_LIBRARIES := \
......
libaudiopolicymanager \
......
上面就是编译时链接的动态库libaudiopolicymanager,然后,搜索名字为libaudiopolicymanager的库,这个库里面有:
LOCAL_SRC_FILES:= \
AudioPolicyFactory.cpp
.......
LOCAL_MODULE:= libaudiopolicymanager
include $(BUILD_SHARED_LIBRARY)
最终,在源文件就是AudioPolicyFactory.cpp,里面就有我们定义的函数:
extern "C" AudioPolicyInterface* createAudioPolicyManager(
AudioPolicyClientInterface *clientInterface)
{
return new AudioPolicyManager(clientInterface);
}
extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface)
{
delete interface;
}
在仔细看看AudioPolicyManager内部定义:
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface,
bool /*forTesting*/)
:
........
mpClientInterface(clientInterface),
........
{
loadConfig();
initialize();
}
loadConfig就是加载音频配置文件的,initialize是初始化音频设备信息函数,最终结果很简单,就是相互保存对方的对象指针引用,但是这个过程可以学到很多,一图总结之:
如何替换AudioPolicyManager?
主要需要两步:
- extends继承AudioPolicyClientInterface接口并实现
- 定义createAudioPolicyManager和destroyAudioPolicyManager两个函数实现
最后,就需要把自身代码编译到AudioPolicyService代码,或者指定其load的manager模块的so库
以loadHwModule为例串联三大类
一个Android设备,存在着许多音频设备,如听筒、麦克风、音箱、蓝牙耳机、音箱等等,而Android如何管理这些设备的呢?
Android开放给各个厂商开发者AudioPolicyManager模块来管理,在该模块中,一个audio_policy_configuration.xml文件配置了一个设备有几个module,每个module里面有哪些设备、数据流,以及这些设备和流之间的关系,而且每个module对应hal的处理逻辑也尽不一样,如primary、usb这两个module,一个是针对原生Android音频设备,一个是针对usb连接方式的音频设备,他们在hal乃至kenerl层实现都不一样,所以loadHwModule最终加载的内容均不一样,以下就是loadHwModule的详细过程。
- AudioPolicyManager加载分析audio_policy_configuration.xml
在这里点击下载xml配置文件,解析通过AudioPolicyManager的loadConfig方法进行加载的,加载过程不在此详细展开,如感兴趣可参考我的这篇文章:
audio_policy_configuration.xml文件解析
最后,在AudioPolicyManager的initialize方法会解析configuration配置文件中的module、设备等信息,进行加载module、打开设备等操作,整个操作贯通到HAL、Kernel层,如下loadHwModule:
mpClientInterface->loadHwModule(hwModule->getName());
以上代码执行成功,返回一个handle句柄,并保存之,后续会用到
2. hwModule就是配置文件中的每一个module,它的name是一个字符串,一般是:primary、a2dp、usb等等,上面mpClientInterface实质是AudioPolicyService的引用,去AudioPolicyService查看实现:
audio_module_handle_t AudioPolicyService::AudioPolicyClient::loadHwModule(const char *name)
{
sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
if (af == 0) {
ALOGW("%s: could not get AudioFlinger", __func__);
return AUDIO_MODULE_HANDLE_NONE;
}
return af->loadHwModule(name);
}
没啥好看的,load工作交给AudioFlinger了。
3. AudioFlinger加载loadHwModule方法,实际调用loadHwModule_l:
audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)
{
//在缓存mAudioHwDevs中查找是否已经打开过,key是唯一句柄handle,value是AudioHwDevice设备
for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
if (strncmp(mAudioHwDevs.valueAt(i)->moduleName(), name, strlen(name)) == 0) {
ALOGW("loadHwModule() module %s already loaded", name);
return mAudioHwDevs.keyAt(i);
}
}
sp<DeviceHalInterface> dev;
//这个mDevicesFactoryHal成员就是我们上面分析过的,它存储了audio hidl的客户端
//,通过他就可以和hidl的服务端打交道
int rc = mDevicesFactoryHal->openDevice(name, &dev);
if (rc) {
ALOGE("loadHwModule() error %d loading module %s", rc, name);
return AUDIO_MODULE_HANDLE_NONE;
}
......
//打开成功的就保存
audio_module_handle_t handle = (audio_module_handle_t) nextUniqueId(AUDIO_UNIQUE_ID_USE_MODULE);
mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags));
ALOGI("loadHwModule() Loaded %s audio interface, handle %d", name, handle);
return handle;
}
看看hidl客户端是如何openDevice打开一个module的,在DevicesFactoryHalHidl中:
status_t DevicesFactoryHalHybrid::openDevice(const char *name, sp<DeviceHalInterface> *device) {
//hidl方式与hal层交互
if (mHidlFactory != 0 && strcmp(AUDIO_HARDWARE_MODULE_ID_A2DP, name) != 0 &&
strcmp(AUDIO_HARDWARE_MODULE_ID_HEARING_AID, name) != 0) {
return mHidlFactory->openDevice(name, device);
}
//本地调用方式,内部直接通过传统hw_get_module_by_class与hal层交互
return mLocalFactory->openDevice(name, device);
}
上面两种方式与HAL层交互,一种是HIDL这种类似binder进程间通信交互,另外一种是使用传统的hw_get_module_xxx获取HAL层的audio模块,同进程交互;第二种不深入分析,感兴趣可去DevicesFactoryHalLocal.cpp去查看,这里看第一种方式HIDL.
在DevicesFactoryHalHidl.cpp的openDevice中:
status_t DevicesFactoryHalHidl::openDevice(const char *name, sp<DeviceHalInterface> *device) {
if (mDeviceFactories.empty()) return NO_INIT;
status_t status;
//将name转换为hidl服务端可识别的类型IDevicesFactory::Device
auto hidlId = idFromHal(name, &status);
if (status != OK) return status;
Result retval = Result::NOT_INITIALIZED;
//遍历保存的每个hidl的客户端client,这点在前面AudioFlinger与HAL建立交互有提及
for (const auto& factory : mDeviceFactories) {
//IPC调用hidl服务端的openDevice
Return<void> ret = factory->openDevice(
hidlId,
[&](Result r, const sp<IDevice>& result) {
retval = r;
if (retval == Result::OK) {
*device = new DeviceHalHidl(result);
}
});
.....
}
ALOGW("The specified device name is not recognized: \"%s\"", name);
return BAD_VALUE;
}
//类型转换函数,这些IDevicesFactory::Device::xx定义在hardware/interfaces/audio/中定义的
static IDevicesFactory::Device idFromHal(const char *name, status_t* status) {
*status = OK;
//加载module的名字,primary走这里
if (strcmp(name, AUDIO_HARDWARE_MODULE_ID_PRIMARY) == 0) {
return IDevicesFactory::Device::PRIMARY;
//a2dp
} else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_A2DP) == 0) {
return IDevicesFactory::Device::A2DP;
} else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_USB) == 0) {
return IDevicesFactory::Device::USB;
} else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX) == 0) {
return IDevicesFactory::Device::R_SUBMIX;
} else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_STUB) == 0) {
return IDevicesFactory::Device::STUB;
}
ALOGE("Invalid device name %s", name);
*status = BAD_VALUE;
return {};
}
简单总结: AudioFlinger使用Audio音频的Client端,以HIDL方式跨进程去调用服务端的openDevice方法,该方法的参数为module模块的名称
4. 进入HIDL服务端,其服务端实现位于/hardware/interfaces/audio/core/all-versions/default/DeviceFactory.cpp中:
Return<void> DevicesFactory::openDevice(IDevicesFactory::Device device, openDevice_cb _hidl_cb) {
//根据设备名称,打开不同的设备
switch (device) {
case IDevicesFactory::Device::PRIMARY:
return openDevice<PrimaryDevice>(AUDIO_HARDWARE_MODULE_ID_PRIMARY, _hidl_cb);
case IDevicesFactory::Device::A2DP:
return openDevice(AUDIO_HARDWARE_MODULE_ID_A2DP, _hidl_cb);
.......
}
_hidl_cb(Result::INVALID_ARGUMENTS, nullptr);
return Void();
}
//模板类创建不同的实例,以primary实例为例会创建PrimaryDevice
template <class DeviceShim, class Callback>
Return<void> DevicesFactory::openDevice(const char* moduleName, Callback _hidl_cb) {
audio_hw_device_t* halDevice;
Result retval(Result::INVALID_ARGUMENTS);
sp<DeviceShim> result;
//加载hal层设备信息
int halStatus = loadAudioInterface(moduleName, &halDevice);
if (halStatus == OK) {
result = new DeviceShim(halDevice);
retval = Result::OK;
} else if (halStatus == -EINVAL) {
retval = Result::NOT_INITIALIZED;
}
_hidl_cb(retval, result);
return Void();
}
// static
int DevicesFactory::loadAudioInterface(const char* if_name, audio_hw_device_t** dev) {
const hw_module_t* mod;
int rc;
//加载音频hal的moduleID,并且其名称为if_name的模块
rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod);
if (rc) {
ALOGE("%s couldn't load audio hw module %s.%s (%s)", __func__, AUDIO_HARDWARE_MODULE_ID,
if_name, strerror(-rc));
goto out;
}
//打开上个步骤加载的模块
rc = audio_hw_device_open(mod, dev);
if (rc) {
ALOGE("%s couldn't open audio hw device in %s.%s (%s)", __func__, AUDIO_HARDWARE_MODULE_ID,
if_name, strerror(-rc));
goto out;
}
if ((*dev)->common.version < AUDIO_DEVICE_API_VERSION_MIN) {
ALOGE("%s wrong audio hw device version %04x", __func__, (*dev)->common.version);
rc = -EINVAL;
audio_hw_device_close(*dev);
goto out;
}
return OK;
out:
*dev = NULL;
return rc;
}
这里调用loadHwModule就分析完了,最终也会走到系统函数hw_get_module_by_class来加载HAL层的AUDIO模块,一图总结之:
简单总结:HIDL IPC调用openDevice,根据module name不同最终会使用系统函数hw_get_module_by_class获取不同的HAL层模块;也就是说这种HIDL交互方式是在传统与HAL交互方式下再次封装了一层,那为什么要这么做呢有什么好处?
最大的好处在于,解耦!传统方式HAL以so提供源码,并且hw_get_module加载hal层进来,与Framework运行在同一个进程中,这种情况在mk编译文件会把hal层源码拉进来一起编译,framework层每做一次升级都会牵扯HAL一起编译在升级;而HIDL方式是将二者独立开来,分属不同进程,只需关注HIDL接口,升级自身framework即可
上面hw_get_module_by_class调用时,其参数AUDIO_HARDWARE_MODULE_ID是audio,if_name是module的名字,取值有primary、a2dp、usb等等,这些参数module_id和if_name会怎么去加载HAL层,去加载哪一个so库呢?
hw_get_module以及hw_get_module_by_class意义?
以上两个函数都是加载HAL层的module,前者内部会调用后者函数,所以分析hw_get_module_by_class函数即可;网上有很多解释这个函数最终会找到id相同并且if_name相同的hal层module,这是错误的;if_name参数只是决定了加载so的名字不同,和HAL层module模块定义的name毫无关系,看下面分析,位于hardware.c中:
假设前面两个参数为audio、primary
int hw_get_module_by_class(const char *class_id, const char *inst,
const struct hw_module_t **module)
{
int i = 0;
char prop[PATH_MAX] = {0};
char path[PATH_MAX] = {0};
char name[PATH_MAX] = {0};
char prop_name[PATH_MAX] = {0};
//name= audio.primary
if (inst)
snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
else
strlcpy(name, class_id, PATH_MAX);
/* First try a property specific to the class and possibly instance */
snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);
if (property_get(prop_name, prop, NULL) > 0) {
//检查audio.primary.so在特定目录下是否存在,特定目录见hw_module_exists
if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
goto found;
}
}
......
/* 如果前面找不到,就检查audio.primary.default.so在特定目录下是否存在*/
if (hw_module_exists(path, sizeof(path), name, "default") == 0) {
goto found;
}
return -ENOENT;
found:
/* 一旦发现我们要找的so,就开始加载,这里path就是上面函数查找的path,
* path是特定目录下/audio.parmary.xx.so */
return load(class_id, path, module);
}
#if defined(__LP64__)
#define HAL_LIBRARY_PATH1 "/system/lib64/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib64/hw"
#define HAL_LIBRARY_PATH3 "/odm/lib64/hw"
#else
#define HAL_LIBRARY_PATH1 "/system/lib/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
#define HAL_LIBRARY_PATH3 "/odm/lib/hw"
#endif
static int hw_module_exists(char *path, size_t path_len, const char *name,
const char *subname)
{
//path = HAL_LIBRARY_PATH3/audio.parmary.xx.so,xx可以没有,也能是default
snprintf(path, path_len, "%s/%s.%s.so",
HAL_LIBRARY_PATH3, name, subname);
if (path_in_path(path, HAL_LIBRARY_PATH3) && access(path, R_OK) == 0)
return 0;
//path = HAL_LIBRARY_PATH2/audio.parmary.xx.so
snprintf(path, path_len, "%s/%s.%s.so",
HAL_LIBRARY_PATH2, name, subname);
if (path_in_path(path, HAL_LIBRARY_PATH2) && access(path, R_OK) == 0)
return 0;
#ifndef __ANDROID_VNDK__
//path = HAL_LIBRARY_PATH1/audio.parmary.xx.so
snprintf(path, path_len, "%s/%s.%s.so",
HAL_LIBRARY_PATH1, name, subname);
if (path_in_path(path, HAL_LIBRARY_PATH1) && access(path, R_OK) == 0)
return 0;
#endif
return -ENOENT;
}
总结一下就是:hw_get_module_by_class函数的参数组合为这个字符串:class_id.inst.xx.so,xx可能为空,去系统特定目录system、vendor、odm下面去找,找得到就加载,找不到就返回错误。最后,我们可以去系统这些目录下找找看有没有相关的so:
有吧,确实是这个逻辑!现在,只需要反向推导,查找那个编译模块是audio.primary.xx,就去查找它的源文件,就能够找到该audio HAL的实现定义了;
本篇内容就讲解到这里,后续的很多逻辑也是如此操作的!