目次
一、应用API层
二、Java框架层
三、Native焦点层
3.1 AudioFlinger模块
3.2 AudioPolicyService模块
四、HAL层
本文基于AOSP13源码举行分析解读。以是与各个SoC平台厂商提供的运行在真实装备上的源码会有渺小差别,但焦点原理区别不大。
音频子体系在Android中是一个较为复杂的子体系,高出应用API层,框架层,Native层和HAL层。利用Java、C++、C语言举行编写。运行在Linux用户空间的4个进程中:APP应用进程(API层的代码)、SystemServer进程(框架层的代码)、AudioServer进程(Native层的代码)、AudioHAL进程(HAL层的代码)。此中运行在AudioServer进程中的AudioFlinger和AudioPolicyService,以及运行在SystemServer进程中的AudioService这三个模块是Android音频子体系的焦点,也是我后续分析的重点。而AudioHAL模块是由各个SoC厂商自行根据芯片特点实现的,每种平台的代码都不一样,AOSP中只界说了标准接口,并没有具体的实今世码。以下是音频子体系的团体架构图:
一、应用API层
应用API层的源码位置:/frameworks/base/media/java/android/media/
这部分代码是运行在调用其代码的APP进程中的,图中用浅蓝色标注的模块。此中重要的接口模块有三个:
- AudioTrack.java:为APP提供播放音频数据的接口。
- AudioRecord.java:为APP提供录制音频数据的接口。
- AudioManager.java:为APP提供管理音频路由、音量控制、音频焦点获取的接口。
从上面的团体架构图中可以看出,AudioTrack.java和AudioRecord.java会分别调用其对应的JNI文件:android_media_AudioTrack.cpp、android_media_AudioRecord.cpp(它们位于/frameworks/base/core/jni/目次中)。而这两个JNI层的代码又会调用Native的客户端代码AudioTrack.cpp和AudioRecord.cpp(它们位于/frameworks/av/media/libaudioclient/目次中)。以是,我们可以看出音频播放和录制数据的应用接口代码的真正实现是在libaudioclient中的。
AudioManger.java是AudioService.java的客户端署理类,它内里的大多数函数都是通过Binder方式远程调用AudioService.java来实现的。固然一小部分简单的函数也会直接调用AudioSystem.java来实现。而大多数情况下AudioSystem.java都是被AudioService.java调用利用的。
在上面的团体架构图中,我把AudioSystem.java放在了APP进程和SystemServer进程这两个进程框线之间,意思是这个java文件会分别运行在两个差别的进程中。我们知道Java的特性是同一个java文件在差别的进程中会有差别对象和数据,不外当我们读一下AudioSystem.java的源码我们就会发现,它是一个全部通过静态方法实现的代码文件,只有静态数据,比如注册的一些Callback函数。以是,APP进程中注册的Callback对象和SystemServer进程中注册的Callback对象并不是同一个。AudioSystem.java文件也位于/frameworks/base/media/java/android/media/目次中,但是它是一个hide class,以是平常的APP步调是无法直接调用它的接口的,固然Java反射除外。AudioSystem.java和AudioTrack.java雷同,它也会通过JNI调用android_media_AudioSystem.cpp文件,然后JNI文件又会调用libaudioclient的AudioSystem.cpp文件。以是AudioSystem.cpp才是真正实今世码逻辑的地方。
/frameworks/av/media/libaudioclient/这个目次中的源码会被编译打包成libaudioclient.so,它包罗了三个告急客户端接口类AudioTrack.cpp、AudioRecord.cpp和AudioSystem.cpp。以是,如果我们写一个native APP,完全可以直接加载libaudioclient这个so库,调用它们提供的接口API。 前面我们提到AudioManager.java会直接调用AudioSystem.java的部分函数,而AudioManager.java是运行在APP进程中的,AudioSystem.java终极又会调用libaudioclient.so的AudioSystem.cpp,以是我们可以推断APP进程在初始化时,已经加载过了libaudioclient.so库。我们可以直接利用C++代码调用AudioTrack.cpp和AudioRecord.cpp中的接口函数。
AudioSystem.cpp也是一个由静态函数实现的类。它的作用是和AudioFilnger、AudioPolicyService通讯,通过Binder方式调用这两个模块提供的接口函数。同时,它提供了直接获取这两个模块的Binder署理接口类的方法:get_audio_flinger()、get_audio_policy_service()。
当我们检察AudioPolicyService模块中的源码时,我们就会看到,AudioPolicyService也是通过AudioSystem.cpp的get_audio_flinger()接口函数,来获取到AudioFlinger的Binder署理类,然后和AudioFilnger模块举行通讯的,固然AudioPolicyService模块和AudioFlinger这两个模块都是运行在同一进程AudioServer中。Binder的机制是跨进程调用时,被调用的Server端函数会运行在Server端的15个Binder线程中的一个,而同一进程内调用时,被调用的Server端函数就直接运行在客户端调用方运行的线程中。以是,AudioPolicyService所调用的任何AudioFlinger函数,都不是运行在AudioServer进程的15个Binder线程中的。趁便说一下,基于这个发现,我们可以知道AudioServer进程中已经加载了libaudioclient.so库,由于它利用了AudioSystem.cpp文件。以是,在AudioServer进程中的模块代码是可以直接利用AudioTrack.cpp和AudioRecord.cpp文件的。正是基于这个发现,我之前在办理一个手机通过蓝牙毗连到宝马车机播放声音延伸很大的标题时,就想到了在AudioFlinger模块中直接通过AudioTrack.cpp和AudioRecord.cpp创建一个超声播放回路,用于监测车内的声音播放延伸巨细,实践证明是可行的。
通过上面的分析我们可以发现,Android音频子体系提供了两套API接口给客户端利用,一套是Java代码实现的,位于/frameworks/base/media/java/android/media/目次中。一套是C++代码实现的,位于/frameworks/av/media/libaudioclient/目次中。
应用API层除了提供焦点的AudioTrack.java、AudioRecord.java和AudioManager.java接口之外,还提供了AudioPatch.java和AudioMix.java这两类接口,用于客户端定制音频路由战略。但是它们两都是hide class,以是平常应用不能利用。我看现在重要是CarAudioService在利用,用于车机场景。后续偶然机我会单独写一篇文章来先容这两个类的作用,由于是和AudioPolicyService模块强干系,以是要在深入分析完AudioPolicyService模块后再先容更为符合。
二、Java框架层
Java框架层的源码位置:/frameworks/base/services/core/java/com/android/server/audio/
这部分代码是运行在SystemServer进程中的,团体架构图中用绿色标注的模块。
AudioService.java是音频装备路由管理、音量控制、焦点控制的具体实现模块。它重要包罗4个子模块。具体模块架构图如下:
根本上从文件名称上我们就可以看出这几个模块的用途:
- AudioDeviceBroker.java:用于管理音频装备路由战略。它包罗两个子模块AudioDeviceInventory.java和BtHelper.java。BtHelper用于和蓝牙Java层模块交互(源码位于/packages/modules/Bluetooth/目次),监听蓝牙装备的毗连状态和codec设置变更信息等。
- MediaFocusControl.java:音频焦点控制模块。
- PlaybackActivityMonitor.java:音频播放变乱监听模块。
- RecordingActivityMonitor.java:音频录制变乱监听模块。
在/frameworks/base/services/core/java/com/android/server/audio/这个目次中,我并没有看到音量控制干系的类,由于它是由AudioService.java自己实现的。尚有一个类,固然不在这个目次中,但须要在这里提一下:
/frameworks/base/services/core/java/com/android/server/WiredAccessoryManager.java
它是监听有线耳机装备插拔状态的入口。
我之前看到这里的代码,曾产生一个疑问,既然AudioService是用于管理音频装备路由战略的,那还要AudioPolicyService干嘛?大概说直接在AudioPolicyService内里实现不就行了,还要AudioService干嘛?我的明白是AudioService是运行在SystemServer进程的,它可以直接和ActivityManagerService等别的Android焦点折务交互,可以知道客户端是哪个package name在播放、在设置装备路由等。辅助AudioPolicyService更好的管理。以是,我以为基于Android体系联合当地用户应用场景来举行音频模块定制化时,应该优先思量修改AudioService模块,由于它更靠近用户场景,AudioService模块实现不了的,再思量修改AudioPolicyService模块。
三、Native焦点层
音频Native焦点层分为两大模块:AudioFlinger和AudioPolicyService。它们都是注册在ServiceManager中的Binder服务。运行在AudioServer进程中,团体架构图中橙色部分。下面分开举行先容。
3.1 AudioFlinger模块
AudioFlinger模块的源码位置:/frameworks/av/services/audioflinger/
我以为AudioFlinger重要分为三个递进关系的子模块:AudioHwDevice、PlaybackThread/RecordThread、PlaybackTrack/RecordTrack。固然我没有提到音效干系的类,是由于我以为不先容音效干系类并不影响团体架构的梳理和明白,后续讲到播放时再来先容也不要紧。
这里的AudioHwDevice并不是指喇叭、听筒、耳机这种真实的物理装备,而是指差别的Audio HAL module,比如primary module、a2dp module等,也可以明白成是音频框架层界说的一种假造装备。它和Audio HAL层的DeviceHal是对应的关系。
PlaybackThread/RecordThread是由AudioFlinger启动的负责音频数据传输的线程。播放和录制的焦点流程都在这内里实现。它和Audio HAL层的StreamHal是对应的关系。
PlaybackTrack/RecordTrack对应的是API层的AudioTrack/RecordTrack,它相称于是服务端。
我还想提一个模块是PatchPanel.cpp,它是用于管理音频装备切换的类。利用AudioPatch类来体现stream和device的毗连关系。之以是在这里提及一下,是由于这个文件中包罗了一些AudioFlinger类的实现函数,各人在检察AudioFlinger.h阐明的函数具体实现时,如果在AudioFlinger.cpp中找不到,就可以在PatchPanel.cpp找一下,重要是和音频装备切换干系的,比如createAudioPatch()函数。
以下是AudioFlinger焦点子模块的类图:
从类图中可以看出,应用层libaudioclient中的AudioTrack.cpp有一个成员变量mAudioTrack(BpAudioTrack),它是IAudioTrack.aidl接口的署理端,通过Binder方式与BnAudioTrack举行交互。BpAudioTrack和BnAudioTrack都是Android基于IAudioTrack.aidl文件编译时自动天生的,以是找不到源码。IAudioTrack.aidl服务端的实现就是继承了BnAudioTrack的AudioFlinger.TrackHandle这个内部类。以是,AudioTrack.cpp现实上就是和它在举行通讯交互。AudioTrack.cpp获取IAudioTrack署理端的方法是调用AudioFlinger的createTrack()函数。
检察AudioFlinger.TrackHandle的源码会发现,在AudioFlinger内部,TrackHandle只是一个署理,负责与客户端交互,真正的实现逻辑是在AudioFlinger.PlaybackThread.Track这个内部类中。
AudioFlinger模块的源码文件个数不多,但是一个文件中界说了好几个内部类,以下是各种内部类地点的源码文件位置,方法各人查找阅读:
- 全部Track干系的源码都界说在TrackBase.h、PlaybackTacks.h、RecordTracks.h这三个头文件中,同一在Tracks.cpp文件中实现。但是TrackHandle类和RecordHandle类是界说在AudioFlinger.h头文件中,实现却是在Tracks.cpp文件中。
- 全部Thread干系的源码都界说在Threads.h文件中,实现是在Threads.cpp文件中。比如PlaybackThread和RecordThread类。
- AudioStreamIn这个类由于比力简单,它的界说和实现都是在AudioFlinger.h头文件中。
DeviceHalInterface、StreamOutHalInterface和StreamInHalInterface这三个接口类是AudioHAL界说的HAL客户端接口类,源码位于/frameworks/av/media/libaudiohal/目次中,在HAL层架构分析时我会进一步讲。
由于Thread和Track这两种类的继承关系较多,以是我画了两个类图来阐明它们的布局,如下:
从这个类图中可以看出,全部的播放和录制Thread类都是继承的ThreadBase,而ThreadBase又继承了Android标准的循环线程类Thread,以是它们都是可以单独循环运行的平常线程。以下是各种Thread的作用先容:
- MixerThread:包罗将上层多个track数据举行混音利用的播放线程。当AudioPolicySerivce哀求openOutput时设置了这些flag,AudioFlinger就会创建此种线程:AUDIO_OUTPUT_FLAG_PRIMARY、AUDIO_OUTPUT_FLAG_FAST、AUDIO_OUTPUT_FLAG_DEEP_BUFFER。它也是AudioFlinger利用频次最多、默认创建的Thread范例。
- DirectOutputThread:不颠末混音等上层音效处置惩罚的直传播放线程。当设置了AUDIO_OUTPUT_FLAG_DIRECT flag时,会被创建利用。
- OffloadThread:要求通过底层DSP硬件举行解码的播放线程。当设置了AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD flag时,会被创建利用。
- DuplicatingThread:多路输出播放线程,比如手机连上蓝牙耳机厥后电时,声音会从喇叭和耳机同时输出。
- SpatializerThread:Android13新增长的空间音频播放线程。当设置了AUDIO_OUTPUT_FLAG_SPATIALIZER flag时,会被创建利用。
全部的Thread类都是AudioFlinger的内部类,但是如果我们检察Threads.h源文件,并没有看到这些类是界说在class AudioFlinger中的?缘故原由是在AudioFlinger.h文件中,界说的AudioFlinger class包罗了这句代码:#include "Threads.h"。Track类的界说也是接纳雷同的方法,以下是Track的类图,从这个类图中可以看到,全部的Track都是Thread的内部类。
3.2 AudioPolicyService模块
AudioPolicyService模块的源码位置:/frameworks/av/services/audiopolicy/
AudioPolicyService模块的源码目次布局比力多,源文件也比力多。但是我以为焦点的就3个子模块:AudioPolicyService、AudioPolicyManager和Engine。Engine负责存储装备路由战略和音量巨细设置。AudioPolicyManager负责装备路由和音量控制的具体实现。AudioPolicyService负责对外提供Binder接口和别的模块举行交互。它们三者的调用可以从以下类图中看出:
简化一下,就可以看出这三者的调用关系是:
AudioPolicyService<-->AudioPolicyManager<-->Engine
固然,在AudioPolicyService模块中,也会存在device、stream、track的概念,只是它们的定名方式不一样。这也是我以为整个Android音频子体系代码实现欠好的地方,我猜大概是各个模块由差别的人编写的缘故原由吧。比如说streamOutput,AudioFlinger内里叫PlaybackThread,AudioPolicyService内里又叫AudioOutputDescriptor,尚有一个叫IOProfile,IOProfile对象生存封装的是config设置文件中针对stream流的设置,而在设置文件中,又界说成叫mixPort,而mix这个名称,让我一开始想到的是AudioFlinger内里的AudioMixer(用于实行多个track混音的模块)。device也是,AudioFlinger内里叫AudioHwDevice,到了AudioPolicyService内里又叫HwModule。在刚开始看这些代码时,真的是能把人绕晕。下面我就来先容一下AudioPolicyService模块内里的焦点数据布局。
AudioPolicyService模块的四个焦点数据类:HwModule、AudioIODescriptorInterface、DeviceDescriptor、ClientDescriptor。
- HwModule:它代表的是HAL层的假造装备Device,与AudioFlinger中的AudioHwDevice对应。在audio_policy_configuration.xml设置文件中对应的是<module>节点。
- AudioIODescriptorInterface:它包罗两个子类,AudioOutputDescriptor和AudioInputDescriptor。代表的是音频输入输出流,对应的是AudioFlinger中的Thread,以及HAL层的Stream。同时它尚有一个成员变量IOProfile对象,IOProfile对应的是audio_policy_configuration.xml设置文件中的<mixPort>节点。
- DeviceDescriptor:它代表的是真实的物理装备,比如喇叭、听筒、Mic等。对应的是audio_policy_configuration.xml设置文件中<devicePort>节点。
- ClientDescriptor:它包罗两个子类,TrackClientDescriptor和RecordClientDescriptor。分别对应的是上层的Track和Record对象。
下图是以音频播放场景举例,来阐明这四种数据范例在各个层级中的界说,颜色雷同的代表同一种数据范例。以及它们四者之间的包罗关系。

从这个图中可以看出,四者之间的关系是:Module包罗多个OutputStream,OutputStream作为中心毗连器,包罗上层的多个track,也包罗底层的多个device。此中AudioPolicyService模块的关系界说是最标准的,也最能阐明它们四者之间的关系。
同时可以看出,Track是只在上层和框架层存在的概念。到了HAL层就没有Track这个概念了。HAL层只有outputStream和真实物理装备Device的概念。
下面分别阐明一下这四个数据布局在各个层级中的界说名称:
- Module:只存在于框架层和HAL层的概念。代表的是一个HAL模块,也可以明白成是一个假造装备。它在AudioPolicyService中的界说是HwModule。在AudioFlinger中的界说是AudioHwDevice。在HAL向上接口层的界说是IDevice.aidl。在HAL向下接口层的界说是audio_hw_device。
- OutputStream:代表的是一个音频输出流。用于毗连上层的track和底层真实的device装备。它在AudioPolicyService中的界说是AudioOutputDescriptor、OutputProfile。在AudioFlinger中的界说是PlaybackThread、AudioStreamOut。在HAL向上接口层的界说是IStreamOut.aidl。在HAL向下接口层的界说是audio_stream_out。
- Track:代表的是应用层的一个播放对象,一个应用进程中可以创建多个Track。它在应用层的界说是AudioTrack.java。在AudioPolicyService中的界说是TrackClientDescriptor。在AudioFlinger中的界说是AudioFlinger.TrackHandle、AudioFlinger.PlaybackThread.Track。
- Device:代表的是一个真实的物理装备。它在应用层的界说是AudioDeviceInfo.java。在AudioPolicyService中的界说是DeviceDescriptor。在AudioFlinger中的界说是AudioDeviceTypeAddr。在HAL层的界说是audio_devices_t。
如果我们在/system/media/audio/include/system/audio-hal-enums.h源文件中看一下audio_devices_t的界说,就会发现它只是一个界说多种常量的罗列。并没有包罗物理装备的设置信息:比如支持的samplingRates、format等。缘故原由是Android音频子体系中还界说了一个叫AudioPort的概念。它代表的是一个音频流中的节点,可以是真实物理装备、也可以是outputstream、还可以是session。AudioPort有两种脚色,一种是sink,比如说代表喇叭装备时。一种是source,比如说代表PlaybackThread时,大概代表mic装备时。
AudioPort是高出整个音频子体系的数据范例,它在应用层、框架层、HAL层中都有利用。它的界说是在/system/media/audio/include/system/audio.h源文件中,界说了两个布局体:audio_port和audio_port_config。各个层级也会基于此界说自己的数据范例,比如Native层的AudioPort和AudioPortConfig类,它们位于/frameworks/av/media/libaudiofoundation/include/media/AudioPort.h源文件中,供C++天下的代码利用,比如AudioPolicyService模块。在Java天下中的界说是:AudioPort.java和AudioPortConfig.java,位于/frameworks/base/media/java/android/media/目次中。
下面我会画一个AudioPolicyService模块中的DeviceDescriptor和OutputProfile的继承关系类图,从而更直观看出AudioPort的界说。
这张类图中,可以直观的看到DeviceDescriptor和OutputProfile都是继承了AudioPort类的。此中PolicyAudioPort类和PolicyAudioPortConfig类是AudioPoicyService模块自己扩展界说的AudioPort。位于/frameworks/av/services/audiopolicy/common/managerdefinitions/include/PolicyAudioPort.h源文件中。
末了,先容一下AudioPolicyService模块的源码目次布局:
- /frameworks/av/services/audiopolicy/根目次下,只有一个源文件:AudioPolicyInterface.h,它界说了AudioPolicyService和AudioPolicyManager须要依照的接口。
- ./service/目次:界说了AudioPolicyService模块的代码实现。
- ./managerdefault/目次:界说了AudioPolicyManager模块的代码实现。这个目次中的代码会被编译打包到libaudiopolicymanagerdefault.so库中。厂商也可以根据自己须要完全重写。
- ./enginedefault/目次:界说了engine模块的代码实现。这个目次会被编译打包到libaudiopolicyenginedefault.so库中。
- ./engineconfigurable/目次,界说了engine模块的别的一种实现,现在没有利用,而是用的./enginedefault/目次中的源码。
- ./engine/目次:界说了engine模块的底子代码实现,EngineBase类就在此中界说。
- ./config/目次:界说了audio_policy_configuration.xml文件的参考模版。
- ./common/目次:界说了AudioPolicyManager模块所利用的一些数据布局,比如HwModule、DeviceDescriptor、AudioOutputDescriptor等。会被编译打包到libaudiopolicycomponents.so库中。
四、HAL层
我以为AudioHAL层可以分为三大部分:
- 第一部分是HIDL署理端的实现。源码位于:/frameworks/av/media/libaudiohal/目次。它供AudioFlinger来调用,运行在调用方AudioServer进程中。通过Binder方式与HIDL服务端通讯。
- 第二部分是HIDL服务端的实现。源码位于:/hardware/interfaces/audio/目次。包罗了音频HIDL接口文件的界说和服务端的实今世码。
- 第三部分是提供给SoC厂商的实现接口界说。由SoC厂商根据自己芯片的特点举行实现。AOSP不提供实现源码。接口界说的源码位于:/hardware/libhardware/include/hardware/audio.h
以上是HIDL署理端libaudiohal的代码实现类图。从类图中可以看出,由DevicesFactoryHalHidl负责创建Device对象,再由DeviceHalHidl来创建Stream对象。
device的真正实现是在DeviceHalHidl.cpp文件中。stream的真正实现是在StreamHalHidl.cpp文件中。
HIDL服务端的启动代码是在/hardware/interfaces/audio/common/all-versions/default/service/目次中,它是Audio HAL进程的启动总入口。有对应的rc文件。
HIDL服务端的实今世码是在/hardware/interfaces/audio/core/all-versions/default/目次中,包罗了DevicesFactory、Device和Stream的实现。正如前面所说,它们着实也是一个署理,只是调用audio.h中界说的接口。真正的实现还是由SoC厂商完成的。
|