<div id="post_detail">
<div class="post">
<h2>
<a id="cb_post_title_url" href="https://www.cnblogs.com/blogs-of-lxl/p/8656286.html">Android : android 8.0 audio 接口分析</a>
</h2>
<div id="cnblogs_post_body" class="blogpost-body" deep="3"><h4 id="1hidl-的概念" style="margin: 8px 0px 16px; padding: 0px; color: #4f4f4f; text-transform: none; line-height: 28px; text-indent: 0px; letter-spacing: normal; font-family: -apple-system, 'SF UI Text', Arial, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'WenQuanYi Micro Hei', sans-serif, SimHei, SimSun; font-size: 20px; font-style: normal; font-weight: bold; word-spacing: 0px; white-space: normal; box-sizing: border-box; orphans: 2; widows: 2; background-color: #ffffff; font-variant-ligatures: normal; font-variant-caps: normal; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"><strong>1、HIDL 的概念</strong></h4>
HIDL 读作 hide-l,全称是 Hardware Interface Definition Language。它在 Android Project Treble 中被起草,在 Android 8.0 中被全面使用,其诞生目的是使 Android 可以在不重新编译 HAL 的情况下对 Framework 进行 OTA 升级。
使用 HIDL 描述的 HAL 描述文件替换旧的用头文件描述的 HAL 文件的过程称为 * HAL 的 binder 化(binderization)。所有运行 Android O 的设备都必须只支持 binder 化后的 HAL 模块。
已发布的 HIDL package包位于 Android 代码库的hardware/interfaces/
或vendor/<vendorName>
目录下。使用 HDIL 描述的 HAL 接口存放在这些目录下的.hal
文件中。比如我们可以在hardware/interfaces/audio/2.0/
目录下找到部分 Audio HAL 描述文件,如下:
Android.bp
Android.mk
IDevice.hal
IDevicesFactory.hal
IPrimaryDevice.hal
IStream.hal
IStreamIn.hal
IStreamOutCallback.hal
IStreamOut.hal
types.hal
另外在frameworks/av/media/下多了个文件夹 libaudiohal :
Android.mk DeviceHalLocal.h DevicesFactoryHalLocal.h EffectHalHidl.h EffectsFactoryHalLocal.h StreamHalLocal.h
ConversionHelperHidl.cpp DevicesFactoryHalHidl.cpp EffectBufferHalHidl.cpp EffectHalLocal.cpp HalDeathHandlerHidl.cpp
ConversionHelperHidl.h DevicesFactoryHalHidl.h EffectBufferHalHidl.h EffectHalLocal.h include
DeviceHalHidl.cpp DevicesFactoryHalHybrid.cpp EffectBufferHalLocal.cpp EffectsFactoryHalHidl.cpp StreamHalHidl.cpp
DeviceHalHidl.h DevicesFactoryHalHybrid.h EffectBufferHalLocal.h EffectsFactoryHalHidl.h StreamHalHidl.h
DeviceHalLocal.cpp DevicesFactoryHalLocal.cpp EffectHalHidl.cpp EffectsFactoryHalLocal.cpp StreamHalLocal.cpp
从文件名命名方式来看,一类是以Hidl结尾,一类是Local结尾,Local结尾的应该是兼容之前的方式,即谷歌在文档里描述的:
HIDL 旨在用于进程间通信 (IPC)。进程之间的通信经过 Binder 化。对于必须与进程相关联的代码库,还可以使用直通模式(在 Java 中不受支持):
要将运行早期版本的 Android 的设备更新为使用 Android O,您可以将惯用的(和旧版)HAL 封装在一个新 HIDL 接口中,该接口将在绑定式模式和同进程(直通)模式提供 HAL。这种封装对于 HAL 和 Android 框架来说都是透明的。直通模式仅适用于 C++ 客户端和实现。运行早期版本的 Android 的设备没有用 Java 编写的 HAL,因此 Java HAL 自然而然经过 Binder 化。
直通式标头文件:
编译 .hal
文件时,除了用于 Binder 通信的标头之外,hidl-gen
还会生成一个额外的直通标头文件 BsFoo.h
;此标头定义了会被执行 dlopen
操作的函数。由于直通式 HAL 在它们被调用的同一进程中运行,因此在大多数情况下,直通方法由直接函数调用(同一线程)来调用。oneway
方法在各自的线程中运行,因为它们不需要等待 HAL 来处理它们(这意味着,在直通模式下使用 oneway
方法的所有 HAL 对于线程必须是安全的)。
如果有一个 IFoo.hal
,BsFoo.h
会封装 HIDL 生成的方法,以提供额外的功能(例如使 oneway
事务在其他线程中运行)。该文件类似于 BpFoo.h
,不过,所需函数是直接调用的,并未使用 Binder 传递调用 IPC。未来,HAL 的实现可能提供多种实现结果,例如 FooFast HAL 和 FooAccurate HAL。在这种情况下,系统会针对每个额外的实现结果创建一个文件(例如 PTFooFast.cpp
和 PTFooAccurate.cpp
)。
2、Audio Record 调用分析:
(1) Java层调用Android的SDK中的API实例化一个AudioRecord对象
-》 \android-8.0.0_r4\frameworks\av\media\libaudioclient\AudioRecord.cpp -》 AudioRecord::AudioRecord
(2) 设置相应参数
-》 mStatus = set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user,
notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags,
uid, pid, pAttributes);
(3)打开录音接口
-》 // create the IAudioRecord
status_t status = openRecord_l(0 /*epoch*/, mOpPackageName);
(4) 获取输入设备属性:
status = AudioSystem::getInputForAttr(&mAttributes, &input,
mSessionId,
// FIXME compare to AudioTrack
mClientPid,
mClientUid,
&config,
mFlags, mSelectedDeviceId, &mPortId);
其中是通过获取audio_policy_service建立binder接口:
// establish binder interface to AudioPolicy service
const sp<IAudioPolicyService> AudioSystem::get_audio_policy_service()
(5) 调用AudioPolicyManager的接口 : \android-8.0.0_r4\frameworks\av\services\audiopolicy\managerdefault\AudioPolicyManager.cpp
aps->getInputForAttr -》 AudioPolicyManager::getInputForAttr
(6) 根据app传下的参数获取对应的设备类型:
device = getDeviceAndMixForInputSource(inputSource, &policyMix);
-》Engine::getDeviceForInputSource (\android-8.0.0_r4\frameworks\av\services\audiopolicy\enginedefault\src\Engine.cpp)
一般录音软件是:AUDIO_SOURCE_MIC ,google 语音引擎是:AUDIO_SOURCE_VOICE_RECOGNITION
再根据当前系统支持的输入设备返回对应的录音设备:(默认的内置mic就是 AUDIO_DEVICE_IN_BUILTIN_MIC)
if (mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO &&
availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
} else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { device = AUDIO_DEVICE_IN_WIRED_HEADSET; } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_HEADSET) { device = AUDIO_DEVICE_IN_USB_HEADSET; } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) { device = AUDIO_DEVICE_IN_USB_DEVICE; } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { device = AUDIO_DEVICE_IN_BUILTIN_MIC; }
PS:通过 dumpsys media.audio_policy 指令查看当前系统所支持的设备模块及类型。
可通过\android-8.0.0_r4\frameworks\av\services\audiopolicy目录里面 audio_policy.conf或者audio_policy_configuration.xml配置设备加载,
使用.conf还是.xml取决于frameworks/av/services/audiopolicy/Android.mk 通过宏USE_XML_AUDIO_POLICY_CONF - 1 : 使用 .xml , 0: 使用 .conf。
(7) 根据返回的device类型获取对应录音设备:
*input = getInputForDevice(device, address, session, uid, inputSource,
config->sample_rate, config->format, config->channel_mask, flags,
policyMix);
(8) 调用getInputProfile函数根据传进来的声音采样率、声音格式、通道掩码等参数与获得的设备支持的Input Profile比较返回一个与设备Profile匹配的IOProfile-》
profile = getInputProfile(device, address,
profileSamplingRate, profileFormat, profileChannelMask,
profileFlags);
(9) 根据返回的profile调用初始化时加载好的client接口:
status_t status = mpClientInterface->openInput(profile->getModuleHandle(),
&input,
&config,
&device,
address,
halInputSource,
profileFlags);
PS: mpClientInterface是由AudioPolicyService中加载对应模块传递过来:
void AudioPolicyService::onFirstRef() { { Mutex::Autolock _l(mLock);</span><span style="color: #008000;">//</span><span style="color: #008000;"> start tone playback thread</span> mTonePlaybackThread = <span style="color: #0000ff;">new</span> AudioCommandThread(String8(<span style="color: #800000;">"</span><span style="color: #800000;">ApmTone</span><span style="color: #800000;">"</span>), <span style="color: #0000ff;">this</span><span style="color: #000000;">); </span><span style="color: #008000;">//</span><span style="color: #008000;"> start audio commands thread</span> mAudioCommandThread = <span style="color: #0000ff;">new</span> AudioCommandThread(String8(<span style="color: #800000;">"</span><span style="color: #800000;">ApmAudio</span><span style="color: #800000;">"</span>), <span style="color: #0000ff;">this</span><span style="color: #000000;">); </span><span style="color: #008000;">//</span><span style="color: #008000;"> start output activity command thread</span> mOutputCommandThread = <span style="color: #0000ff;">new</span> AudioCommandThread(String8(<span style="color: #800000;">"</span><span style="color: #800000;">ApmOutput</span><span style="color: #800000;">"</span>), <span style="color: #0000ff;">this</span><span style="color: #000000;">); mAudioPolicyClient </span>= <span style="color: #0000ff;">new</span> AudioPolicyClient(<span style="color: #0000ff;">this</span><span style="color: #000000;">); mAudioPolicyManager </span>=<span style="color: #000000;"> createAudioPolicyManager(mAudioPolicyClient); <span style="font-family: Courier New;">//对应的模块加载放在AudioPolicyManager的构造函数中</span> } </span><span style="color: #008000;">//</span><span style="color: #008000;"> load audio processing modules</span> sp<AudioPolicyEffects>audioPolicyEffects = <span style="color: #0000ff;">new</span><span style="color: #000000;"> AudioPolicyEffects(); { Mutex::Autolock _l(mLock); mAudioPolicyEffects </span>=<span style="color: #000000;"> audioPolicyEffects; }
}