夏龍根
(中國(guó)移動(dòng)通信集團(tuán)廣東有限公司佛山分公司,廣東 佛山 528000)
目前市面上大多數(shù)手機(jī)具有兩個(gè)或兩個(gè)以上卡槽。DSDS(Dual SIM Dual Standby)雙卡雙待幾乎是現(xiàn)在市面上手機(jī)的標(biāo)配,雙卡雙待指手機(jī)可以插入兩張手機(jī)卡,而且能同時(shí)待機(jī)。其中一張卡用于通話,一張卡用于上網(wǎng)?;蛘叩疆惖厣蠈W(xué)或者調(diào)任,新辦一張當(dāng)?shù)氐目ǎ幌敕艞壴鹊奶?hào)碼[1]。
由于一臺(tái)手機(jī)有兩張卡或者多張卡同時(shí)在線,不同的卡用途有區(qū)別,有時(shí)需要辨識(shí)不同卡槽號(hào)碼的網(wǎng)絡(luò)信號(hào),如卡槽1 是語(yǔ)音功能,質(zhì)量不好;而卡槽2 是數(shù)據(jù)功能,質(zhì)量尚可。這就需要識(shí)別兩張卡所使用的網(wǎng)絡(luò)信息。Cellular-Z 是常用的識(shí)別網(wǎng)絡(luò)信號(hào)的APP[2]。但這是一款網(wǎng)絡(luò)優(yōu)化專業(yè)工具,一般用戶使用存在難度。為此,本文從Xamarin.Android開(kāi)發(fā)的角度,提出一種卡槽信號(hào)識(shí)別的實(shí)現(xiàn)方法。
最初的Android 系統(tǒng)不支持雙卡雙待功能,Android SDK 也沒(méi)有提供相應(yīng)的API。Android6.0 以后增加了相關(guān)的API,可以利用反射獲取副卡等相關(guān)信息。但這種反射的方法實(shí)現(xiàn)起來(lái)較為繁瑣,且芯片商不一樣,實(shí)現(xiàn)方案也不一樣。如部分廠商直接在TelephonyManager 類中重載可以得到simid,而華為高端機(jī)型使用了自身的芯片,第二張sim 卡的相關(guān)API封裝在Android.Telephony.MSimTelephonyManager 這個(gè)類[3]。因此,現(xiàn)有的反射法,難以做到代碼通用。
在Android 系統(tǒng)中,有slotid 和subid 兩個(gè)概念。其中slotid 是指卡槽,雙卡機(jī)器的卡槽1 值為0,卡槽2 值為1,依次類推;subid(Subscription Identifier)是數(shù)據(jù)庫(kù)telephony.db(在"/data/user_de/0/com.android.providers.telephony/databases"目錄下)的表siminfo 的主鍵遞增項(xiàng),subid 的值從1 開(kāi)始,每插入一個(gè)新卡,subId 的值就會(huì)加1。簡(jiǎn)言之,slotid 區(qū)分卡槽,subid 區(qū)分卡槽內(nèi)的卡[4]。
每一張SIM 卡都對(duì)應(yīng)一個(gè)Subscription,其 中Android.Telephony.SubscriptionManager.ActiveSubscriptionInfoList 是SIM卡的信息接口函數(shù),包含如ICCID、MNC、MCC 等信息,ActiveSubscriptionInfo 中有記錄用于語(yǔ)音的默認(rèn)SIM 卡信息DefaultVoiceSubscription、短信默認(rèn)卡信息DefaultSmsSubscription、數(shù)據(jù)業(yè)務(wù)默認(rèn)卡信息DefaultDataSubscription 等等[5],如圖1 所示。
圖1 SIM 卡及卡槽相關(guān)接口方法和字段的邏輯層級(jí)關(guān)系
本文基于Android 10 以上的信號(hào)監(jiān)聽(tīng)Android.Telephony.SignalStrength 類及 Android.Telephony.ServiceState 類,通過(guò)識(shí)別不同卡槽的信號(hào)強(qiáng)度和小區(qū)占用信息[6],并關(guān)聯(lián)小區(qū)的CGI 及鄰區(qū)信息,實(shí)現(xiàn)對(duì)不同卡槽的網(wǎng)絡(luò)信號(hào)識(shí)別和代碼實(shí)現(xiàn)。
1)獲取注冊(cè)小區(qū)信息。
ServiceState 類中的NetworkRegistrationInfo 方法可以獲取收集的小區(qū)注冊(cè)信息,包括網(wǎng)絡(luò)類型transportType(其中WLAN 表示wifi,WWAN 表示移動(dòng)網(wǎng)絡(luò))、注冊(cè)狀態(tài)registrationState(HOME 表示已注冊(cè))、網(wǎng)絡(luò)類型accessNetworkTechnology(GSM/LTE/NR 等),以及小區(qū)可提供的服務(wù)availableServices(如語(yǔ)音、短息或者數(shù)據(jù)業(yè)務(wù))。
通過(guò)CellIdentity 方法可以獲取小區(qū)的位置區(qū)碼TAC、小區(qū)國(guó)家識(shí)別碼MCC、頻點(diǎn)、頻段、全球識(shí)別碼CGI(或者NCI),確定占用的小區(qū)。
2)獲取小區(qū)信號(hào)強(qiáng)度。
SignalStrength 類中的CellSignalStrengths 方 法可以獲取手機(jī)收集的服務(wù)小區(qū)信號(hào)強(qiáng)度,并通過(guò)GetType()得到該服務(wù)小區(qū)的網(wǎng)絡(luò)制式:如占用的是GSM 小區(qū),則通過(guò)可將CellSignalStrengths 強(qiáng)制轉(zhuǎn)化為CellSignalStrengthGsm 來(lái)讀取GSM 小區(qū)的信號(hào)強(qiáng)度;如占用的是LTE 小區(qū),則通過(guò)可將CellSignalStrengths 強(qiáng)制轉(zhuǎn)化為CellSignalStrengthLte 來(lái)讀取LTE 小區(qū)的信號(hào)強(qiáng)度。如下代碼所示:
3)獲取鄰區(qū)信息。
Android.Telephony.NeighboringCellInfo 類是獲取手機(jī)占用小區(qū)的相鄰小區(qū)信息接口,但該方法返回?cái)?shù)據(jù)為空,方法在android 系統(tǒng)中已被棄用??梢酝ㄟ^(guò)使用Android.Telephony.AllCellInfo 的方法將結(jié)果輸出,并通過(guò)注冊(cè)狀態(tài)IsRegistered 決定哪個(gè)是鄰居,其中值NO 表示鄰區(qū)、YES 表示服務(wù)小區(qū)[7]。
通過(guò)AllCellInfo 方法還可以獲取鄰區(qū)的小區(qū)注冊(cè)信息CellIdentity 以及信號(hào)強(qiáng)度CellSignalStrength。并且,可以通過(guò)mTimeStamp 獲取小區(qū)的時(shí)間戳。對(duì)于4/5 G 小區(qū),由于采用GPS 同步,通過(guò)該字段可以獲得精準(zhǔn)的時(shí)間信息。
基于Android 10 以上的通用API 接口類,實(shí)現(xiàn)分卡槽的注冊(cè)小區(qū)信息、信號(hào)強(qiáng)度以及對(duì)應(yīng)的鄰區(qū)信息。Xamarin.Android 提供類似于Android 的接口函數(shù),并且可以在微軟Visual Studio 開(kāi)發(fā)環(huán)境完成代碼及編譯。
通過(guò)ActiveSubscriptionInfoList 獲取手機(jī)的可用卡槽列表,再根據(jù)每個(gè)卡槽的ServiceState 和SignalStrength 方法獲取注冊(cè)小區(qū)及信號(hào)強(qiáng)度信息,并根據(jù)AllCellInfo 查找屬于該卡槽注冊(cè)小區(qū)的相鄰小區(qū)信息,所述的Android 實(shí)現(xiàn)流程如圖2 所示。
圖2 分卡槽獲取網(wǎng)絡(luò)信息的Android 實(shí)現(xiàn)流程
1)注冊(cè)小區(qū)的讀取方法。
服務(wù)小區(qū)ServiceState.NetworkRegistrationInfoList返回的是一個(gè)ILIST 列表,說(shuō)明手機(jī)可能占用多個(gè)小區(qū);小區(qū)信號(hào)強(qiáng)度SignalStrength.CellSignalStrengths也是一個(gè)ILIST 列表。這就涉及到多個(gè)小區(qū)和多個(gè)信號(hào)強(qiáng)度的匹配問(wèn)題。
從實(shí)踐看,一般可根據(jù)兩個(gè)ILIST 的順序匹配,即第一個(gè)注冊(cè)小區(qū)對(duì)應(yīng)第一個(gè)信號(hào)強(qiáng)度,最后一個(gè)注冊(cè)小區(qū)對(duì)應(yīng)最后一個(gè)信號(hào)強(qiáng)度。從程序?qū)崿F(xiàn)上,也可以通過(guò)一些限制條件校驗(yàn),通過(guò)判斷NetworkRegistrationInfoList 中的accessNetworkTechnology與CellSignalStrengths 中的GetType()是否對(duì)應(yīng)。即若accessNetworkTechnology 值為NR,則CellSignalStrengths中的GetType()對(duì)應(yīng)typeof(CellSignalStrengthNr);若accessNetworkTechnology 值為L(zhǎng)TE,則對(duì)應(yīng)typeof(CellSignalStrengthLte)。
另一個(gè)校驗(yàn)方法是通過(guò)NetworkRegistrationInfoList中的availableServices,判斷注冊(cè)小區(qū)可提供的服務(wù)是否與ActiveSubscriptionInfo 中的默認(rèn)SIM 卡信息匹配。若SIM 卡默認(rèn)為語(yǔ)音卡DefaultVoiceSubscriptionId,則availableServices 中應(yīng)包含VOICE;若默認(rèn)為數(shù)據(jù)卡DefaultDataSubscription,則availableServices 中應(yīng)包含DATA。
對(duì)于占用服務(wù)小區(qū)數(shù)量超過(guò)1 的情況,即ILIST列表長(zhǎng)度大于1,一般為終端占用了5 G NSA 小區(qū),這種情況下一個(gè)服務(wù)小區(qū)是NR,一個(gè)是LTE。
2)相鄰小區(qū)讀取方法。
由于NeighboringCellInfo 類已被棄用,通過(guò)使用AllCellInfo 獲取相鄰小區(qū)信息。但AllCellInfo 類提供了所有小區(qū)的匯總,包括所有卡槽的服務(wù)小區(qū)和鄰小區(qū)。如前所述,服務(wù)小區(qū)和鄰小區(qū)通過(guò)注冊(cè)狀態(tài)IsRegistered 區(qū)別,卡槽的區(qū)分沒(méi)有明顯的特征,但可以通過(guò)順序方法讀取。
寫(xiě)一個(gè)簡(jiǎn)單的AllCellInfo 信息打印程序,輸出所有小區(qū)信息列表,就可以發(fā)現(xiàn)Android 的AllCellInfo是按照卡槽的順序排列的,同一卡槽的小區(qū)依次排列,首先是mRegistered=YES 的服務(wù)小區(qū),接下來(lái)是mRegistered=NO 的鄰區(qū)。當(dāng)該卡槽小區(qū)信息(包括服務(wù)小區(qū)和鄰區(qū))輸出完以后,再輸出下一個(gè)卡槽信息,AllCellInfo 打印信息如下:
所以,在讀物卡槽對(duì)應(yīng)鄰區(qū)時(shí),可以先從AllCellInfo 獲取所有小區(qū)列表,并按順序保存,從頭到尾一次讀取。當(dāng)讀取到mRegistered=YES,且對(duì)應(yīng)的mCi 和對(duì)應(yīng)服務(wù)小區(qū)的CGI 相等時(shí),后續(xù)的鄰區(qū)就是該卡槽所占用服務(wù)小區(qū)的鄰區(qū)列表;當(dāng)讀到列表末尾,或者下一條mRegistered=YES,且對(duì)應(yīng)的mCi 和對(duì)應(yīng)服務(wù)小區(qū)的CGI 不相等時(shí),讀取動(dòng)作停止,最終得到卡槽對(duì)應(yīng)的鄰區(qū)列表。
通過(guò)現(xiàn)有Android 系統(tǒng)的公開(kāi)接口函數(shù)方法,實(shí)現(xiàn)多卡槽環(huán)境下的網(wǎng)絡(luò)信息匹配識(shí)別,解決了Android 系統(tǒng)是不支持雙卡雙待功能。期間還提供了基于Xamarin.Android 的實(shí)現(xiàn)方法流程及關(guān)鍵代碼。相比較常見(jiàn)的反射法獲取占用小區(qū)信息,本文所述方法是基于公開(kāi)的接口函數(shù),具有更強(qiáng)的通用性,不因手機(jī)終端芯片不同而出現(xiàn)較大區(qū)別,可以部署于大多數(shù)Android 手機(jī)。