底层修改音频输出设备

近期项目上遇到一个问题,本想转给Audio同事,看着这题比较有意思,正好学习下Audio相关的内容。于是就把这题先hold在手里

问题描述如下:
预置条件:蓝牙耳机连接或者有线耳机插入
操作步骤:Setting中Sound中有一个开关选项,勾选上这个开关后,期望行为是来电铃声(提醒和铃声)只从蓝牙耳机或者有线耳机中发出。不勾选这个开关,则还是原先默认的效果,即从蓝牙耳机以及Speaker中同时发出

PS:蓝牙耳机断开时,该开关关闭且不可点击,蓝牙连接上,开关可点击。包括Setting中的开关UI,是之前已经完成,通过广播监听蓝牙耳机的状态,不在本文的讨论范围内

拿到这个问题最初第一反应是通过监听来电,上层是否有API可以调用来更改输出设备,查了些资料有两个API:android.media.AudioManager的setSpeakerphoneOn和setBluetoothScoOn。它们为通话状态(AudioSystem.FOR_COMMUNICATION)强制指定语音输入和输出设备。这个想法很快发现在我们的这个问题中行不通。因为这个功能不需要依托于任何界面以及点击操作,只要用户打开了Setting里的开关,退出setting后仍需要起效。下面尝试下通过改变底层音频输出的策略,顺便说一下,这里的开关的状态通过系统属性保存。

底层修改音频输出设备
AudioPolicy的核心文件之一
Engine.cpp
(frameworks/av/services/audiopolicy/enginedefault/src/Engine.cpp)
先来看其中的getStrategyForStream这个方法,方法头有一行注释stream to strategy mapping, 再结合这个方法名,不难看出该方法是根据Stream的Type来选择对应的strategy


底层修改音频输出设备_第1张图片

这里可以看出,来电铃声、警告、通知使用的都是strategy sonification

下面的这张图看起来更加直观一些,第一栏是流的类型,第二栏是音频策略


底层修改音频输出设备_第2张图片

顺理成章的我们往下看到了getDeviceForStrategy这个函数


底层修改音频输出设备_第3张图片

这个函数中可以看出,先是获取当前存在的设备集合availableOutputDevices,然后又调用了getDeviceForStrategyInt,
主要的工作在getDeviceForStrategyInt中,
下面我们来看这个函数

底层修改音频输出设备_第4张图片

底层修改音频输出设备_第5张图片

这个函数比较长,这里就截取了其中一部分,这个函数主要做的工作就是根据传入的strategty类型进行输出设备的选择。发现一个有意思的地方,大部分case在选择输出设备之前会先检测是否处于通话中这一特殊情况。然后就是根据优先级来匹配设备。
下表概略地反映了由策略到选择的设备的映射关系:

底层修改音频输出设备_第6张图片

这里我们看case STRATEGY_SONIFICATION,先判断是否处于通话中这个特殊状态,是的话输出策略设定为STRATEGY_PHONE,然后直接break出来,直接在这里修改似乎不好下手。
鉴于该情况,在getDeviceForStrategyInt中的Switch Case判断外,即该函数体最后return device上面添加如下:

底层修改音频输出设备_第7张图片

修改完执行mmm frameworks/av/services/audiopolicy/enginedefault,这里有一点需要注意的是,如果编译生成的so文件push到手机重启后发现没有生效,分别rm掉out目录下system/lib以及lib64下的libaudiopolicyenginedefault.so文件
重新mmm该模块,确保生成system/lib以及lib64的库文件,都push到手机中重启

底层修改音频输出设备_第8张图片

测试效果发现,来电时若setting中开关打开且耳机在位,speaker中没有声音输出,只从耳机端发出。

小结:
本文简单讲了下音频输出device的选择,总的说来就是audiopolicy会根据stream选择strategy,然后根据strategy选择输出device,即stream->strategy->device
主要涉及的文件以及方法如下:
/frameworks/av/services/audiopolicy/enginedefault/src/Engine.cpp

  1. routing_strategy Engine::getStrategyForStream(audio_stream_type_t stream)2. audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy, audio_output_flags_t flags)

PS:这种系统层的修改,修改不当引起的问题会比较严重,不像上层修改比较灵活不会破坏AudioPolicy原有的策略,所以务必谨慎再谨慎,最好有系统属性或者其它判断控制,让改变维持在只有自己需要的特定条件下才会走到,其它情况保持默认

本文只是通过解决这个问题过程中的一些记录,难免有错误的地方,欢迎邮件指正,我的邮箱[email protected]

你可能感兴趣的:(底层修改音频输出设备)