问题描述:
Modem已经注册成功了,但是android系统显示仍然是无网状态。
背景知识:
Android为了屏蔽各Modem厂商之间的差异,并未直接使用AT指令,而是定义了通用接口——RIL命令来控制Modem(当然,RIL命令与AT指令之间存在某种不固定的映射关系,因厂商而异)。各Modem厂商则负责提供对应于RIL命令的Vender RIL。RIL是Modem的HAL(硬件抽象),是android系统与Modem之间的通信桥梁,为AP提供了控制Modem的无线接口,RIL实现了Android平台与Modem间的分离。
Android的通信栈共由4层构成:
1、应用程序——利用通信框架提供的API访问Modem;
2、通信框架——为上层提供API,负责将上层请求转成RIL指令,通过UNIX SOCKET(该socket可在/dev/socket/目录下找到,名为rild)方式传递到RIL守护进程(通常双卡系统会有两个rild进程,可通过“ps | grep rild”命令查看),并处理来自RIL进程的回复,将结果传递给上层;
3、RIL层————RIL守护进程利用Vender RIL控制Modem;
4、Vender RIL——是实际控制Modem的软件,由厂商提供;实现了android定义的标准Modem控制接口。它是*.so形式的共享库。
问题确认:
1、Radio日志包含了RIL层与Modem之间的交互信息(RIL指令),以及android通讯框架层的所有相关日志信息;Modem日志记录了其与基站的所有交互(鉴权)过程。因此分析通讯相关问题,Radio日志和Modem日志都是关键资料。
2、通过获取并分析Radio和Modem日志,确认是两边状态未同步所致(Modem日志明确显示已经通过鉴权注册成功)。
探索思路:
首先我们认定这是android系统通信框架的BUG,所以希望通过直接与Modem通信,而绕开android系统的通信框架。 理论上,的确是可以绕过android系统的通讯框架,而直接与RIL层通信,并通过RIL层发指令对Modem进行控制的; 但是首先,需要你伪装成Radio进程,其次一旦你的程序与RIL守护进程建立连接,那么android系统的通讯框架(包括上层Phone应用)将会与RIL失去连接,以致于功能失效; 因此,你的程序务必功能足够完善,能够模拟android通讯框架的绝大部分关键功能,其次你的程序还需要足够健壮,保证不会导致系统Crash。 顺着这个思路,实现的难度之大、开发调试的周期之长,可想而知。并且,有点“重复造轮子”的意味。因此,调试android系统框架层、及RIL层代码成为最(甚至是唯一)可行的方向。
问题解决:
由于笔者所参与开发的android设备,采用了虚拟sim卡(软sim)技术,所以发起扫卡和注册的时间并不固定。 通常的android设备只会在开机,或者打开飞行模式再关闭时,触发扫卡和注册,但采用虚拟sim卡技术的android设备,可能在任意时间点触发。基于这个差异,笔者发现了android通信框架与Modem状态未同步问题的必现场景——在开机等待了一定时间(超过1分半钟)后,再发起注册。
而后经过不懈努力,终于找到了AP侧与BP侧状态不同步的蛛丝马迹——有一个状态取值on和off的变量radio(可能象征了设备的无线射频是否开启),决定框架层是否会调用“> OPERATOR”等这些RIL指令去查询Modem的实际注册状态。如果radio的状态为off,框架层的确是不会发起注册结果相关的任何查询的,而在AP和BP采用异步通信的场景下,不查询意味着即使Modem注册成功,android系统也是不会知道的。
而正好发现,笔者开发的设备在开机等待了一定时间后radio的状态就off,且不会再自己on了。于是抓住radio状态这个关键点做文章,最终把这个问题成功修复了。
问题总结:这是android系统的一个BUG(或者说android系统并未考虑到虚拟sim卡这种应用场景)