黑马程序员技术交流社区

标题: Android 双卡双待识别 [打印本页]

作者: 陈君    时间: 2014-9-17 18:21
标题: Android 双卡双待识别
简介

Android双卡双待已经越来越普及了,解决双卡双待管理是广大手机开发人员必须得面对的问题,为实现Android平台的双卡双待操作,笔者研究了Android 应用层操作双卡双待的机制。



机制

获取基于ITelephony接口实现phone应用中的“phone服务”,通过TelephonyManager接口获取不同的卡(GSMPhone /CDMAPhone)进行不同的操作(拨号、接通、挂断、保持通话等)。

Android平台是一个多样型的平台,不同的手机获取ITelephony接口不同,用一种方法实现双卡双待管理是不可取的。那怎么办呢?只有针对不同的手机分析出一套管理的方案,该方案实现难度大,因为需要各个厂家的SDK的资料。为了实现该功能,笔者做了大量工作,整合各个厂家的SDK的资料。



实现

为了更好的管理双卡双待的问题,新建一个双卡双待模块静态库,其它项目引用便是,项目如图: 效果如图: 小米手机 测试效果 华为手机测试效果 AbsSim是抽象类,负责实现手机操作的类。不同的厂家继承该类实现各自的接口。AbsSim信息如下:
  1. public abstract class AbsSim implements IDualDetector { //抽象基类
  2. protected final String TAG = getClass().getSimpleName();
  3. protected ArrayList<SimSlot> mSimSlots = new ArrayList<SimSlot>();
  4. protected boolean mIsDualSimPhone = false;
  5. protected String mCallLogExtraField = "";

  6. public abstract String getSimPhoneNumber(int paramInt); // 返回手机号码

  7. public abstract int getDataState(int paramInt);// 返回数据状态

  8. public abstract String getIMSI(int paramInt);// 返回手机标识

  9. public abstract String getIMSI(int paramInt, Context paramContext);// 返回手机标识

  10. public abstract int getPhoneState(int paramInt);// 返回手机状态

  11. public abstract boolean isServiceAvaliable(int paramInt);// 服务是否可用

  12. public abstract boolean isSimStateIsReady(int paramInt);// 卡是否在使用

  13. public abstract int getSimOperator(int paramInt);// 服务商(电信、移动、联通)

  14. protected abstract Object getITelephonyMSim(int paramInt);// 获取操作接口

  15. protected abstract Object getMSimTelephonyManager(int paramInt);// 获取操作接口

  16. @Override
  17. public AbsSim detect() { // 根据手机信息匹配
  18. if ((getITelephonyMSim(0) != null) && (getITelephonyMSim(1) != null)
  19. // && (getmMSimSmsManager(0) != null)
  20. // && (getmMSimSmsManager(1) != null)
  21. // && (detectSms(paramContext, paramBoolean))
  22. // && (detectCallLog(paramContext, paramBoolean))
  23. )
  24. return this;
  25. return null;
  26. }

  27. public boolean directCall(String paramString, int paramInt) { // 拨打电话(根据不同卡拨打电话)
  28. Intent localIntent = new Intent("android.intent.action.CALL",
  29. Uri.fromParts("tel", paramString, null));
  30. localIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  31. try {
  32. getContext().startActivity(localIntent);
  33. return true;
  34. } catch (Throwable localThrowable) {
  35. localThrowable.printStackTrace();
  36. }
  37. return false;
  38. }

  39. protected boolean detectCallLog() { // 通过通话记录信息匹配
  40. return false;
  41. }

  42. protected boolean detectSms() {// 通过短信记录信息匹配
  43. return false;
  44. }

  45. protected Context getContext() { // 返回句柄
  46. return SimManager.getInstance().getContext();
  47. }

  48. protected int getSimSlotNum() { // 返回插槽个数(默认2)
  49. return 2;
  50. }

  51. public void init() { // 初始化
  52. for (int i = 0; i < getSimSlotNum(); i++) {
  53. try {
  54. String imsi = getIMSI(i);
  55. boolean isUsing = isSimStateIsReady(i);
  56. if (imsi != null || isUsing) {
  57. if (imsi == null || hasSimSlotByIMSI(imsi))
  58. continue;

  59. SimSlot simSlot = new SimSlot();
  60. mSimSlots.add(simSlot);
  61. simSlot.setUsing(isUsing);
  62. simSlot.setIMSI(imsi);
  63. simSlot.setSimOperator(getSimOperator(i));
  64. }
  65. } catch (Exception e) {
  66. e.printStackTrace();
  67. }
  68. }
  69. }

  70. public boolean hasSimPhone() {// 是否有sd卡在使用
  71. if (mSimSlots.isEmpty())
  72. return false;

  73. for (SimSlot simslot : mSimSlots) {
  74. if (simslot.isUsing())
  75. return true;
  76. }

  77. return false;
  78. }

  79. public boolean isDualSimPhone() {// 是否为双卡
  80. if (getSimSlots().isEmpty() || getSimSlots().size() < 2)
  81. return false;

  82. for (SimSlot simSlot : getSimSlots()) {
  83. if (!simSlot.isUsing())
  84. return false;
  85. }

  86. return true;
  87. }

  88. public ArrayList<SimSlot> getSimSlots() { // 返回已确认的卡
  89. return mSimSlots;
  90. }

  91. protected boolean delSimSlotByIMSI(String imsi) { // 删除相同的卡的信息
  92. for (SimSlot simSlot : getSimSlots()) {
  93. if (simSlot.getIMSI() != null && simSlot.getIMSI().equals(imsi)) {
  94. getSimSlots().remove(simSlot);
  95. }
  96. }

  97. return false;
  98. }

  99. protected boolean hasSimSlotByIMSI(String imsi) {
  100. for (SimSlot simSlot : getSimSlots()) {
  101. if (simSlot.getIMSI() != null && simSlot.getIMSI().equals(imsi)) {
  102. return true;
  103. }
  104. }

  105. return false;
  106. }
  107. }
复制代码

现在列举一款实现MTK方案:
  1. public class MTKDualSim extends AbsSim {// 采用MTK方案的类(根据厂家SDK实现不同的接口)
  2. private Object mMSimTelephonyManager = null;
  3. private Object mTelephonyMSim = null;

  4. public MTKDualSim() {
  5. mCallLogExtraField = "simid";

  6. String str1 = SimManager.getModel();
  7. String str2 = SimManager.getManufaturer();
  8. if ((str1 != null) && (str2 != null)) {
  9. String str3 = str1.toLowerCase();
  10. String str4 = str2.toLowerCase();
  11. if ((str4.indexOf("huawei") > -1) && (str3.indexOf("h30-t00") > -1))
  12. mCallLogExtraField = "subscription";
  13. if ((str4.indexOf("hisense") > -1)
  14. && (str3.indexOf("hs-u970") > -1)) {
  15. mCallLogExtraField = "subtype";
  16. }
  17. }
  18. }

  19. @Override
  20. public boolean directCall(String paramString, int paramInt) {
  21. if (SimManager.isSDKVersionMore4_1()) {
  22. Intent localIntent1 = new Intent("android.intent.action.CALL",
  23. Uri.fromParts("tel", paramString, null));
  24. localIntent1.putExtra("simId", paramInt);
  25. localIntent1.putExtra("com.android.phone.extra.slot", paramInt);
  26. localIntent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  27. try {
  28. getContext().startActivity(localIntent1);
  29. return true;
  30. } catch (Throwable localThrowable1) {
  31. localThrowable1.printStackTrace();
  32. }
  33. } else if (SimManager.isSDKVersionMore4_0()) {
  34. Intent localIntent2 = new Intent(
  35. "com.android.phone.OutgoingCallReceiver");
  36. localIntent2.putExtra("com.android.phone.extra.slot", paramInt);
  37. localIntent2.putExtra("simId", paramInt);
  38. localIntent2.putExtra("com.android.phone.force.slot", true);
  39. localIntent2.setClassName("com.android.phone",
  40. "com.android.phone.OutgoingCallReceiver");
  41. localIntent2.setData(Uri.fromParts("tel", paramString, null));
  42. try {
  43. getContext().sendBroadcast(localIntent2);
  44. return true;
  45. } catch (Throwable localThrowable2) {
  46. localThrowable2.printStackTrace();
  47. }
  48. }

  49. try {
  50. Intent localIntent3 = new Intent();
  51. localIntent3.setAction("out_going_call_to_phone_app");
  52. localIntent3.putExtra("number", paramString);
  53. localIntent3.putExtra("simId", paramInt);
  54. localIntent3.putExtra("com.android.phone.extra.slot", paramInt);
  55. localIntent3.putExtra("launch_from_dialer", 1);
  56. localIntent3.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  57. getContext().sendBroadcast(localIntent3);
  58. return true;
  59. } catch (Throwable localThrowable3) {
  60. localThrowable3.printStackTrace();
  61. }
  62. return false;
  63. }

  64. @Override
  65. public AbsSim detect() {
  66. String imsi = getIMSI(0);
  67. if (imsi != null && !TextUtils.isEmpty(imsi)) {
  68. return this;
  69. }

  70. return super.detect();
  71. }

  72. @Override
  73. public String getSimPhoneNumber(int paramInt) {
  74. Object[] arrayOfObject2 = new Object[1];
  75. try {
  76. Object localObject = getMSimTelephonyManager(paramInt);
  77. arrayOfObject2[0] = Integer.valueOf(paramInt);
  78. String result = (String) ReflecterHelper.invokeMethod(localObject,
  79. "getLine1NumberGemini", arrayOfObject2);
  80. arrayOfObject2 = null;
  81. return result;
  82. } catch (Throwable localThrowable) {
  83. localThrowable.printStackTrace();
  84. }
  85. return "";
  86. }

  87. @Override
  88. public int getDataState(int paramInt) {
  89. Object[] arrayOfObject2 = new Object[1];
  90. try {
  91. Object localObject = getMSimTelephonyManager(paramInt);
  92. arrayOfObject2[0] = Integer.valueOf(paramInt);
  93. int result = ((Integer) ReflecterHelper.invokeMethod(localObject,
  94. "getDataStateGemini", arrayOfObject2)).intValue();
  95. arrayOfObject2 = null;
  96. return result;
  97. } catch (Throwable localThrowable) {
  98. localThrowable.printStackTrace();
  99. }
  100. arrayOfObject2 = null;
  101. return -1;
  102. }

  103. @Override
  104. public String getIMSI(int paramInt) {
  105. return getIMSI(paramInt, null);
  106. }

  107. @Override
  108. public String getIMSI(int paramInt, Context paramContext) {
  109. Object localObject = getMSimTelephonyManager(paramInt);
  110. Object[] arrayOfObject2 = new Object[1];
  111. try {
  112. arrayOfObject2[0] = Integer.valueOf(paramInt);
  113. String result = (String) ReflecterHelper.invokeMethod(localObject,
  114. "getSubscriberIdGemini", arrayOfObject2);
  115. arrayOfObject2 = null;
  116. return result;
  117. } catch (Throwable localThrowable) {
  118. localThrowable.printStackTrace();
  119. }
  120. arrayOfObject2 = null;
  121. return null;
  122. }

  123. @Override
  124. public int getPhoneState(int paramInt) {
  125. Object localObject = getMSimTelephonyManager(paramInt);
  126. Object[] arrayOfObject2 = new Object[1];
  127. try {
  128. arrayOfObject2[0] = Integer.valueOf(paramInt);
  129. int result = ((Integer) ReflecterHelper.invokeMethod(localObject,
  130. "getCallStateGemini", arrayOfObject2)).intValue();
  131. arrayOfObject2 = null;
  132. return result;
  133. } catch (Throwable localThrowable) {
  134. localThrowable.printStackTrace();
  135. }
  136. arrayOfObject2 = null;
  137. return 0;
  138. }

  139. @Override
  140. public boolean isServiceAvaliable(int paramInt) {
  141. Object localObject = getITelephonyMSim(paramInt);
  142. if (localObject == null)
  143. return false;
  144. Object[] arrayOfObject2 = new Object[1];
  145. try {
  146. arrayOfObject2[0] = Integer.valueOf(paramInt);
  147. boolean result = ((Boolean) ReflecterHelper.invokeMethod(
  148. localObject, "isRadioOnGemini", arrayOfObject2))
  149. .booleanValue();
  150. arrayOfObject2 = null;
  151. return result;
  152. } catch (Throwable localThrowable) {
  153. localThrowable.printStackTrace();
  154. }
  155. arrayOfObject2 = null;
  156. return false;
  157. }

  158. @Override
  159. public boolean isSimStateIsReady(int paramInt) {
  160. Object localObject = getMSimTelephonyManager(paramInt);
  161. if (localObject != null) {
  162. Object[] arrayOfObject2 = new Object[1];
  163. try {
  164. arrayOfObject2[0] = Integer.valueOf(paramInt);
  165. int result = ((Integer) ReflecterHelper.invokeMethod(
  166. localObject, "getSimStateGemini", arrayOfObject2))
  167. .intValue();
  168. arrayOfObject2 = null;
  169. return result == 5;
  170. } catch (Throwable localThrowable) {
  171. localThrowable.printStackTrace();
  172. }
  173. arrayOfObject2 = null;
  174. }

  175. return false;
  176. }

  177. @Override
  178. public int getSimOperator(int paramInt) { // 注意
  179. Object localObject = getMSimTelephonyManager(paramInt);
  180. Object[] arrayOfObject2 = new Object[1];
  181. try {
  182. arrayOfObject2[0] = Integer.valueOf(paramInt);
  183. String result = ((String) ReflecterHelper.invokeMethod(localObject,
  184. "getSimOperatorGemini", arrayOfObject2));
  185. arrayOfObject2 = null;
  186. return Integer.valueOf(result);
  187. } catch (Throwable localThrowable) {
  188. localThrowable.printStackTrace();
  189. }
  190. arrayOfObject2 = null;
  191. return 0;
  192. }

  193. @Override
  194. protected Object getITelephonyMSim(int paramInt) {
  195. if (mTelephonyMSim == null)
  196. mTelephonyMSim = ITelephony.Stub.asInterface(ServiceManager
  197. .getService("phone"));
  198. return mTelephonyMSim;
  199. }

  200. @Override
  201. protected Object getMSimTelephonyManager(int paramInt) {
  202. if (mMSimTelephonyManager != null)
  203. return mMSimTelephonyManager;
  204. Object[] arrayOfObject3 = new Object[1];
  205. try {
  206. mMSimTelephonyManager = SimManager.getInstance()
  207. .getTelephonyManagerByPhone();
  208. try {
  209. Object localObject = mMSimTelephonyManager;
  210. arrayOfObject3[0] = Integer.valueOf(0);
  211. ReflecterHelper.invokeMethod(localObject,
  212. "getSubscriberIdGemini", arrayOfObject3);
  213. arrayOfObject3 = null;
  214. return mMSimTelephonyManager;
  215. } catch (Throwable localThrowable2) {
  216. localThrowable2.printStackTrace();
  217. }
  218. } catch (Throwable localThrowable1) {
  219. localThrowable1.printStackTrace();
  220. }
  221. arrayOfObject3 = null;
  222. return null;
  223. }
  224. }
复制代码

再列举一款单卡的方案:
  1. public class SingleSim extends AbsSim implements IDualDetector {// 单卡方案
  2. private final String TAG = getClass().getSimpleName();
  3. private HashMap<String, Byte> mCallLogExtraFields = new SingleSim$1(this);

  4. @Override
  5. public boolean hasSimPhone() {
  6. return false;
  7. }

  8. @Override
  9. public AbsSim detect() {// 根据某些字段判是否为双卡(有可能误判)
  10. if (mIsDualSimPhone)
  11. return null;

  12. Cursor localSafeCursor = null;
  13. String[] arrayOfString;
  14. try {
  15. localSafeCursor = SimManager
  16. .getInstance()
  17. .getContext()
  18. .getContentResolver()
  19. .query(CallLog.Calls.CONTENT_URI, null, null, null,
  20. "_id limit 0,1");

  21. if (localSafeCursor != null && localSafeCursor.moveToFirst()) {
  22. arrayOfString = localSafeCursor.getColumnNames();

  23. for (int i = 0; i < arrayOfString.length; i++) {
  24. String str = arrayOfString[i];
  25. if (mCallLogExtraFields.containsKey(str.toLowerCase())) {
  26. mIsDualSimPhone = true;
  27. mCallLogExtraField = str;
  28. }
  29. }

  30. }
  31. } catch (Exception e) {
  32. e.printStackTrace();
  33. }

  34. return this;
  35. }

  36. @Override
  37. public boolean isDualSimPhone() {
  38. return mIsDualSimPhone;
  39. }

  40. @Override
  41. public int getSimSlotNum() {
  42. return 1;
  43. }

  44. @Override
  45. public String getSimPhoneNumber(int paramInt) {
  46. return ((TelephonyManager) getMSimTelephonyManager(0)).getLine1Number();
  47. }

  48. @Override
  49. public int getDataState(int paramInt) {
  50. return ((TelephonyManager) getMSimTelephonyManager(0)).getDataState();
  51. }

  52. @Override
  53. public String getIMSI(int paramInt) {
  54. return ((TelephonyManager) getMSimTelephonyManager(0)).getDeviceId();
  55. }

  56. @Override
  57. public String getIMSI(int paramInt, Context paramContext) {
  58. return ((TelephonyManager) getMSimTelephonyManager(0))
  59. .getSubscriberId();
  60. }

  61. @Override
  62. public int getPhoneState(int paramInt) {
  63. return ((TelephonyManager) getMSimTelephonyManager(0)).getCallState();
  64. }

  65. @Override
  66. public boolean isServiceAvaliable(int paramInt) {
  67. ITelephony localITelephony = (ITelephony) getITelephonyMSim(0);
  68. if (localITelephony == null)
  69. return false;
  70. try {
  71. boolean bool = localITelephony.isRadioOn();
  72. return bool;
  73. } catch (Throwable localThrowable) {
  74. localThrowable.printStackTrace();
  75. }
  76. return false;
  77. }

  78. @Override
  79. public boolean isSimStateIsReady(int paramInt) {
  80. return ((TelephonyManager) getMSimTelephonyManager(0)).getSimState() == 5;
  81. }

  82. @Override
  83. public int getSimOperator(int paramInt) {
  84. TelephonyManager localTelephonyManager = (TelephonyManager) getMSimTelephonyManager(paramInt);
  85. return Integer.parseInt(localTelephonyManager.getSimOperator());
  86. }

  87. @Override
  88. protected Object getITelephonyMSim(int paramInt) {
  89. return SimManager.getInstance().getITelephonyByPhone();
  90. }

  91. @Override
  92. protected Object getMSimTelephonyManager(int paramInt) {
  93. return SimManager.getInstance().getTelephonyManagerByPhone();
  94. }
  95. }
复制代码

总结

利用java 反射机制操作Android隐藏的类,很好的解决了双卡双待的问题。
Java反射是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。


作者: 夜半风    时间: 2014-9-17 19:05
虽然看不懂 但是感觉高大上
作者: noiary    时间: 2014-9-17 20:22
学了这么久,终于看到终极目标的一角!  up!
作者: lishuliang28    时间: 2014-9-17 21:39
挺厉害的样子
作者: 备战    时间: 2014-9-18 09:12

顶一个。。
作者: huanhuan    时间: 2014-9-18 11:36
看不懂看不懂啊啊啊啊啊啊
作者: bayshier    时间: 2014-9-18 14:05
学习了, 楼主好人
作者: Kingland    时间: 2016-8-18 08:19
拜读了,加了楼主QQ,想进一步联系。
作者: baby14    时间: 2019-6-11 06:56
多谢分享




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2