AudioFlinger、AudioPolicyService和AudioPolicyManager之间的关系?

简介

AudioFlinger和AudioPolicyService属于binder服务,而AudioPolicyManager是AudioPolicyService服务进程下的一个独立功能模块,该模块可以由厂家自行实现(但必须遵循aosp的接口定义),最后提供so库,由AudioPolicyService服务load进来调用即可,它们的关系图如下所示:

使用audiotrack播放怎么实现播放监听 audio监听音箱怎么样_android

从划分层次来说,它们均属于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交互就联通了,一图总结上面的引用关系.

使用audiotrack播放怎么实现播放监听 audio监听音箱怎么样_配置文件_02

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是初始化音频设备信息函数,最终结果很简单,就是相互保存对方的对象指针引用,但是这个过程可以学到很多,一图总结之:

使用audiotrack播放怎么实现播放监听 audio监听音箱怎么样_加载_03

如何替换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的详细过程。

  1. 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模块,一图总结之:

使用audiotrack播放怎么实现播放监听 audio监听音箱怎么样_配置文件_04

简单总结: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:

使用audiotrack播放怎么实现播放监听 audio监听音箱怎么样_加载_05

有吧,确实是这个逻辑!现在,只需要反向推导,查找那个编译模块是audio.primary.xx,就去查找它的源文件,就能够找到该audio HAL的实现定义了;

本篇内容就讲解到这里,后续的很多逻辑也是如此操作的!