1. 程式人生 > >Android 雙卡雙待

Android 雙卡雙待

轉自這裡

一、雙卡雙待背景分析

使用者為了兼顧運營商優勢,使用雙卡雙待手機:

雙卡雙待這項技術在發展中國家使用很普遍,因為在發展中國家電信運營商發展不夠成熟,相關管理制度不完善。從使用者的角度出發,主要考慮資費問題,比如:移動通話訊號好,聯通3G上網流暢、流量費相對便宜,為了兼顧運營商的優勢,使用者選擇雙卡雙待手機。

運營商為了爭奪原有2G使用者,推出雙卡雙待手機:

原有國內六大運營商中國移動,中國聯通,中國電信,中國網通,中國鐵通和中國衛通,經過了整合與拆分,使用者在使用制式上各有不同包括GSM、TD-SCDMA、WCDMA、CDMA2000;其中GSM佔有量非常大,各運營商在推廣新業務時,不得不兼顧原有GSM使用者。例如電信如果手機只能使用電信卡,市場上推廣會有很多障礙,運營商通過推出雙卡雙待來解決這個問題。

雙卡雙待相關技術發展:

  • 兩套晶片,缺點是耗電量大、硬體成本高。這個相當於是兩個手機“粘”在一起。

  • 雙卡單待,需要手動切換,不是真正的雙卡雙待,使用者只能使用一張卡。相當於加了一個“開關”。

  • 雙卡雙待,使用晶片控制手機在兩個網路之間切換,因為切換速度很快使用者感覺是兩張卡在同時待機。是目前採用的技術手段。

二、雙卡雙待碎片化現狀

雙卡方案不統一:

Android本身碎片化問題嚴重,Google標準API中沒有雙卡雙待的支援,對於雙卡雙待實現方式業界也沒有標準,於是雙卡解決方案有了現在百花齊放、百家爭鳴的局勢。

涉及模組眾多:

作為一個智慧手機平臺,電話部分是Android系統的重點功能。電話部分主要功能包括:呼叫(Call)、簡訊(Sms)、資料連線(Data Connection)、Sim卡相關、電話本

等。儘管Android整個框架已經使用AIDL機制來解耦,使得各個模組之間儘可能的獨立,但對於開發者和產品人員來說,所有涉及到這些模組的功能點都會受到雙卡雙待的困擾。

已成事實標準的雙卡雙待逼迫主流廠家開始支援:

在過去雙卡雙待是山寨手機的一個代名詞,山寨機是最早發現這塊需求並快速響應;後面隨著使用者的普及,很多大手機廠商紛紛推出雙卡雙待手機,包括一些在國外市場只支援單卡的機型,也會在國內做好了雙卡雙待,才推向市場。

最新的HTC One在國內沒有單卡機器售賣,原本一體的機身設計,後經改裝加入雙卡雙待功能。三星S系列也逐漸鋪開雙卡機型,雙卡雙待已經不是小眾使用者,也不是低端使用者,這個現狀對於開發者是個繞不過的坎兒。如果要開發電話模組相關的功能,開發團隊必須要解決雙卡雙待的問題。

三、解決策略

雙卡雙待實現和真機ROM的Framework層實現密切相關,不同的手機實現方式不一樣,同一系列的手機實現的策略類似,但是可能有差別。下面介紹分析解決的過程:

1、【準備工作】

工具準備:smali 和 dex2jar

詳細操作流程請參見工具網站說明

https://code.google.com/p/dex2jar/

https://code.google.com/p/smali/downloads/list

材料準備:需要將適配的雙卡手機 system目錄下的framework 和 app目錄檔案提取出來備做反編譯用。

2、【分析過程】

通過檢視Android原始碼和分析真機反編譯程式碼可以看出Android電話模組架構主要核心機制是:一個服務(比如本文提到的電話服務)對應一個Manager管理類,一個Manager管理類對應一個AIDL介面(Android程序通訊介面),每個AIDL介面會對應一個具體服務功能的實現。

電話服務 —> TelephonyManager —>PhoneInterfaceManager(ITelephony)—>底層具體服務

TelephonyManager(電話服務管理類)

Android框架中有上下文(context)的概念,很多資源和系統服務可以直接從上下文中獲得,比如要獲得電話管理類(TelephonyManager)可以通過下面方式獲得:

context.getSystemService("phone");

我們通過context 的實現類ContextImpl可以獲取對電話管理類的訪問

@Override

public Object getSystemService(String name) {

ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);

return fetcher == null ? null : fetcher.getService(this);

}

所有的系統服務管理類,都註冊在SYSTEM_SERVICE_MAP中。

從上圖原始碼中可以看到,getSystemService方法最終是靠傳入對應服務的key從SYSTEM_SERVICE_MAP中獲得對應的管理類,這裡第一個坑爹點會出現,因為key對應的值,廠商會修改成各種值,比如有些手機是”phone2”,有些比如HTC手機是”htctelephony”,所以需要反編譯廠商原始碼去查詢這些key值,下邊會詳細講到。

ITelephony(電話相關的AIDL介面)

TelephonyManager中的具體操作大部分是通過ITelephony介面訪問的,比如獲得電話狀態的操作:

public class TelephonyManager {

...

public int getCallState() {

try {

return getITelephony().getCallState();

} catch (RemoteException ex) {

return CALL_STATE_IDLE;

} catch (NullPointerException ex) {

return CALL_STATE_IDLE;

}

}

...

}

ITelephony介面位於com.android.internal.telephony.*包中,這個包是支撐android.telephony.*對外介面的具體實現。

PhoneInterfaceManager (ITelephony的具體實現類,提供具體服務功能)

TelephonyManager大部分操作是通過ITelephony介面訪問的,也就是通過其實現類PhoneInterfaceManager 來實際獲得具體相關操作

public class PhoneInterfaceManager extends ITelephony.Stub {

...

public int getCallState() {

return DefaultPhoneNotifier.convertCallState(mCM.getState());

}

...

}

PhoneInterfaceManager 封裝了電話相關的具體服務操作,這些操作最終將訊息傳送到RIL.java中轉。

RIL.java和底層通訊

RIL.java是電話相關服務在Java層的資訊收發中心。

RIL.java使用socket和rild程序通訊,RILRequest是一個請求的封裝,通過RILSender將請求命令傳送出去,通過RILReceiver接收命令的響應和主動訊息。

經過上述的分析,可以總結出電話相關服務的架構。

3、【架構分析】

Android電話系統分為四個層次(見上圖)。

Phone應用程式層:主要包括電話服務和Phone應用程式。

Java Framework層:主要包括兩個程式包,其中:android.telephony.* 是framework對外溝通的介面,com.android.internal.telephony* 是和底層溝通的橋樑(socket )。

Native Framework層: Rild守護程序。

驅動層:與網路進行溝通傳輸。

為了支援雙卡雙待功能,廠商實現的Java Framework層在TelephonyManager的節點向下開始自定義API實現對第二張卡的支援,通過反編譯並分析真機的framework.jar可以得到自TelephonyManager以下廠商自定義的操作。然後,在App層通過反射呼叫對應的功能點就可以實現對兩張卡的操作。

4、【解決方案舉例】

通過對市面上大部分雙卡機型的反編譯分析看,雙卡雙待的解決方案可以總結為3類:

TelephonyManager異化:三星、Moto系列

這類真機中提供了兩個電話服務管理類,通過context.getSystemService("phone2")可以得到電話服務類中的第二張卡的電話服務管理類。這個方案屬於最有節操的,開發成本是最低的。

在android.app.ContextImpl中註冊上下文的各種管理類,以下為Google官方程式碼中的實現:

以下是反編譯三星 Node2 framework.jar得到的相關程式碼塊

registerService("phone", new ServiceFetcher()

{

public Object createService(ContextImpl paramContextImpl)

{

return new TelephonyManager(paramContextImpl.getOuterContext(), 0);

}

});

registerService("phone2", new ServiceFetcher()

{

public Object createService(ContextImpl paramContextImpl)

{

return new TelephonyManager(paramContextImpl.getOuterContext(), 1);

}

});

可以看出通過context.getSystemService("phone2"),就可以從上文中提到的SYSTEM_SERVICE_MAP拿到第二張卡的管理類,因為只是管理類例項不一樣,只需要拿到第二張卡的TelephonyManager例項,所有的方法都可以按照標準API正常使用。

方法異化:MTK晶片系列

MTK解決方案廣泛用於聯想以及中興廠商中,這類手機沒有新建Manager管理類,而是在管理類中新加入xxxGemini(int para)的方法,其中int引數代表卡槽,0為一卡槽,1為二卡槽。開發者需要通過反射來呼叫和標準API對應的Gemini方法來實現對應的操作。以下為反編譯 聯想A750 對應的獲得Sim卡狀態的方法具體實現。

public int getSimState()

{

return getSimStateGemini(getDefaultSim());

}

public int getSimStateGemini(int paramInt)

{......}

呼叫對應的Gemini方法可以實現對兩張卡的操作,比如:通過反射呼叫getSimStateGemini分別傳入0和1作為引數,就可以獲得兩張卡的SIM卡狀態。

新建Manager類:華為系列

這種方案比較隱蔽的,通過修改增加自己實現類完成對雙卡的支援,以下為華為C8825D的處理方案:

首先,得到對應的管理類

c = Class.forName("android.telephony.MSimTelephonyManager");

//其中MSimTelephonyManager類為廠商自定義的電話服務管理類

然後,通過反射呼叫對應的方法,可以成功的返回操作結果

method = c.getDeclaredMethod("getSimState", int.class);

四、總結

雙卡雙待手機的適配過程主要是分析“異化”點,找到在Framework層中是如何區分卡槽的,不同的手機實現不同,對於TelephonyManager相關的解決方案總結有以上三種。對於簡訊相關的操作位於SmsManager中,解決策略和TelephonyManager類似,不再詳細分析。

經過上面的分析過程可以知道,一個模組的功能點,適配開發過程中都需要進行下面三個步驟:

(1)反編譯系統framework.jar、phone.apk等找到對應功能不同卡槽的操作方式。

(2)在原生代碼中通過反射呼叫該方法。

(3)驗證操作是否成功。

過程相對比較複雜,需要反覆的試驗和驗證,而且每款手機都要這樣調查和解決一遍,因此開發成本是相當高的。