夏龙根
(中国移动通信集团广东有限公司佛山分公司,广东 佛山 528000)
目前市面上大多数手机具有两个或两个以上卡槽。DSDS(Dual SIM Dual Standby)双卡双待几乎是现在市面上手机的标配,双卡双待指手机可以插入两张手机卡,而且能同时待机。其中一张卡用于通话,一张卡用于上网。或者到异地上学或者调任,新办一张当地的卡,而不想放弃原先的号码[1]。
由于一台手机有两张卡或者多张卡同时在线,不同的卡用途有区别,有时需要辨识不同卡槽号码的网络信号,如卡槽1 是语音功能,质量不好;而卡槽2 是数据功能,质量尚可。这就需要识别两张卡所使用的网络信息。Cellular-Z 是常用的识别网络信号的APP[2]。但这是一款网络优化专业工具,一般用户使用存在难度。为此,本文从Xamarin.Android开发的角度,提出一种卡槽信号识别的实现方法。
最初的Android 系统不支持双卡双待功能,Android SDK 也没有提供相应的API。Android6.0 以后增加了相关的API,可以利用反射获取副卡等相关信息。但这种反射的方法实现起来较为繁琐,且芯片商不一样,实现方案也不一样。如部分厂商直接在TelephonyManager 类中重载可以得到simid,而华为高端机型使用了自身的芯片,第二张sim 卡的相关API封装在Android.Telephony.MSimTelephonyManager 这个类[3]。因此,现有的反射法,难以做到代码通用。
在Android 系统中,有slotid 和subid 两个概念。其中slotid 是指卡槽,双卡机器的卡槽1 值为0,卡槽2 值为1,依次类推;subid(Subscription Identifier)是数据库telephony.db(在"/data/user_de/0/com.android.providers.telephony/databases"目录下)的表siminfo 的主键递增项,subid 的值从1 开始,每插入一个新卡,subId 的值就会加1。简言之,slotid 区分卡槽,subid 区分卡槽内的卡[4]。
每一张SIM 卡都对应一个Subscription,其 中Android.Telephony.SubscriptionManager.ActiveSubscriptionInfoList 是SIM卡的信息接口函数,包含如ICCID、MNC、MCC 等信息,ActiveSubscriptionInfo 中有记录用于语音的默认SIM 卡信息DefaultVoiceSubscription、短信默认卡信息DefaultSmsSubscription、数据业务默认卡信息DefaultDataSubscription 等等[5],如图1 所示。
图1 SIM 卡及卡槽相关接口方法和字段的逻辑层级关系
本文基于Android 10 以上的信号监听Android.Telephony.SignalStrength 类及 Android.Telephony.ServiceState 类,通过识别不同卡槽的信号强度和小区占用信息[6],并关联小区的CGI 及邻区信息,实现对不同卡槽的网络信号识别和代码实现。
1)获取注册小区信息。
ServiceState 类中的NetworkRegistrationInfo 方法可以获取收集的小区注册信息,包括网络类型transportType(其中WLAN 表示wifi,WWAN 表示移动网络)、注册状态registrationState(HOME 表示已注册)、网络类型accessNetworkTechnology(GSM/LTE/NR 等),以及小区可提供的服务availableServices(如语音、短息或者数据业务)。
通过CellIdentity 方法可以获取小区的位置区码TAC、小区国家识别码MCC、频点、频段、全球识别码CGI(或者NCI),确定占用的小区。
2)获取小区信号强度。
SignalStrength 类中的CellSignalStrengths 方 法可以获取手机收集的服务小区信号强度,并通过GetType()得到该服务小区的网络制式:如占用的是GSM 小区,则通过可将CellSignalStrengths 强制转化为CellSignalStrengthGsm 来读取GSM 小区的信号强度;如占用的是LTE 小区,则通过可将CellSignalStrengths 强制转化为CellSignalStrengthLte 来读取LTE 小区的信号强度。如下代码所示:
3)获取邻区信息。
Android.Telephony.NeighboringCellInfo 类是获取手机占用小区的相邻小区信息接口,但该方法返回数据为空,方法在android 系统中已被弃用。可以通过使用Android.Telephony.AllCellInfo 的方法将结果输出,并通过注册状态IsRegistered 决定哪个是邻居,其中值NO 表示邻区、YES 表示服务小区[7]。
通过AllCellInfo 方法还可以获取邻区的小区注册信息CellIdentity 以及信号强度CellSignalStrength。并且,可以通过mTimeStamp 获取小区的时间戳。对于4/5 G 小区,由于采用GPS 同步,通过该字段可以获得精准的时间信息。
基于Android 10 以上的通用API 接口类,实现分卡槽的注册小区信息、信号强度以及对应的邻区信息。Xamarin.Android 提供类似于Android 的接口函数,并且可以在微软Visual Studio 开发环境完成代码及编译。
通过ActiveSubscriptionInfoList 获取手机的可用卡槽列表,再根据每个卡槽的ServiceState 和SignalStrength 方法获取注册小区及信号强度信息,并根据AllCellInfo 查找属于该卡槽注册小区的相邻小区信息,所述的Android 实现流程如图2 所示。
图2 分卡槽获取网络信息的Android 实现流程
1)注册小区的读取方法。
服务小区ServiceState.NetworkRegistrationInfoList返回的是一个ILIST 列表,说明手机可能占用多个小区;小区信号强度SignalStrength.CellSignalStrengths也是一个ILIST 列表。这就涉及到多个小区和多个信号强度的匹配问题。
从实践看,一般可根据两个ILIST 的顺序匹配,即第一个注册小区对应第一个信号强度,最后一个注册小区对应最后一个信号强度。从程序实现上,也可以通过一些限制条件校验,通过判断NetworkRegistrationInfoList 中的accessNetworkTechnology与CellSignalStrengths 中的GetType()是否对应。即若accessNetworkTechnology 值为NR,则CellSignalStrengths中的GetType()对应typeof(CellSignalStrengthNr);若accessNetworkTechnology 值为LTE,则对应typeof(CellSignalStrengthLte)。
另一个校验方法是通过NetworkRegistrationInfoList中的availableServices,判断注册小区可提供的服务是否与ActiveSubscriptionInfo 中的默认SIM 卡信息匹配。若SIM 卡默认为语音卡DefaultVoiceSubscriptionId,则availableServices 中应包含VOICE;若默认为数据卡DefaultDataSubscription,则availableServices 中应包含DATA。
对于占用服务小区数量超过1 的情况,即ILIST列表长度大于1,一般为终端占用了5 G NSA 小区,这种情况下一个服务小区是NR,一个是LTE。
2)相邻小区读取方法。
由于NeighboringCellInfo 类已被弃用,通过使用AllCellInfo 获取相邻小区信息。但AllCellInfo 类提供了所有小区的汇总,包括所有卡槽的服务小区和邻小区。如前所述,服务小区和邻小区通过注册状态IsRegistered 区别,卡槽的区分没有明显的特征,但可以通过顺序方法读取。
写一个简单的AllCellInfo 信息打印程序,输出所有小区信息列表,就可以发现Android 的AllCellInfo是按照卡槽的顺序排列的,同一卡槽的小区依次排列,首先是mRegistered=YES 的服务小区,接下来是mRegistered=NO 的邻区。当该卡槽小区信息(包括服务小区和邻区)输出完以后,再输出下一个卡槽信息,AllCellInfo 打印信息如下:
所以,在读物卡槽对应邻区时,可以先从AllCellInfo 获取所有小区列表,并按顺序保存,从头到尾一次读取。当读取到mRegistered=YES,且对应的mCi 和对应服务小区的CGI 相等时,后续的邻区就是该卡槽所占用服务小区的邻区列表;当读到列表末尾,或者下一条mRegistered=YES,且对应的mCi 和对应服务小区的CGI 不相等时,读取动作停止,最终得到卡槽对应的邻区列表。
通过现有Android 系统的公开接口函数方法,实现多卡槽环境下的网络信息匹配识别,解决了Android 系统是不支持双卡双待功能。期间还提供了基于Xamarin.Android 的实现方法流程及关键代码。相比较常见的反射法获取占用小区信息,本文所述方法是基于公开的接口函数,具有更强的通用性,不因手机终端芯片不同而出现较大区别,可以部署于大多数Android 手机。