安卓源码避坑指南3——拨打电话的SIM卡无效导致蓝牙断连
它来了、它来了,它带着BUG赶来了,欢迎大家查看本期的安卓源码避坑指南。本期的问题场景比较特殊,电话SIM卡是无效的(欠费过期了,很是贫穷…)。
开开心心地上着班、摸着鱼(-_-),突然工作邮件提醒,一经查看原来是QA同学甩过来个问题。那咱就话不多说,先简单介绍下本篇的问题场景。
测试环境:Android 9的车机系统 + 手机中的SIM卡为无效SIM卡
测试步骤:
- 车机和手机使用蓝牙相互连接成功
- 车机端主动拨打电话
测试现象:由于手机中SIM卡为无效电话卡所以电话拨打失败,但是几秒钟过后手机与车机间的蓝牙连接断开
当看到这么个场景+问题现象后,我是懵逼的。打个电话蓝牙连接就断开,这么牛逼的吗?经过分析logcat发现蓝牙服务进程号有改变,那问题原因指向就很明显了:
- 用户主动开关蓝牙
- 蓝牙服务crash
由于测试步骤中不涉及开关蓝牙操作,那肯定就是蓝牙服务进程crash。根据这个基本判断提取相关crash-log做进一步分析。
Process: com.android.bluetooth
PID: 21997
Flags: 0x30c8be45
Package: com.android.bluetooth v28 (9)
Foreground: No
Build:系统版本号(此处省略。。。)
java.lang.RuntimeException: Error receiving broadcast Intent { act=android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED flg=0x10000010 (has extras) } in com.android.bluetooth.hfpclient.connserv.HfpClientConnectionService$1@72e6f2
at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0(LoadedApk.java:1401)
at android.app.-$$Lambda$LoadedApk$ReceiverDispatcher$Args$_BumDX2UKsnxLVrE6UJsJZkotuA.run(Unknown Source:2)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6718)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.bluetooth.BluetoothHeadsetClientCall.getNumber()' on a null object reference
at com.android.bluetooth.hfpclient.connserv.HfpClientConnection.finishInitializing(HfpClientConnection.java:92)
at com.android.bluetooth.hfpclient.connserv.HfpClientConnection.<init>(HfpClientConnection.java:61)
at com.android.bluetooth.hfpclient.connserv.HfpClientDeviceBlock.buildConnection(HfpClientDeviceBlock.java:253)
at com.android.bluetooth.hfpclient.connserv.HfpClientDeviceBlock.handleCall(HfpClientDeviceBlock.java:169)
at com.android.bluetooth.hfpclient.connserv.HfpClientConnectionService$1.onReceive(HfpClientConnectionService.java:122)
at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0(LoadedApk.java:1391)
... 8 more
从以上问题log明显看到程序运行过程中遇到空指针调用从而引发com.android.bluetooth进程crash,导致蓝牙连接断开的问题根因crash找到了,那是怎么触发这个crash的呢?我们试着从源码分析下这块的流程。
首先查看snoop文件,由于问题产生时只有拨打电话的操作,所以分析下AT命令的交互:
从HCI上可以看出AT交互很简单,就是一条ATD指令请求拨打电话,由于手机SIM卡无效,10s手机回复了错误的AT命令。10s的时间设定是因为手机蓝牙服务HeadsetService. dialOutgoingCall( )在收到AT拨打电话指令后会给电话模块发送对应的广播并设置定时器:
手机端拨打电话10s超时后发送AT_RESPONSE_ERROR回复车机蓝牙,那车机收到这条错误指令后是怎么处理才导致crash了呢?咱就跟着程序分析下去,处理的时序图如下:
由于这个特殊场景使得HfpClientConnectionService对于这次拨打电话的操作而言第一次接收到的ACTION_CALL_CHANGED中call.state就是CALL_STATE_TERMINATED,从而初始化HfpClientConnection时调用了close()将mCurrentCall又置为空值,这样才遇到mCurrentCall.getNumber()对空指针取值引发crash。
分析到这里我们都知道mCurrentCall的值为空的原因了,那对应的解决方案就请大家集思广益在评论区一起交流讨论吧!
经过这样类似问题的回溯,我不禁对QA测试工程师越发佩服,因为使用的手机中SIM卡是无效卡的情况几乎不可能遇到,那对于本期分析的问题就很难被发现。正是QA们天马行空的魔鬼操作才让这些问题提前暴露出来,让蓝牙系统整体上更稳定,给测试大佬们敬茶 -_-。
本期是安卓源码避坑指南系列的第三篇文章了,想了解更多安卓源码bug的同学可以翻看以前的文章,也欢迎感兴趣的小伙伴私信留言共同学习。