当前位置: 首页 > news >正文

Android 11 AudioPolicyService 启动流程

AudioPolicyService在init进程中启动,源码路径:frameworks/av/media/audioserver/audioserver.rc

service audioserver /system/bin/audioserverclass coreuser audioserver# media gid needed for /dev/fm (radio) and for /data/misc/media (tee)group audio camera drmrpc media mediadrm net_bt net_bt_admin net_bw_acct wakelockcapabilities BLOCK_SUSPENDioprio rt 4task_profiles ProcessCapacityHigh HighPerformanceonrestart restart vendor.audio-halonrestart restart vendor.audio-hal-4-0-msd# Keep the original service names for backward compatibilityonrestart restart vendor.audio-hal-2-0onrestart restart audio-hal-2-0

启动audioserver服务,对应的源文件为:frameworks/av/media/audioserver/main_audioserver.cpp

int main(int argc __unused, char **argv)
{if (doLog && (childPid = fork()) != 0) {//省略}else{//省略AudioFlinger::instantiate();//添加“media.audio_flinger”服务AudioPolicyService::instantiate();//添加media.audio_policy服务//省略}
}

创建AudioPolicyService时,导致其onFirstRef函数被调用

//frameworks/av/services/audiopolicy/service/AudioPolicyService.cpp
void AudioPolicyService::onFirstRef()
{{Mutex::Autolock _l(mLock);// start audio commands threadmAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this);// start output activity command threadmOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this);mAudioPolicyClient = new AudioPolicyClient(this);mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient);}//省略

createAudioPolicyManager

//frameworks/av/services/audiopolicy/manager/AudioPolicyFactory.cpp
extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)
{AudioPolicyManager *apm = new AudioPolicyManager(clientInterface);//1status_t status = apm->initialize();//2if (status != NO_ERROR) {delete apm;apm = nullptr;}return apm;
}

注释1处创建AudioPolicyManager对象,加载配置文件。关于配置文件的加载,参考:Android 11 Audio音频系统配置文件解析
注释2处,调用AudioPolicyManager的initialize函数

initialize

//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
status_t AudioPolicyManager::initialize() {{auto engLib = EngineLibrary::load("libaudiopolicyengine" + getConfig().getEngineLibraryNameSuffix() + ".so");//加载libaudiopolicyenginedefault.somEngine = engLib->createEngine();//得到Engine对象}// after parsing the config, mOutputDevicesAll and mInputDevicesAll contain all known devices;// open all output streams needed to access attached devicesonNewAudioModulesAvailableInt(nullptr /*newDevices*/);//省略

在函数中onNewAudioModulesAvailableInt中,主要完成以下三件事情:

  1. 根据配置文件中hwModule的名字加载对应的so文件(loadHwModule)
  2. 每个hwModule的mOutputProfiles中的每个outProfile,打开输出流,创建播放线程
  3. 每个hwModule的mInputProfiles中的每个inProfile,打开输入流,创建录音线程

1,加载so文件

//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
void AudioPolicyManager::onNewAudioModulesAvailableInt(DeviceVector *newDevices)
{for (const auto& hwModule : mHwModulesAll) {if (std::find(mHwModules.begin(), mHwModules.end(), hwModule) != mHwModules.end()) {continue;}hwModule->setHandle(mpClientInterface->loadHwModule(hwModule->getName()));//1//省略      
}	

注释1处,调用loadHwModule处理,并将返回的结果handle赋值给hwModule 的mHandle。注意传入的是hwModule的名字,如:primary。最终调用到AudioFlinger的loadHwModule函数

//frameworks/av/services/audioflinger/AudioFlinger.cpp
audio_module_handle_t AudioFlinger::loadHwModule(const char *name)
{//省略Mutex::Autolock _l(mLock);AutoMutex lock(mHardwareLock);return loadHwModule_l(name);
}

loadHwModule_l

//frameworks/av/services/audioflinger/AudioFlinger.cpp
audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)
{//省略sp<DeviceHalInterface> dev;int rc = mDevicesFactoryHal->openDevice(name, &dev);//1//省略audio_module_handle_t handle = (audio_module_handle_t) nextUniqueId(AUDIO_UNIQUE_ID_USE_MODULE);AudioHwDevice *audioDevice = new AudioHwDevice(handle, name, dev, flags);//2mAudioHwDevs.add(handle, audioDevice);//3return handle;
}

注释1处通过hidl,去加载对应的so(如:audio.primary.default.so),并调用audio hal的open函数,从hal中得到一个audio_hw_device对象。注释2处将该对象封装在AudioHwDevice 中,然后注释3处将AudioHwDevice 添加到mAudioHwDevs数组中。

打开输出流创建线程

//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
void AudioPolicyManager::onNewAudioModulesAvailableInt(DeviceVector *newDevices)
{for (const auto& hwModule : mHwModulesAll) {if (std::find(mHwModules.begin(), mHwModules.end(), hwModule) != mHwModules.end()) {continue;}//省略   for (const auto& outProfile : hwModule->getOutputProfiles()) {//省略sp<SwAudioOutputDescriptor> outputDesc = new SwAudioOutputDescriptor(outProfile,mpClientInterface);audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;status_t status = outputDesc->open(nullptr, DeviceVector(supportedDevice),AUDIO_STREAM_DEFAULT,AUDIO_OUTPUT_FLAG_NONE, &output);//省略addOutput(output, outputDesc);//保存到mOutputs数组中}   //省略
}	

针对module下的每个outProfile ,都会创建一个SwAudioOutputDescriptor对象,并调用其open方法,然后将该SwAudioOutputDescriptor保存到mOutputs数组中,注意是根据返回的output来保存的。open函数最终调用到AudioFlinger的openOutput_l函数

//frameworks/av/services/audioflinger/AudioFlinger.cpp
sp<AudioFlinger::ThreadBase> AudioFlinger::openOutput_l(audio_module_handle_t module,audio_io_handle_t *output,audio_config_t *config,audio_devices_t deviceType,const String8& address,audio_output_flags_t flags)
{AudioHwDevice *outHwDev = findSuitableHwDev_l(module, deviceType);//根据handle,从mAudioHwDevs数组中找出AudioHwDevice //省略AudioStreamOut *outputStream = NULL;status_t status = outHwDev->openOutputStream(&outputStream,*output,deviceType,flags,config,address.string());//1if (status == NO_ERROR) {if (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) {//省略} else {sp<PlaybackThread> thread;if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {//省略} else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT)|| !isValidPcmSinkFormat(config->format)|| !isValidPcmSinkChannelMask(config->channel_mask)) {//省略} else {thread = new MixerThread(this, outputStream, *output, mSystemReady);//创建MixerThread播放线程}mPlaybackThreads.add(*output, thread);//添加到mPlaybackThreadsreturn thread;}}return 0;
}

注释1处调用AudioHwDevice 的openOutputStream函数去打开输出流。然后就是创建MixerThread播放线程并保存在mPlaybackThreads数组中,需要注意的是,也是根据output添加的。这里的output和上面添加outputDesc时的output是同一个。

openOutputStream

//frameworks/av/services/audioflinger/AudioHwDevice.cpp
status_t AudioHwDevice::openOutputStream(AudioStreamOut **ppStreamOut,audio_io_handle_t handle,audio_devices_t deviceType,audio_output_flags_t flags,struct audio_config *config,const char *address)
{struct audio_config originalConfig = *config;AudioStreamOut *outputStream = new AudioStreamOut(this, flags);//创建AudioStreamOut对象status_t status = outputStream->open(handle, deviceType, config, address);//省略*ppStreamOut = outputStream;return status;
}

AudioStreamOut::open

//frameworks/av/services/audioflinger/AudioStreamOut.cpp
status_t AudioStreamOut::open(audio_io_handle_t handle,audio_devices_t deviceType,struct audio_config *config,const char *address)
{sp<StreamOutHalInterface> outStream;int status = hwDev()->openOutputStream(handle,deviceType,customFlags,config,address,&outStream);
//省略

openOutputStream最后会通过hidl,调用到audio hal 的open_output_stream函数,返回一个audio_stream_out对象。

打开输入流,创建录音线程

这个流程和上面的是一样的,最终调用到AudioFlinger的openInput_l处理

//frameworks/av/services/audioflinger/AudioFlinger.cpp
sp<AudioFlinger::ThreadBase> AudioFlinger::openInput_l(audio_module_handle_t module,audio_io_handle_t *input,audio_config_t *config,audio_devices_t devices,const String8& address,audio_source_t source,audio_input_flags_t flags,audio_devices_t outputDevice,const String8& outputDeviceAddress)
{AudioHwDevice *inHwDev = findSuitableHwDev_l(module, devices);//找出AudioHwDevice //省略sp<DeviceHalInterface> inHwHal = inHwDev->hwDevice();sp<StreamInHalInterface> inStream;status_t status = inHwHal->openInputStream(*input, devices, &halconfig, flags, address.string(), source,outputDevice, outputDeviceAddress, &inStream);//1//省略if (status == NO_ERROR && inStream != 0) {AudioStreamIn *inputStream = new AudioStreamIn(inHwDev, inStream, flags);//将HAL层得到的audio_stream_in保存在AudioStreamIn中if ((flags & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0) {//省略} else {// Start record thread// RecordThread requires both input and output device indication to forward to audio// pre processing modulessp<RecordThread> thread = new RecordThread(this, inputStream, *input, mSystemReady);//创建RecordThread线程mRecordThreads.add(*input, thread);//保存在mRecordThreads数组中ALOGV("openInput_l() created record thread: ID %d thread %p", *input, thread.get());return thread;}//省略

注释1处,通过hidl,调用到audio hal 的open_input_stream函数,返回一个audio_stream_in对象。

总结

在AudioPolicyService服务的启动过程中,会解析配置文件。针对每个HwModule,根据名字去加载对应的HAL库,得到audio_hw_device对象,将其保存在AudioHwDevice对象中,并将AudioHwDevice对象添加到mAudioHwDevs数组中。

对于HwModule下的每个outProfile ,都会创建SwAudioOutputDescriptor对象,并去打开输出流,创建播放线程。根据返回的output,将SwAudioOutputDescriptor添加到mOutput数组中。在打开输出流的过程中,会得到HAL层的audio_stream_out对象,并将其保存在AudioStreamOut中,创建播放线程的时候,传入该AudioStreamOut,最后将播放线程保存到mPlaybackThreads中。

对于HwModule下的每个inProfile,都会去打开输入流,创建录音线程。在打开输出流的过程中,会得到HAL层的audio_stream_in对象,并将其保存在AudioStreamIn中,创建录音线程的时候,传入该AudioStreamIn。最后将录音线程保存在mRecordThreads中

保存AudioHwDevice和保存播放线程,是根据同一个output保存的。

相关文章:

Android 11 AudioPolicyService 启动流程

AudioPolicyService在init进程中启动&#xff0c;源码路径&#xff1a;frameworks/av/media/audioserver/audioserver.rc service audioserver /system/bin/audioserverclass coreuser audioserver# media gid needed for /dev/fm (radio) and for /data/misc/media (tee)grou…...

java中static关键字面试五连问

抽象&#xff08;abstract&#xff09;方法是否可同时是静态的&#xff08;static&#xff09;? 抽象方法本来将来就是要被重写的&#xff0c;而静态方法不能被重写&#xff0c;所以是错误的 是否可以从一个静态&#xff08;static&#xff09;方法内部发出对非静态方法的调…...

基于文本来推荐相似酒店

基于文本来推荐相似酒店 查看数据集基本信息 import pandas as pd import numpy as np from nltk.corpus import stopwords from sklearn.metrics.pairwise import linear_kernel from sklearn.feature_extraction.text import CountVectorizer from sklearn.feature_extrac…...

红队内网攻防渗透:内网渗透之前置知识外网权限提升技术

红队内网攻防渗透 1. 内网权限提升技术1.1 外网权限提升的思路-前置知识1.1.1 外网权限提升知识点:1.1.2 外网权限提升基础内容1.1.2.1 为什么我们要学习权限提升转移技术:1.1.2.2 具体有哪些权限需要我们了解掌握的:1.1.2.3 以上常见权限获取方法简要归类说明:1.1.2.4 以上…...

【漏洞复现】大华智能物联综合管理平台 log4j远程代码执行漏洞

0x01 产品简介 大华ICC智能物联综合管理平台对技术组件进行模块化和松耦合&#xff0c;将解决方案分层分级&#xff0c;提高面向智慧物联的数据接入与生态合作能力。 0x02 漏洞概述 大华ICC智能物联综合管理平台/evo-apigw/evo-brm/1.2.0/user/is-exist 接口处存在 l0g4i远程…...

OrangePi AIpro测评

文章目录 1、外观部分2、系统初探3、AI性能体验4、总结 首先非常感谢csdn以及香橙派能够提供这样一个平台&#xff0c;可以测试OrangePi AIpro这样一块开发板&#xff0c;这块板子给我的感觉还是非常不错的&#xff0c;非常适合用来作为嵌入式学习的板子&#xff0c;性能也达到…...

写代码之前一定要提前想好思路

就和写数学题目一样&#xff0c;在做题目之前要先把思路确立下来。可能是我早年做数学的时候老是着急做题目没怎么分析过题目&#xff0c;把这个习惯不自觉地代入了代码的写入当中。习惯的养成使得我即使明白了自己的问题也依然会不断的犯错&#xff0c;看来只有刻意地提醒自己…...

「清新题精讲」Skiers

更好的阅读体验 Skiers Description 给定 n n n 个点的有向无环平面图&#xff0c;求最少多少条从 1 1 1 到 n n n 的路径能覆盖原图的所有边&#xff1f; 1 ≤ n ≤ 5 1 0 3 1\le n\le 5\times10^3 1≤n≤5103 Solution 考虑从 1 1 1 到 n n n 的路径其实是边的链覆…...

Transformer详解(8)-基于transformer的英文到中文翻译模型

1、数据使用TED&#xff0c;数据清洗 WIT是“转录和翻译演讲网络清单”的缩写&#xff0c;是 TED 演讲多语言转录的现成版本&#xff0c;可用于研究目的。 2、英文中文翻译模型搭建 3、模型训练 4、模型推理...

算法的时间复杂度(详解)

前言&#xff1a; 算法(Algorithm):就是定义良好的计算过程&#xff0c;他取一个或一组的值为输入&#xff0c;并产生出一个或一组值作为 输出。简单来说算法就是一系列的计算步骤&#xff0c;用来将输入数据转化成输出结果 一、算法效率 1.1 如何衡量一个算法的好坏 如何衡…...

Flutter 中的 NestedScrollViewViewport 小部件:全面指南

Flutter 中的 NestedScrollViewViewport 小部件&#xff1a;全面指南 Flutter 是一个功能丰富的 UI 工具集&#xff0c;它提供了多种布局和控件来帮助开发者构建美观且功能强大的应用。在 Flutter 的滚动控件中&#xff0c;NestedScrollView 是一个特别的存在&#xff0c;它允…...

断开自定义模块与自定义库的链接

断开自定义模块与自定义库的链接 1、断开模块与库的链接 1、断开模块与库的链接 如果摸个库文件添加到模型中&#xff0c;无法“Disable Link”时&#xff0c;可以使用save_system命令进行断开到模型中用户定义的库模块的链接&#xff1b; 参考链接&#xff1a; 传送门 save…...

粉丝问,有没有UI的统计页面,安排!

移动应用的数据统计页面具有以下几个重要作用&#xff1a; 监控业务指标&#xff1a;数据统计页面可以帮助用户监控关键业务指标和数据&#xff0c;例如用户活跃度、销售额、转化率等。通过实时更新和可视化呈现数据&#xff0c;用户可以及时了解业务的整体状况和趋势。分析用…...

Nginx R31 doc-17-debugging 调试

前言 大家好&#xff0c;我是老马。很高兴遇到你。 我们为 java 开发者实现了 java 版本的 nginx https://github.com/houbb/nginx4j 如果你想知道 servlet 如何处理的&#xff0c;可以参考我的另一个项目&#xff1a; 手写从零实现简易版 tomcat minicat 手写 nginx 系列 …...

python -【一】基础语法

python 基础语法 一. 基础数据类型 常用的 6 种数据类型 类型描述说明数字&#xff08;Number&#xff09;int&#xff0c;float&#xff0c;complex(复数)&#xff0c;bool复数&#xff1a;4 3j&#xff0c;j 表示复数字符串&#xff08;String&#xff09;文本&#xff1…...

数据结构 | 详解二叉树——堆与堆排序

&#x1f95d;堆 堆总是一棵完全二叉树。 大堆&#xff1a;父节点总是大于子节点。 小堆&#xff1a;父节点总是小于子节点。 注意&#xff1a;1.同一个节点下的两个子节点并无要求先后顺序。 2.堆可以是无序的。 &#x1f349;堆的实现 &#x1f334;深度剖析 1.父节点和子…...

vb.net,C#强制结束进程,“优雅”的退出方式

在VB.NET中&#xff0c;Application.Exit()和Environment.Exit(0)都用于结束程序&#xff0c;但它们的使用场景和背后的逻辑略有不同。 **Application.Exit()**&#xff1a; Application.Exit()通常用于Windows Forms应用程序中。当调用Application.Exit()时&#xff0c;它会触…...

牛客热题:数据流中的中位数

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;力扣刷题日记 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 文章目录 牛客热题&#xff1a;数据流中的中位数题目链接方法一…...

JavaScript-JavaWeb

目录 什么是JavaScript? js引入方式 js基础语法 书写语法 变量 数据据类型 运算符 类型转换 流程语句 js函数 js对象 1.Array 2.String 3.JSON js事件监听 什么是JavaScript? ● JavaScript(简称:JS)是一门跨平台、面向对象的脚本语言。是用来控制网页行为的,它能…...

vue组件通讯$parent和$children获取单签组件的⽗组件和当前组件的⼦组件的例子

在 Vue 中&#xff0c;$parent 和 $children 是实例属性&#xff0c;允许你访问组件的父组件和子组件。但是&#xff0c;请注意&#xff0c;这些属性主要用于在开发过程中进行调试和临时访问&#xff0c;并不推荐在正常的组件通信中使用&#xff0c;因为它们破坏了组件的封装性…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)

引言 在人工智能飞速发展的今天&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;已成为技术领域的焦点。从智能写作到代码生成&#xff0c;LLM 的应用场景不断扩展&#xff0c;深刻改变了我们的工作和生活方式。然而&#xff0c;理解这些模型的内部…...

(一)单例模式

一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...

热烈祝贺埃文科技正式加入可信数据空间发展联盟

2025年4月29日&#xff0c;在福州举办的第八届数字中国建设峰会“可信数据空间分论坛”上&#xff0c;可信数据空间发展联盟正式宣告成立。国家数据局党组书记、局长刘烈宏出席并致辞&#xff0c;强调该联盟是推进全国一体化数据市场建设的关键抓手。 郑州埃文科技有限公司&am…...

rknn toolkit2搭建和推理

安装Miniconda Miniconda - Anaconda Miniconda 选择一个 新的 版本 &#xff0c;不用和RKNN的python版本保持一致 使用 ./xxx.sh进行安装 下面配置一下载源 # 清华大学源&#xff08;最常用&#xff09; conda config --add channels https://mirrors.tuna.tsinghua.edu.cn…...

在golang中如何将已安装的依赖降级处理,比如:将 go-ansible/v2@v2.2.0 更换为 go-ansible/@v1.1.7

在 Go 项目中降级 go-ansible 从 v2.2.0 到 v1.1.7 具体步骤&#xff1a; 第一步&#xff1a; 修改 go.mod 文件 // 原 v2 版本声明 require github.com/apenella/go-ansible/v2 v2.2.0 替换为&#xff1a; // 改为 v…...

数据库——redis

一、Redis 介绍 1. 概述 Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源的、高性能的内存键值数据库系统&#xff0c;具有以下核心特点&#xff1a; 内存存储架构&#xff1a;数据主要存储在内存中&#xff0c;提供微秒级的读写响应 多数据结构支持&…...