簡介
Android雙卡雙待已經越來越普及了,解決雙卡雙待管理是廣大手機開發人員必須得面對的問題,為實現Android平台的雙卡雙待操作,筆者研究了Android 應用程式層操作雙卡雙待的機制。
機制
擷取基於ITelephony介面實現phone應用中的“phone服務”,通過TelephonyManager介面擷取不同的卡(GSMPhone /CDMAPhone)進行不同的操作(撥號、接通、掛斷、保留等)。
Android平台是一個多樣型的平台,不同的手機擷取ITelephony介面不同,用一種方法實現雙卡雙待管理是不可取的。那怎麼辦呢?只有針對不同的手機分析出一套管理的方案,該方案實現難度大,因為需要各個廠家的SDK的資料。為了實現該功能,筆者做了大量工作,整合各個廠家的SDK的資料。
實現
為了更好的管理雙卡雙待的問題,建立一個雙卡雙待模組靜態庫,其它項目引用便是,項目
效果
小米手機 測試效果
華為手機測試效果
AbsSim是抽象類別,負責實現手機操作的類。不同的廠家繼承該類實現各自的介面。AbsSim資訊如下:
public abstract class AbsSim implements IDualDetector { //抽象基類protected final String TAG = getClass().getSimpleName();protected ArrayList<SimSlot> mSimSlots = new ArrayList<SimSlot>();protected boolean mIsDualSimPhone = false;protected String mCallLogExtraField = "";public abstract String getSimPhoneNumber(int paramInt); // 返回手機號碼public abstract int getDataState(int paramInt);// 返回資料狀態public abstract String getIMSI(int paramInt);// 返回手機標識public abstract String getIMSI(int paramInt, Context paramContext);// 返回手機標識public abstract int getPhoneState(int paramInt);// 返回手機狀態public abstract boolean isServiceAvaliable(int paramInt);// 服務是否可用public abstract boolean isSimStateIsReady(int paramInt);// 卡是否在使用public abstract int getSimOperator(int paramInt);// 服務商(電信、移動、聯通)protected abstract Object getITelephonyMSim(int paramInt);// 擷取操作介面protected abstract Object getMSimTelephonyManager(int paramInt);// 擷取操作介面@Overridepublic AbsSim detect() { // 根據手機資訊匹配if ((getITelephonyMSim(0) != null) && (getITelephonyMSim(1) != null)// && (getmMSimSmsManager(0) != null)// && (getmMSimSmsManager(1) != null)// && (detectSms(paramContext, paramBoolean))// && (detectCallLog(paramContext, paramBoolean)))return this;return null;}public boolean directCall(String paramString, int paramInt) { // 撥打到電話(根據不同卡撥打到電話)Intent localIntent = new Intent("android.intent.action.CALL",Uri.fromParts("tel", paramString, null));localIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);try {getContext().startActivity(localIntent);return true;} catch (Throwable localThrowable) {localThrowable.printStackTrace();}return false;}protected boolean detectCallLog() { // 通過通話記錄資訊匹配return false;}protected boolean detectSms() {// 通過簡訊記錄資訊匹配return false;}protected Context getContext() { // 返回控制代碼return SimManager.getInstance().getContext();}protected int getSimSlotNum() { // 返回插槽個數(預設2)return 2;}public void init() { // 初始化for (int i = 0; i < getSimSlotNum(); i++) {try {String imsi = getIMSI(i);boolean isUsing = isSimStateIsReady(i);if (imsi != null || isUsing) {if (imsi == null || hasSimSlotByIMSI(imsi))continue;SimSlot simSlot = new SimSlot();mSimSlots.add(simSlot);simSlot.setUsing(isUsing);simSlot.setIMSI(imsi);simSlot.setSimOperator(getSimOperator(i));}} catch (Exception e) {e.printStackTrace();}}}public boolean hasSimPhone() {// 是否有sd卡在使用if (mSimSlots.isEmpty())return false;for (SimSlot simslot : mSimSlots) {if (simslot.isUsing())return true;}return false;}public boolean isDualSimPhone() {// 是否為雙卡if (getSimSlots().isEmpty() || getSimSlots().size() < 2)return false;for (SimSlot simSlot : getSimSlots()) {if (!simSlot.isUsing())return false;}return true;}public ArrayList<SimSlot> getSimSlots() { // 返回已確認的卡return mSimSlots;}protected boolean delSimSlotByIMSI(String imsi) { // 刪除相同的卡的資訊for (SimSlot simSlot : getSimSlots()) {if (simSlot.getIMSI() != null && simSlot.getIMSI().equals(imsi)) {getSimSlots().remove(simSlot);}}return false;}protected boolean hasSimSlotByIMSI(String imsi) {for (SimSlot simSlot : getSimSlots()) {if (simSlot.getIMSI() != null && simSlot.getIMSI().equals(imsi)) {return true;}}return false;}}
現在列舉一款實現MTK方案:
public class MTKDualSim extends AbsSim {// 採用MTK方案的類(根據廠家SDK實現不同的介面)private Object mMSimTelephonyManager = null;private Object mTelephonyMSim = null;public MTKDualSim() {mCallLogExtraField = "simid";String str1 = SimManager.getModel();String str2 = SimManager.getManufaturer();if ((str1 != null) && (str2 != null)) {String str3 = str1.toLowerCase();String str4 = str2.toLowerCase();if ((str4.indexOf("huawei") > -1) && (str3.indexOf("h30-t00") > -1))mCallLogExtraField = "subscription";if ((str4.indexOf("hisense") > -1)&& (str3.indexOf("hs-u970") > -1)) {mCallLogExtraField = "subtype";}}}@Overridepublic boolean directCall(String paramString, int paramInt) {if (SimManager.isSDKVersionMore4_1()) {Intent localIntent1 = new Intent("android.intent.action.CALL",Uri.fromParts("tel", paramString, null));localIntent1.putExtra("simId", paramInt);localIntent1.putExtra("com.android.phone.extra.slot", paramInt);localIntent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);try {getContext().startActivity(localIntent1);return true;} catch (Throwable localThrowable1) {localThrowable1.printStackTrace();}} else if (SimManager.isSDKVersionMore4_0()) {Intent localIntent2 = new Intent("com.android.phone.OutgoingCallReceiver");localIntent2.putExtra("com.android.phone.extra.slot", paramInt);localIntent2.putExtra("simId", paramInt);localIntent2.putExtra("com.android.phone.force.slot", true);localIntent2.setClassName("com.android.phone","com.android.phone.OutgoingCallReceiver");localIntent2.setData(Uri.fromParts("tel", paramString, null));try {getContext().sendBroadcast(localIntent2);return true;} catch (Throwable localThrowable2) {localThrowable2.printStackTrace();}}try {Intent localIntent3 = new Intent();localIntent3.setAction("out_going_call_to_phone_app");localIntent3.putExtra("number", paramString);localIntent3.putExtra("simId", paramInt);localIntent3.putExtra("com.android.phone.extra.slot", paramInt);localIntent3.putExtra("launch_from_dialer", 1);localIntent3.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);getContext().sendBroadcast(localIntent3);return true;} catch (Throwable localThrowable3) {localThrowable3.printStackTrace();}return false;}@Overridepublic AbsSim detect() {String imsi = getIMSI(0);if (imsi != null && !TextUtils.isEmpty(imsi)) {return this;}return super.detect();}@Overridepublic String getSimPhoneNumber(int paramInt) {Object[] arrayOfObject2 = new Object[1];try {Object localObject = getMSimTelephonyManager(paramInt);arrayOfObject2[0] = Integer.valueOf(paramInt);String result = (String) ReflecterHelper.invokeMethod(localObject,"getLine1NumberGemini", arrayOfObject2);arrayOfObject2 = null;return result;} catch (Throwable localThrowable) {localThrowable.printStackTrace();}return "";}@Overridepublic int getDataState(int paramInt) {Object[] arrayOfObject2 = new Object[1];try {Object localObject = getMSimTelephonyManager(paramInt);arrayOfObject2[0] = Integer.valueOf(paramInt);int result = ((Integer) ReflecterHelper.invokeMethod(localObject,"getDataStateGemini", arrayOfObject2)).intValue();arrayOfObject2 = null;return result;} catch (Throwable localThrowable) {localThrowable.printStackTrace();}arrayOfObject2 = null;return -1;}@Overridepublic String getIMSI(int paramInt) {return getIMSI(paramInt, null);}@Overridepublic String getIMSI(int paramInt, Context paramContext) {Object localObject = getMSimTelephonyManager(paramInt);Object[] arrayOfObject2 = new Object[1];try {arrayOfObject2[0] = Integer.valueOf(paramInt);String result = (String) ReflecterHelper.invokeMethod(localObject,"getSubscriberIdGemini", arrayOfObject2);arrayOfObject2 = null;return result;} catch (Throwable localThrowable) {localThrowable.printStackTrace();}arrayOfObject2 = null;return null;}@Overridepublic int getPhoneState(int paramInt) {Object localObject = getMSimTelephonyManager(paramInt);Object[] arrayOfObject2 = new Object[1];try {arrayOfObject2[0] = Integer.valueOf(paramInt);int result = ((Integer) ReflecterHelper.invokeMethod(localObject,"getCallStateGemini", arrayOfObject2)).intValue();arrayOfObject2 = null;return result;} catch (Throwable localThrowable) {localThrowable.printStackTrace();}arrayOfObject2 = null;return 0;}@Overridepublic boolean isServiceAvaliable(int paramInt) {Object localObject = getITelephonyMSim(paramInt);if (localObject == null)return false;Object[] arrayOfObject2 = new Object[1];try {arrayOfObject2[0] = Integer.valueOf(paramInt);boolean result = ((Boolean) ReflecterHelper.invokeMethod(localObject, "isRadioOnGemini", arrayOfObject2)).booleanValue();arrayOfObject2 = null;return result;} catch (Throwable localThrowable) {localThrowable.printStackTrace();}arrayOfObject2 = null;return false;}@Overridepublic boolean isSimStateIsReady(int paramInt) {Object localObject = getMSimTelephonyManager(paramInt);if (localObject != null) {Object[] arrayOfObject2 = new Object[1];try {arrayOfObject2[0] = Integer.valueOf(paramInt);int result = ((Integer) ReflecterHelper.invokeMethod(localObject, "getSimStateGemini", arrayOfObject2)).intValue();arrayOfObject2 = null;return result == 5;} catch (Throwable localThrowable) {localThrowable.printStackTrace();}arrayOfObject2 = null;}return false;}@Overridepublic int getSimOperator(int paramInt) { // 注意Object localObject = getMSimTelephonyManager(paramInt);Object[] arrayOfObject2 = new Object[1];try {arrayOfObject2[0] = Integer.valueOf(paramInt);String result = ((String) ReflecterHelper.invokeMethod(localObject,"getSimOperatorGemini", arrayOfObject2));arrayOfObject2 = null;return Integer.valueOf(result);} catch (Throwable localThrowable) {localThrowable.printStackTrace();}arrayOfObject2 = null;return 0;}@Overrideprotected Object getITelephonyMSim(int paramInt) {if (mTelephonyMSim == null)mTelephonyMSim = ITelephony.Stub.asInterface(ServiceManager.getService("phone"));return mTelephonyMSim;}@Overrideprotected Object getMSimTelephonyManager(int paramInt) {if (mMSimTelephonyManager != null)return mMSimTelephonyManager;Object[] arrayOfObject3 = new Object[1];try {mMSimTelephonyManager = SimManager.getInstance().getTelephonyManagerByPhone();try {Object localObject = mMSimTelephonyManager;arrayOfObject3[0] = Integer.valueOf(0);ReflecterHelper.invokeMethod(localObject,"getSubscriberIdGemini", arrayOfObject3);arrayOfObject3 = null;return mMSimTelephonyManager;} catch (Throwable localThrowable2) {localThrowable2.printStackTrace();}} catch (Throwable localThrowable1) {localThrowable1.printStackTrace();}arrayOfObject3 = null;return null;}}
再列舉一款單卡的方案:
public class SingleSim extends AbsSim implements IDualDetector {// 單卡方案private final String TAG = getClass().getSimpleName();private HashMap<String, Byte> mCallLogExtraFields = new SingleSim$1(this);@Overridepublic boolean hasSimPhone() {return false;}@Overridepublic AbsSim detect() {// 根據某些欄位判是否為雙卡(有可能誤判)if (mIsDualSimPhone)return null;Cursor localSafeCursor = null;String[] arrayOfString;try {localSafeCursor = SimManager.getInstance().getContext().getContentResolver().query(CallLog.Calls.CONTENT_URI, null, null, null,"_id limit 0,1");if (localSafeCursor != null && localSafeCursor.moveToFirst()) {arrayOfString = localSafeCursor.getColumnNames();for (int i = 0; i < arrayOfString.length; i++) {String str = arrayOfString[i];if (mCallLogExtraFields.containsKey(str.toLowerCase())) {mIsDualSimPhone = true;mCallLogExtraField = str;}}}} catch (Exception e) {e.printStackTrace();}return this;}@Overridepublic boolean isDualSimPhone() {return mIsDualSimPhone;}@Overridepublic int getSimSlotNum() {return 1;}@Overridepublic String getSimPhoneNumber(int paramInt) {return ((TelephonyManager) getMSimTelephonyManager(0)).getLine1Number();}@Overridepublic int getDataState(int paramInt) {return ((TelephonyManager) getMSimTelephonyManager(0)).getDataState();}@Overridepublic String getIMSI(int paramInt) {return ((TelephonyManager) getMSimTelephonyManager(0)).getDeviceId();}@Overridepublic String getIMSI(int paramInt, Context paramContext) {return ((TelephonyManager) getMSimTelephonyManager(0)).getSubscriberId();}@Overridepublic int getPhoneState(int paramInt) {return ((TelephonyManager) getMSimTelephonyManager(0)).getCallState();}@Overridepublic boolean isServiceAvaliable(int paramInt) {ITelephony localITelephony = (ITelephony) getITelephonyMSim(0);if (localITelephony == null)return false;try {boolean bool = localITelephony.isRadioOn();return bool;} catch (Throwable localThrowable) {localThrowable.printStackTrace();}return false;}@Overridepublic boolean isSimStateIsReady(int paramInt) {return ((TelephonyManager) getMSimTelephonyManager(0)).getSimState() == 5;}@Overridepublic int getSimOperator(int paramInt) {TelephonyManager localTelephonyManager = (TelephonyManager) getMSimTelephonyManager(paramInt);return Integer.parseInt(localTelephonyManager.getSimOperator());}@Overrideprotected Object getITelephonyMSim(int paramInt) {return SimManager.getInstance().getITelephonyByPhone();}@Overrideprotected Object getMSimTelephonyManager(int paramInt) {return SimManager.getInstance().getTelephonyManagerByPhone();}}
總結
利用java 反射機制操作Android隱藏的類,很好的解決了雙卡雙待的問題。
Java反射是Java被視為動態(或准動態)語言的一個關鍵性質。這個機制允許程式在運行時透過Reflection APIs取得任何一個已知名稱的class的內部資訊,包括其modifiers(諸如public, static 等)、superclass(例如Object)、實現之interfaces(例如Cloneable),也包括fields和methods的所有資訊,並可於運行時改變fields內容或喚起methods。
項目下載