android中双卡双待的那些代码
来源:互联网 发布:营销活动效果数据分析 编辑:程序博客网 时间:2024/05/11 01:59
这阵子忙着整理项目了,所以就没怎么出新的文章了,不过下面写的这篇文章对大家很有帮助。关于双卡双待的信息获取,包含了imei、phonenumber、operatorName(sim卡生产商,国内就主要指三大运营商了)、NetworkType(这里就主要是4G、3G等了)。
前言:
睡着国内的双卡手机出现,导致获取双卡的信息也是成了一个头痛的事了。google给开发者暴露的api还是停留在单卡上,所以在这里我就整理出相关的代码,让更多的猿友少走弯路。
首先从phonenumber的获取着手吧,顺便带着大家一起去看下相关的源码,以前获取phonenumber我是这么获取的:
((TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE)) .getLine1Number();
这里就调用了TelephonyManager的getLine1Number方法,这里顺道去源码看看getLine1Number是怎么获取的:
/** * Returns the phone number string for line 1, for example, the MSISDN * for a GSM phone. Return null if it is unavailable. * <p> * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * OR * {@link android.Manifest.permission#READ_SMS} * <p> * The default SMS app can also use this. */ public String getLine1Number() { return getLine1Number(getSubId()); }
注:我这里源码都是android-25下面的,刚看了下android-23下面的源码是这么调用的:
/** * Returns the phone number string for line 1, for example, the MSISDN * for a GSM phone. Return null if it is unavailable. * <p> * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * OR * {@link android.Manifest.permission#READ_SMS} * <p> * The default SMS app can also use this. */ public String getLine1Number() { return getLine1NumberForSubscriber(getDefaultSubscription()); }
还是有些区别的,起码方法的调用是不一样的,所以建议你在看该篇文章的时候还是把compileSdk升到25: compileSdkVersion 25
可以看到25的api是继续调了:getLine1Number(getSubId())
该方法,那就继续往下走吧:
/** * Returns the phone number string for line 1, for example, the MSISDN * for a GSM phone for a particular subscription. Return null if it is unavailable. * <p> * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * OR * {@link android.Manifest.permission#READ_SMS} * <p> * The default SMS app can also use this. * * @param subId whose phone number for line 1 is returned * @hide */ public String getLine1Number(int subId) { String number = null; try { ITelephony telephony = getITelephony(); if (telephony != null) number = telephony.getLine1NumberForDisplay(subId, mContext.getOpPackageName()); } catch (RemoteException ex) { } catch (NullPointerException ex) { } if (number != null) { return number; } try { IPhoneSubInfo info = getSubscriberInfo(); if (info == null) return null; return info.getLine1NumberForSubscriber(subId, mContext.getOpPackageName()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { // This could happen before phone restarts due to crashing return null; } }
看到这的时候真的是心灰意冷啊,为什么这么说,该方法竟然是hide类型的方法,对于这种方法咋们就用到反射了,后面会详细介绍的,看看它的参数是如何解释的: @param subId whose phone number for line 1 is returned
反正我是英语不好的哈,接着我就去查了查相关的说法,这里去看看这篇文章是如何解释的(subid指的是什么),简单来说subbed
指的就是sim卡的索引了,当有一个sim卡的时候subid=1,有两个的时候subid=2。依次类推就可以知道有几个卡subid就是多少了。不过这里的subid还是可以通过反射来获取subid,后面也会讲到如何获取我们的subbed:
private static final String SIM_LINE_NUMBER = "getLine1Number";private static final String SIM_STATE = "getSimState";public static String getSimPhonenumber(Context context, int slotIdx) { if (PermissionUtil.hasSelfPermission(context, Manifest.permission.READ_PHONE_STATE) || PermissionUtil.hasSelfPermission(context, "android.permission.READ_PRIVILEGED_PHONE_STATE")) { Log.d(TAG, "READ_PHONE_STATE permission has BEEN granted to getSimPhonenumber()."); if (getSimStateBySlotIdx(context, slotIdx)) { return (String) getSimByMethod(context, SIM_LINE_NUMBER, getSubidBySlotId(context, slotIdx)); } return null; } else { Log.d(TAG, "READ_PHONE_STATE permission has NOT been granted to getSimPhonenumber()."); return null; }}/** *获取相应卡的状态 * @param slotIdx:0(sim1),1(sim2) * @return true:使用中;false:未使用中 */public static boolean getSimStateBySlotIdx(Context context, int slotIdx) { boolean isReady = false; Object getSimState = getSimByMethod(context, SIM_STATE, slotIdx); if (getSimState != null) { int simState = Integer.parseInt(getSimState.toString()); if ((simState != TelephonyManager.SIM_STATE_ABSENT) && (simState != TelephonyManager.SIM_STATE_UNKNOWN)) { isReady = true; } } return isReady;}/** * 通过slotid获取相应卡的subid * @param context * @param slotId * @return */public static int getSubidBySlotId(Context context, int slotId) { SubscriptionManager subscriptionManager = (SubscriptionManager) context.getSystemService( Context.TELEPHONY_SUBSCRIPTION_SERVICE); try { Class<?> telephonyClass = Class.forName(subscriptionManager.getClass().getName()); Class<?>[] parameter = new Class[1]; parameter[0] = int.class; Method getSimState = telephonyClass.getMethod("getSubId", parameter); Object[] obParameter = new Object[1]; obParameter[0] = slotId; Object ob_phone = getSimState.invoke(subscriptionManager, obParameter); if (ob_phone != null) { Log.d(TAG, "slotId:" + slotId + ";" + ((int[]) ob_phone)[0]); return ((int[]) ob_phone)[0]; } } catch (Exception e) { e.printStackTrace(); } return -1;}/***通过反射调用相应的方法**/public static Object getSimByMethod(Context context, String method, int param) { TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); try { Class<?> telephonyClass = Class.forName(telephony.getClass().getName()); Class<?>[] parameter = new Class[1]; parameter[0] = int.class; Method getSimState = telephonyClass.getMethod(method, parameter); Object[] obParameter = new Object[1]; obParameter[0] = param; Object ob_phone = getSimState.invoke(telephony, obParameter); if (ob_phone != null) { return ob_phone; } } catch (Exception e) { e.printStackTrace(); } return null;}
可以看到getSimPhonenumber
方法需要slotIdx
参数,这里还是去这篇文章看看slotldx
是咋回事(slotldx到底是啥玩意),通过了解后,slotldx指的是那个卡槽了,如果当前要获取卡1,slotldx=0;如果是卡2,slotldx=1;到此知道为啥getSimPhonenumber
方法需要定义这么个参数了吧。至于说getSimState
方法,还是一样通过反射去获取每个卡的状态的,这里就不赘述源码了。上面可以看到获取subId的代码了吧,就是getSubidBySlotId
方法了,这里通过反射调用了SubscriptionManager
类中的getSubId
方法,需要的参数也是我们的slotId
。源码如下:
/** @hide */public static int[] getSubId(int slotId) { if (!isValidSlotId(slotId)) { logd("[getSubId]- fail"); return null; } int[] subId = null; try { ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); if (iSub != null) { subId = iSub.getSubId(slotId); } } catch (RemoteException ex) { // ignore it } return subId;}
还有imei
、operatorName
、NetworkType
都可以通过相应的方法获取了:
private static final String SIM_OPERATOR_NAME = "getNetworkOperatorName";private static final String SIM_NETWORK_TYPE = "getNetworkType";private static final String SIM_IMEI = "getImei";//获取相应卡的imeipublic static String getSimImei(Context context, int slotIdx) { if (PermissionUtil.hasSelfPermission(context, Manifest.permission.READ_PHONE_STATE) || PermissionUtil.hasSelfPermission(context, "android.permission.READ_PRIVILEGED_PHONE_STATE")) { Log.d(TAG, "READ_PHONE_STATE permission has BEEN granted to getSimImei()."); if (getSimStateBySlotIdx(context, slotIdx)) { //sim1 if (slotIdx == 0) { //这里的参数传的是slotldx return (String) getSimByMethod(context, SIM_IMEI, 0); } else if (slotIdx == 1) { return (String) getSimByMethod(context, SIM_IMEI, 1); } } return null; } else { Log.d(TAG, "READ_PHONE_STATE permission has NOT been granted to getSimImei()."); return null; }}public static String getSimNetworkName(Context context, int slotIdx) { if (getSimStateBySlotIdx(context, slotIdx)) { return getNetworkName((int) getSimByMethod(context, SIM_NETWORK_TYPE, getSubidBySlotId(context, slotIdx))); } return "UNKNOWN";}public static String getSimOperatorName(Context context, int slotIdx) { if (getSimStateBySlotIdx(context, slotIdx)) { return (String) getSimByMethod(context, SIM_OPERATOR_NAME, getSubidBySlotId(context, slotIdx)); } return null;}
到此相关的属性获取基本ok了,大家如果还需要获取什么属性,直接去TelephonyManager
查看相关的源码。还有一个就是插卡和拔卡的监听、网络变化的监听:
//网络变化的监听public class SimConnectReceive extends BroadcastReceiver { private static final String TAG = SimConnectReceive.class.getSimpleName(); public final static String ACTION_SIM_STATE_CHANGED = ConnectivityManager.CONNECTIVITY_ACTION; @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(ACTION_SIM_STATE_CHANGED)) { Log.d(TAG, "onReceive"); EventBus.getDefault().post(new SimConnectChange()); } }}//插卡和拔卡的监听public class SimStateReceive extends BroadcastReceiver { private static final String TAG = SimStateReceive.class.getSimpleName(); public final static String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED"; @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(ACTION_SIM_STATE_CHANGED)) { Log.d(TAG, "onReceive"); EventBus.getDefault().post(new SimStateChange()); } }}
还有就是不要忘了manifest中权限: <uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
对于6.0的动态权限处理也是添加该权限的判断。
最后贴上该功能的代码:
github传送门
thanks:DualSIMCard
有什么问题可以email我:a1002326270@163.com
- android中双卡双待的那些代码
- Android 的 那些 秘密代码
- Android的代码安全那些事
- 那些代码的事儿
- Android studio 导入 Eclipse 代码的那些坑
- Android代码混淆踩过的那些坑
- Android代码优化那些事儿
- Android代码混淆那些事
- Android 代码混淆那些事
- 代码规范的那些事儿
- 代码规范的那些事儿
- 代码规范的那些事儿
- 代码规范的那些事儿
- 代码规范的那些事儿
- 那些年的代码情节
- 代码规范的那些事儿
- 那些奇葩的代码注释
- 那些令人喷饭的代码注释
- 【集训】jzoj 2017.7.14 noip模拟赛A 总结
- C++编程规范19:总是初始化变量
- Jmeter JDBC显示事务处理(优化版本)
- **农商手机银行app更新代码后访问速度非常慢
- 送给自己的生日礼物:突破自己(辞职南下)
- android中双卡双待的那些代码
- 一个基于Spring Boot的API、RESTful API项目
- 写给那些傻傻的,想做服务器开发的应届生
- Spring容器事件
- MySQL存储过程详解
- 关于《编程之美》中买书问题动态规划Java简单实现
- 计算机视觉领军企业
- tomcat内存配置及配置参数详解
- OCM考试实验-Skillset1----Network configuration