拨打电话的SIM卡无效导致蓝牙断连

安卓蓝牙如何实现断线重连(安卓源码避坑指南3)(1)

它来了、它来了,它带着BUG赶来了,欢迎大家查看本期的安卓源码避坑指南。本期的问题场景比较特殊,电话SIM卡是无效的(欠费过期了,很是贫穷…)。

开开心心地上着班、摸着鱼(笑脸),突然工作邮件提醒,一经查看原来是QA同学甩过来个问题。那咱就话不多说,先简单介绍下本篇的问题场景。

测试环境:Android 9的车机系统 手机中的SIM卡为无效SIM卡

测试步骤

  1. 车机和手机使用蓝牙相互连接成功
  2. 车机端主动拨打电话

测试现象:由于手机中SIM卡为无效电话卡所以电话拨打失败,但是几秒钟过后手机与车机间的蓝牙连接断开

当看到这么个场景 问题现象后,我是懵逼的,打个电话蓝牙连接就断开,这么牛逼的吗?经过分析logcat发现蓝牙服务进程号有改变,那问题原因指向就很明显了:

由于测试步骤中不涉及开关蓝牙操作,那肯定就是蓝牙服务进程crash。根据这个基本判断提取相关crash-log做进一步分析。

安卓蓝牙如何实现断线重连(安卓源码避坑指南3)(2)

从以上log明显看到程序运行过程中遇到空指针调用从而引发 com.android.bluetooth 进程crash,导致蓝牙连接断开的问题根因crash找到了,那是怎么触发这个crash的呢?我们试着从源码分析下这块的流程。

首先查看snoop文件,由于问题产生时只有拨打电话的操作,所以分析下AT命令的交互:

安卓蓝牙如何实现断线重连(安卓源码避坑指南3)(3)

从HCI上可以看出AT交互很简单,就是一条ATD指令请求拨打电话,由于手机SIM卡无效,10s手机回复了错误的AT命令。10s的时间设定是因为手机蓝牙服务HeadsetService. dialOutgoingCall( )在收到AT拨打电话指令后会给电话模块发送对应的广播并设置定时器:

安卓蓝牙如何实现断线重连(安卓源码避坑指南3)(4)

手机端拨打电话10s超时后发送 AT_RESPONSE_ERROR 回复车机蓝牙,那车机收到这条错误指令后是怎么处理才导致crash了呢?咱就跟着程序分析下去,处理的时序图如下:

安卓蓝牙如何实现断线重连(安卓源码避坑指南3)(5)

由于这个特殊场景使得HfpClientConnectionService对于这次拨打电话的操作而言第一次接收到的ACTION_CALL_CHANGED中call.state就是CALL_STATE_TERMINATED,从而初始化HfpClientConnection时调用了close()将mCurrentCall又置为空值,这样才遇到mCurrentCall.getNumber() 对空指针取值引发crash。

分析到这里我们都知道mCurrentCall的值为空的原因了,那对应的解决方案就请大家集思广益在评论区一起交流讨论吧!

经过这样类似问题的回溯,我不禁对QA测试工程师越发佩服,因为使用的手机中SIM卡是无效卡的情况几乎不可能遇到,那对于本期分析的问题就很难被发现。正是QA们天马行空的魔鬼操作才让这些问题提前暴露出来,让蓝牙系统整体上更稳定,给测试大佬们敬茶 -_-。

本期是安卓源码避坑指南系列的第三篇文章了,想了解更多安卓源码bug的同学可以翻看以前的文章,也欢迎感兴趣的小伙伴私信留言共同学习。

,