Android应用打断后的蓝牙外放问题分析

背景

几年前,蓝牙外放问题一直是业务的痛点,工单多,原因杂,检测难,分析慢,经过几个版本的需求迭代后,工单数下降超过了95%。

可是最近又有客户反馈,在蓝牙场景音频通话过程中,被其他通话应用打断,就会出现蓝牙外放问题。本篇用来分析该问题的原因和解决方法。

分析过程

首先介绍下蓝牙外放的本质。对于Android应用,在通话音量下,应用必须主动建立sco链接,这时候应用才可以使用蓝牙耳机进行采集和播放,如果不主动建立sco链接,那么采播通道就不会走蓝牙,此时就出现了蓝牙外放问题。可是并不是应用建立sco链接就一定会成功,Android内部会针对通话音量的mode owner进行检查, 如果当前应用并不是mode owner,那么也会链接sco失败。那么这时候就可以得到一个结论,出现蓝牙外放问题,主要是如下2个原因:

  • 应用未建立sco链接
  • 应用建立sco链接失败了

那接下来分析思路就清晰了,出现蓝牙外放,就从如上两点进行排查即可。

这时候需要先看audioservice的dump信息,对应的命令是

adb shell dumpsys audio > audio.txt

截取时间点log如下:

02-23 19:24:32:036 setMode(MODE_IN_COMMUNICATION) from package=com.tencent.liteav.demo pid=24442 selected mode=MODE_IN_COMMUNICATION by pid=24442
02-23 19:24:43:714 setMode(MODE_IN_COMMUNICATION) from package=com.tencent.mobileqq pid=25280 selected mode=MODE_IN_COMMUNICATION by pid=25280
02-23 19:24:48:765 setMode(MODE_NORMAL) from package=com.tencent.mobileqq pid=25280 selected mode=MODE_IN_COMMUNICATION by pid=24442
02-23 19:24:49:292 setMode(MODE_IN_COMMUNICATION) from package=com.tencent.mobileqq pid=25280 selected mode=MODE_IN_COMMUNICATION by pid=25280
02-23 19:24:55:335 setMode(MODE_NORMAL) from package=MSG_CHECK_MODE_FOR_UID pid=25280 selected mode=MODE_IN_COMMUNICATION by pid=24442
02-23 19:24:55:369 mode IN COMMUNICATION timeout for package=com.tencent.mobileqq pid=25280
02-23 19:25:04:586 setMode(MODE_NORMAL) from package=com.tencent.liteav.demo pid=24442 selected mode=MODE_NORMAL by pid=0

外放时间点是 19:24:49 到 19:25:04。

对应的sco 链接轨迹如下:

02-23 19:24:32:058 startBluetoothSco()) from u/pid:11823/24442
02-23 19:24:41:264 startBluetoothSco()) from u/pid:10424/25280
02-23 19:24:48:761 stopBluetoothSco()) from u/pid:10424/25280
02-23 19:24:49:863 startBluetoothSco()) from u/pid:11823/24442
02-23 19:24:53:868 stopBluetoothSco()) from u/pid:11823/24442
02-23 19:24:57:877 startBluetoothSco()) from u/pid:11823/24442
02-23 19:25:01:883 stopBluetoothSco()) from u/pid:11823/24442

从log可以推断问题轨迹如下:

  1. com.tencent.liteav.demo 先使用通话音量, 同时建立sco链接
  2. com.tencent.mobileqq 接入,也就是收到了 QQ语音电话,同时QQ也建立sco链接
  3. com.tencent.mobileqq 退出,也就是QQ语音电话挂掉,同时QQ也结束sco链接
  4. com.tencent.liteav.demo 开始建立sco,然后停止sco进行循环

demo 只有在建立sco后,未收到sco链接成功事件才会尝试重新链接sco,那就可以得出如下结论:

  • 应用在通话音量下有进行sco链接
  • 应用链接sco失败了

那接下来就需要看为什么sco链接失败了,这时候就可以参考下logcat日志,和正常case对比下就可以获取到差异信息,先看链接sco正常时候的log:

02-23 19:24:32.058 24442 25180 I AudioManager: startBluetoothSco() packageName = com.tencent.liteav.demo
02-23 19:24:32.058 24442 25180 I AudioManager: In startbluetoothSco(), calling application: com.tencent.liteav.demo
02-23 19:24:32.058  1184  8982 I AS.AudioService: In startBluetoothSco()
02-23 19:24:32.058   971  2570 D APM_AudioPolicyManager: setStreamVolumeIndex: stream AUDIO_STREAM_DTMF index: 10 attributes={ Content type: AUDIO_CONTENT_TYPE_UNKNOWN Usage: AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING Source: AUDIO_SOURCE_DEFAULT Flags: 0x0 Tags:  }
02-23 19:24:32.058   971  2570 D APM_AudioPolicyManager: setVolumeIndexForAttributes: group 4 matching with { Content type: AUDIO_CONTENT_TYPE_UNKNOWN Usage: AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING Source: AUDIO_SOURCE_DEFAULT Flags: 0x0 Tags:  }
02-23 19:24:32.058   971  2570 D APM_AudioPolicyManager: setVolumeCurveIndex device 00000004, index 10
02-23 19:24:32.058  1184  8982 I AS.AudioService: In startBluetoothScoInt(), scoAudioMode: -1
02-23 19:24:32.058  1184  8982 I AS.AudioDeviceBroker: In setSpeakerphoneOffForPid: 24442
02-23 19:24:32.058  1184  8982 I AS.AudioDeviceBroker: In updateSpeakerphoneOn(), mForcedUseForCommExt: 0
02-23 19:24:32.058  1184  8982 I AS.BtHelper: In requestScoState(), state: 12, scoAudioMode: -1
02-23 19:24:32.058   971   971 D AudioPolicyManagerCustom: setForceUse() usage 0, config 0, mPhoneState 3
02-23 19:24:32.059   971  2570 D APM_AudioPolicyManager: setStreamVolumeIndex: stream AUDIO_STREAM_DTMF index: 15 attributes={ Content type: AUDIO_CONTENT_TYPE_UNKNOWN Usage: AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING Source: AUDIO_SOURCE_DEFAULT Flags: 0x0 Tags:  }
02-23 19:24:32.059   971  2570 D APM_AudioPolicyManager: setVolumeIndexForAttributes: group 4 matching with { Content type: AUDIO_CONTENT_TYPE_UNKNOWN Usage: AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING Source: AUDIO_SOURCE_DEFAULT Flags: 0x0 Tags:  }

对比异常时候的log如下:

02-23 19:24:57.874 24442 25180 I AudioManager: startBluetoothSco() packageName = com.tencent.liteav.demo
02-23 19:24:57.874 24442 25180 I AudioManager: In startbluetoothSco(), calling application: com.tencent.liteav.demo
02-23 19:24:57.874  1184  1240 I AS.AudioService: In startBluetoothSco()
02-23 19:24:57.876  1184  1240 I AS.AudioService: In startBluetoothScoInt(), scoAudioMode: -1
02-23 19:24:57.876  1184  1240 I AS.AudioDeviceBroker: In setSpeakerphoneOffForPid: 24442
02-23 19:24:57.876  1184  1240 I AS.AudioDeviceBroker: In updateSpeakerphoneOn(), mForcedUseForCommExt: 0
02-23 19:24:57.877  1184  1240 I AS.BtHelper: In requestScoState(), state: 12, scoAudioMode: -1
02-23 19:24:57.877   971  5575 D AudioPolicyManagerCustom: setForceUse() usage 0, config 0, mPhoneState 3
02-23 19:24:57.878  4819  5438 E HeadsetStateMachine: returning mCurrentState as Connected
02-23 19:24:57.879  1184  1240 I AS.BtHelper: In checkScoAudioState(), mScoAudioState: 0
02-23 19:24:57.879  1184  1240 W AS.BtHelper: requestScoState: audio mode is not NORMAL and modeOwnerPid 25280 != creatorPid 24442

可以看到有个关键信息:
!!#ff0000 02-23 19:24:57.879 1184 1240 W AS.BtHelper: requestScoState: audio mode is not NORMAL and modeOwnerPid 25280 != creatorPid 24442
!!

bthelper 报错了,提示当前应用非mode owner,那原因就清楚了,由于当前应用并不是modeowner,所以链接sco不会成功

从时间点上看,19:24:57 时QQ电话已经打断结束了, 音量类型也被刷新成demo了,这时候mode owner 也应该要更新为demo:

02-23 19:24:55:335 setMode(MODE_NORMAL) from package=MSG_CHECK_MODE_FOR_UID pid=25280 selected mode=MODE_IN_COMMUNICATION by pid=24442

可是19:24:57 时候mode owner 还是qq,因此可以判断出是系统bug。

那应用如何解决呢?
其实知道原因后,解决办法就比较顺理成章了,应用只需要在建立sco前尝试争取成mode owner 就可以了,也就是设置通话音量类型。
按照这样修改后,客户反馈蓝牙外放问题得到了修复。

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
img
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题
图片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值