简介
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);// 获取操作接口 @Override public 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"; } } } @Override public 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; } @Override public AbsSim detect() { String imsi = getIMSI(0); if (imsi != null && !TextUtils.isEmpty(imsi)) { return this; } return super.detect(); } @Override public 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 ""; } @Override public 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; } @Override public String getIMSI(int paramInt) { return getIMSI(paramInt, null); } @Override public 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; } @Override public 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; } @Override public 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; } @Override public 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; } @Override public 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; } @Override protected Object getITelephonyMSim(int paramInt) { if (mTelephonyMSim == null) mTelephonyMSim = ITelephony.Stub.asInterface(ServiceManager .getService("phone")); return mTelephonyMSim; } @Override protected 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); @Override public boolean hasSimPhone() { return false; } @Override public 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; } @Override public boolean isDualSimPhone() { return mIsDualSimPhone; } @Override public int getSimSlotNum() { return 1; } @Override public String getSimPhoneNumber(int paramInt) { return ((TelephonyManager) getMSimTelephonyManager(0)).getLine1Number(); } @Override public int getDataState(int paramInt) { return ((TelephonyManager) getMSimTelephonyManager(0)).getDataState(); } @Override public String getIMSI(int paramInt) { return ((TelephonyManager) getMSimTelephonyManager(0)).getDeviceId(); } @Override public String getIMSI(int paramInt, Context paramContext) { return ((TelephonyManager) getMSimTelephonyManager(0)) .getSubscriberId(); } @Override public int getPhoneState(int paramInt) { return ((TelephonyManager) getMSimTelephonyManager(0)).getCallState(); } @Override public 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; } @Override public boolean isSimStateIsReady(int paramInt) { return ((TelephonyManager) getMSimTelephonyManager(0)).getSimState() == 5; } @Override public int getSimOperator(int paramInt) { TelephonyManager localTelephonyManager = (TelephonyManager) getMSimTelephonyManager(paramInt); return Integer.parseInt(localTelephonyManager.getSimOperator()); } @Override protected Object getITelephonyMSim(int paramInt) { return SimManager.getInstance().getITelephonyByPhone(); } @Override protected 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。
项目下载
- 2楼sara_loveyou46分钟前
- 楼主的精神可嘉,不知道是否能够覆盖市面上80%以上的设备
- Re: banketree30分钟前
- 回复sara_loveyoun写该项目就是冲着广大用户而来。
- 1楼flxue昨天 23:49
- 赞一个,java reflection是没法直接获取API时候比较长想到的方式,不过会不会遇到permission check虽然知道的接口但实际没法跑?
- Re: banketree59分钟前
- 回复flxuen测试截至此刻,未检测到任何问题。