背景
幾年前移必,藍牙外放問題一直是業(yè)務(wù)的痛點,工單多毡鉴,原因雜崔泵,檢測難,分析慢猪瞬,經(jīng)過幾個版本的需求迭代后憎瘸,工單數(shù)下降超過了95%。
可是最近又有客戶反饋撑螺,在藍牙場景音頻通話過程中含思,被其他通話應(yīng)用打斷,就會出現(xiàn)藍牙外放問題甘晤。本篇用來分析該問題的原因和解決方法含潘。
分析過程
首先介紹下藍牙外放的本質(zhì)。對于Android應(yīng)用线婚,在通話音量下遏弱,應(yīng)用必須主動建立sco鏈接,這時候應(yīng)用才可以使用藍牙耳機進行采集和播放塞弊,如果不主動建立sco鏈接漱逸,那么采播通道就不會走藍牙,此時就出現(xiàn)了藍牙外放問題游沿∈问悖可是并不是應(yīng)用建立sco鏈接就一定會成功,Android內(nèi)部會針對通話音量的mode owner進行檢查诀黍, 如果當前應(yīng)用并不是mode owner袋坑,那么也會鏈接sco失敗。那么這時候就可以得到一個結(jié)論眯勾,出現(xiàn)藍牙外放問題枣宫,主要是如下2個原因:
- 應(yīng)用未建立sco鏈接
- 應(yīng)用建立sco鏈接失敗了
那接下來分析思路就清晰了,出現(xiàn)藍牙外放吃环,就從如上兩點進行排查即可也颤。
這時候需要先看audioservice的dump信息,對應(yīng)的命令是
adb shell dumpsys audio > audio.txt
截取時間點log如下:
02-22 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-22 19:24:43:714 setMode(MODE_IN_COMMUNICATION) from package=com.tencent.mobileqq pid=25280 selected mode=MODE_IN_COMMUNICATION by pid=25280
02-22 19:24:48:765 setMode(MODE_NORMAL) from package=com.tencent.mobileqq pid=25280 selected mode=MODE_IN_COMMUNICATION by pid=24442
02-22 19:24:49:292 setMode(MODE_IN_COMMUNICATION) from package=com.tencent.mobileqq pid=25280 selected mode=MODE_IN_COMMUNICATION by pid=25280
02-22 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-22 19:24:55:369 mode IN COMMUNICATION timeout for package=com.tencent.mobileqq pid=25280
02-22 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郁轻。
對應(yīng)的sco 鏈接軌跡如下:
02-22 19:24:32:058 startBluetoothSco()) from u/pid:11823/24442
02-22 19:24:41:264 startBluetoothSco()) from u/pid:10424/25280
02-22 19:24:48:761 stopBluetoothSco()) from u/pid:10424/25280
02-22 19:24:49:863 startBluetoothSco()) from u/pid:11823/24442
02-22 19:24:53:868 stopBluetoothSco()) from u/pid:11823/24442
02-22 19:24:57:877 startBluetoothSco()) from u/pid:11823/24442
02-22 19:25:01:883 stopBluetoothSco()) from u/pid:11823/24442
從log可以推斷問題軌跡如下:
- com.tencent.liteav.demo 先使用通話音量, 同時建立sco鏈接
- com.tencent.mobileqq 接入翅娶,也就是收到了 QQ語音電話,同時QQ也建立sco鏈接
- com.tencent.mobileqq 退出,也就是QQ語音電話掛掉故觅,同時QQ也結(jié)束sco鏈接
- com.tencent.liteav.demo 開始建立sco厂庇,然后停止sco進行循環(huán)
demo 只有在建立sco后,未收到sco鏈接成功事件才會嘗試重新鏈接sco输吏,那就可以得出如下結(jié)論:
- 應(yīng)用在通話音量下有進行sco鏈接
- 應(yīng)用鏈接sco失敗了
那接下來就需要看為什么sco鏈接失敗了权旷,這時候就可以參考下logcat日志,和正常case對比下就可以獲取到差異信息贯溅,先看鏈接sco正常時候的log:
02-22 19:24:32.058 24442 25180 I AudioManager: startBluetoothSco() packageName = com.tencent.liteav.demo
02-22 19:24:32.058 24442 25180 I AudioManager: In startbluetoothSco(), calling application: com.tencent.liteav.demo
02-22 19:24:32.058 1184 8982 I AS.AudioService: In startBluetoothSco()
02-22 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-22 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-22 19:24:32.058 971 2570 D APM_AudioPolicyManager: setVolumeCurveIndex device 00000004, index 10
02-22 19:24:32.058 1184 8982 I AS.AudioService: In startBluetoothScoInt(), scoAudioMode: -1
02-22 19:24:32.058 1184 8982 I AS.AudioDeviceBroker: In setSpeakerphoneOffForPid: 24442
02-22 19:24:32.058 1184 8982 I AS.AudioDeviceBroker: In updateSpeakerphoneOn(), mForcedUseForCommExt: 0
02-22 19:24:32.058 1184 8982 I AS.BtHelper: In requestScoState(), state: 12, scoAudioMode: -1
02-22 19:24:32.058 971 971 D AudioPolicyManagerCustom: setForceUse() usage 0, config 0, mPhoneState 3
02-22 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-22 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-22 19:24:57.874 24442 25180 I AudioManager: startBluetoothSco() packageName = com.tencent.liteav.demo
02-22 19:24:57.874 24442 25180 I AudioManager: In startbluetoothSco(), calling application: com.tencent.liteav.demo
02-22 19:24:57.874 1184 1240 I AS.AudioService: In startBluetoothSco()
02-22 19:24:57.876 1184 1240 I AS.AudioService: In startBluetoothScoInt(), scoAudioMode: -1
02-22 19:24:57.876 1184 1240 I AS.AudioDeviceBroker: In setSpeakerphoneOffForPid: 24442
02-22 19:24:57.876 1184 1240 I AS.AudioDeviceBroker: In updateSpeakerphoneOn(), mForcedUseForCommExt: 0
02-22 19:24:57.877 1184 1240 I AS.BtHelper: In requestScoState(), state: 12, scoAudioMode: -1
02-22 19:24:57.877 971 5575 D AudioPolicyManagerCustom: setForceUse() usage 0, config 0, mPhoneState 3
02-22 19:24:57.878 4819 5438 E HeadsetStateMachine: returning mCurrentState as Connected
02-22 19:24:57.879 1184 1240 I AS.BtHelper: In checkScoAudioState(), mScoAudioState: 0
02-22 19:24:57.879 1184 1240 W AS.BtHelper: requestScoState: audio mode is not NORMAL and modeOwnerPid 25280 != creatorPid 24442
可以看到有個關(guān)鍵信息:
!!#ff0000 02-22 19:24:57.879 1184 1240 W AS.BtHelper: requestScoState: audio mode is not NORMAL and modeOwnerPid 25280 != creatorPid 24442
!!
bthelper 報錯了拄氯,提示當前應(yīng)用非mode owner,那原因就清楚了它浅,由于當前應(yīng)用并不是modeowner译柏,所以鏈接sco不會成功。
從時間點上看姐霍,19:24:57 時QQ電話已經(jīng)打斷結(jié)束了鄙麦, 音量類型也被刷新成demo了,這時候mode owner 也應(yīng)該要更新為demo:
02-22 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镊折,因此可以判斷出是系統(tǒng)bug胯府。
那應(yīng)用如何解決呢?
其實知道原因后恨胚,解決辦法就比較順理成章了骂因,應(yīng)用只需要在建立sco前嘗試爭取成mode owner 就可以了,也就是設(shè)置通話音量類型赃泡。
按照這樣修改后寒波,客戶反饋藍牙外放問題得到了修復(fù)。