Android 获取双卡手机IMEI,IMSI,ICCID
一、首先要添加权限
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
二、获取主卡的IMEI,IMSI,ICCID
/*** Author: liuqiang* Time: 2017-08-14 15:28* Description:* <p>* IMEI 与你的手机是绑定关系 用于区别移动终端设备* IMSI 与你的手机卡是绑定关系 用于区别移动用户的有效信息 IMSI是用户的标识。* ICCID ICCID是卡的标识,由20位数字组成* ICCID只是用来区别SIM卡,不作接入网络的鉴权认证。而IMSI在接入网络的时候,会到运营商的服务器中进行验证。* https://github.com/android/platform_frameworks_base/blob/master/telephony/java/android/telephony/TelephonyManager.java*/@RequiresApi(api = Build.VERSION_CODES.O)public void check(View view) {TelephonyManager telephonyManager = (TelephonyManager) this.getSystemService(TELEPHONY_SERVICE);// 取得相关系统服务String simOperatorName = telephonyManager.getSimOperatorName();String imei = telephonyManager.getDeviceId(); //取出 IMEIString imeiAPI26 = telephonyManager.getImei(); //取出 IMEI 需要 api26以上String tel = telephonyManager.getLine1Number(); //取出 MSISDN,很可能为空String imsi = telephonyManager.getSubscriberId(); //取出 IMSIString icc = telephonyManager.getSimSerialNumber(); //取出 ICCIDLog.d("Q_M", "运行商名字--" + simOperatorName);Log.d("Q_M", "IMEI--" + imei);Log.d("Q_M", "IMEI_API26--" + imeiAPI26);Log.d("Q_M", "IMSI--" + imsi);Log.d("Q_M", "ICCID--" + icc);}
三、如果手机有多张卡
TelephonyManager的官方源码
其实多卡情况下主要要获得的是两个地方:getSubscriberId
和getSimSerialNumber
,打开上面的的源码,搜索一下这两个方法,发现这两个方法都有一个带参数(subId)的重载方法,并且这两个方法都是@hide
的,hide倒是无所谓,这个可以通过反射调用,主要的问题要弄清楚他的这个参数subId是个什么东西。代码片段如下:
/*** Returns the unique subscriber ID, for example, the IMSI for a GSM phone.* Return null if it is unavailable.* <p>* Requires Permission:* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}*/public String getSubscriberId() {return getSubscriberId(getSubId());}/*** Returns the unique subscriber ID, for example, the IMSI for a GSM phone* for a subscription.* Return null if it is unavailable.* <p>* Requires Permission:* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}** @param subId whose subscriber id is returned* @hide*/public String getSubscriberId(int subId) {try {IPhoneSubInfo info = getSubscriberInfo();if (info == null)return null;return info.getSubscriberIdForSubscriber(subId, mContext.getOpPackageName());} catch (RemoteException ex) {return null;} catch (NullPointerException ex) {// This could happen before phone restarts due to crashingreturn null;}}
从上面的注释来来,这个subId 是subscription id
的简写,既然提到subscription id
,那不得不说的就是SubscriptionManager
,这个东西这里不解释了看这篇文章,官方文档。只要知道一点,这个类就是为了5.0之后,为了配合TelephonyProvider
操作和处理/data/data/com.android.providers.telephony/databases/telephony.db
这个数据库中的表的,就可以了。这个数据在root了的手机上可以直接通过SQLite-Editor-2.1.1.apk
这个工具apk查看。当然也可以把他导出到电脑上查看。
0 、不算业余知识的业余知识
既然所有的CURD都是针对这个数据库,那么来研究下这个数据库,
打开后发现这个数据库中有张叫siminfo
的表。如下图:
截图中并没有包含表中所有的列,但是这些是主要的,后面的那些和这篇文章无关。上图这个是在Android模拟器中导出来的。在看一张表的时候,不仅要了解他的字段的含义,而且要了解这种表中数据的插入规则。
很重要:每次插入一张新SIM卡(这张卡没有插入到过这个手机)的时候,都会在数据库中添加一条记录,插入的时候
_id
字段从1开始每次增1;每次移除一张SIM卡,sim_id
字段设置为-1
(但是这条记录不会被删除),表示这张卡曾经插入过,但是又被移除了,用专业术语来说就是这张卡现在不是Active
的,处于不可用状态。
这些字段都表示了什么意思,其中最重要的是_id
和sim_id
:
_id:从数据库的角度来说,做过sqlite开发的都知道,他是个从1开始自增的主键。但是他在这里还代表了程序中另一个东西subId
也就是subscription id
icc_id:不解释,上面说过了
sim_id:这个字段有两层含义,在大于-1,的情况下他表示的是卡槽序号,比如sim_id为0表示卡1
,取值为1的时候表示的是卡2
,以此类推,但是一般手机不会超过两个卡槽吧?!如果取值为-1,表示这张SIM卡曾经被插入过,但是现在被移除了。
display_name:顾名思义,显示名。这个一般可以改,但是默认的是读取的运营商的名字,比如:中国移动,中国联通,中国电信
carrier_name :恩,运营商名字
number:SIM卡对应的手机号,这个不一定能取到
mcc:Mobile Country Code,移动国家码
mnc:Mobile Network Code,移动网络码
现在的主要问题是如何获取subscription id
,对吧,how?
1 、读数据库取subId也即是表的_id 字段
///data/data/com.android.providers.telephony/databases/telephony.dbpublic void getSimInfo() {Uri uri = Uri.parse("content://telephony/siminfo");Cursor cursor = null;ContentResolver contentResolver = getApplicationContext().getContentResolver();cursor = contentResolver.query(uri,new String[]{"_id", "sim_id", "icc_id", "display_name"}, "0=0",new String[]{}, null);if (null != cursor) {while (cursor.moveToNext()) {String icc_id = cursor.getString(cursor.getColumnIndex("icc_id"));String display_name = cursor.getString(cursor.getColumnIndex("display_name"));int sim_id = cursor.getInt(cursor.getColumnIndex("sim_id"));int _id = cursor.getInt(cursor.getColumnIndex("_id"));Log.d("Q_M", "icc_id-->" + icc_id);Log.d("Q_M", "sim_id-->" + sim_id);Log.d("Q_M", "display_name-->" + display_name);Log.d("Q_M", "subId或者说是_id->" + _id);Log.d("Q_M", "---------------------------------");}}}
如上代码,我在小米6的测试机上进行测试,插入过3张卡,两张移动,一张联通的,运行结果如下(因为是真是的SIM卡,隐藏了icc_id):
08-14 16:46:11.208 11583-11583/me.febsky.rootcheck D/Q_M: icc_id-->898600*************7
08-14 16:46:11.208 11583-11583/me.febsky.rootcheck D/Q_M: sim_id-->0
08-14 16:46:11.208 11583-11583/me.febsky.rootcheck D/Q_M: display_name-->中国移动
08-14 16:46:11.208 11583-11583/me.febsky.rootcheck D/Q_M: subId或者说是_id->1
08-14 16:46:11.208 11583-11583/me.febsky.rootcheck D/Q_M: ---------------------------------
08-14 16:46:11.208 11583-11583/me.febsky.rootcheck D/Q_M: icc_id-->898601*************6
08-14 16:46:11.208 11583-11583/me.febsky.rootcheck D/Q_M: sim_id-->-1
08-14 16:46:11.208 11583-11583/me.febsky.rootcheck D/Q_M: display_name-->CARD 2
08-14 16:46:11.208 11583-11583/me.febsky.rootcheck D/Q_M: subId或者说是_id->2
08-14 16:46:11.208 11583-11583/me.febsky.rootcheck D/Q_M: ---------------------------------
08-14 16:46:11.208 11583-11583/me.febsky.rootcheck D/Q_M: icc_id-->898602*************9
08-14 16:46:11.208 11583-11583/me.febsky.rootcheck D/Q_M: sim_id-->1
08-14 16:46:11.208 11583-11583/me.febsky.rootcheck D/Q_M: display_name-->中国移动
08-14 16:46:11.208 11583-11583/me.febsky.rootcheck D/Q_M: subId或者说是_id->3
08-14 16:46:11.208 11583-11583/me.febsky.rootcheck D/Q_M: ---------------------------------
2、通过SubscriptionManager取subId
但是这个api有两个缺陷:必须5.0以上才能用;只能获取active的卡的信息。其实对于第二点缺陷,源码中是能获取所有的卡的信息的,也就是表中所有的数据,通过源码看到这个方法叫做getAllSubscriptionInfoList
不过这个方法也被hide了,能用是能用,就是这种@hide的方法,不一定在哪个版本就会被删除掉。需要做很多兼容性上的操作。
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)public void getSimInfoBySubscriptionManager() {List<SubscriptionInfo> list = SubscriptionManager.from(this).getActiveSubscriptionInfoList();for (SubscriptionInfo info : list) {Log.d("Q_M", "ICCID-->" + info.getIccId());Log.d("Q_M", "subId-->" + info.getSubscriptionId());Log.d("Q_M", "DisplayName-->" + info.getDisplayName());Log.d("Q_M", "CarrierName-->" + info.getCarrierName());Log.d("Q_M", "---------------------------------");}}
上面的SubscriptionInfo
源码,就几乎对应了siminfo这个表中的所有的字段。
其实代码分析到这里,双卡的其他基本信息其实都已经获取到了,无论通过读取数据库的方式,还是通过SubscriptionManager
的方式,唯一没有获取到的就是getSubscriberId()
来得到的IMSI。
3、通过反射获取调用IMSI
反射调用带有参数的getSubscriberId(subId)
是很简单的,但是问题就出在,这个方法是@hide,所以说这个方法可能在不同的Android版本中会出现不同的实现。subId在5.0传入的是long类型的参数,而5.1-7.1.1传入的是int类型的参数。再高的版本我就没看了
5.0.0上的方法签名:
/*** Returns the unique subscriber ID, for example, the IMSI for a GSM phone* for a subscription.* Return null if it is unavailable.* <p>* Requires Permission:* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}** @param subId whose subscriber id is returned*//** {@hide} */public String getSubscriberId(long subId) {try {return getSubscriberInfo().getSubscriberIdForSubscriber(subId);} catch (RemoteException ex) {return null;} catch (NullPointerException ex) {// This could happen before phone restarts due to crashingreturn null;}}
5.1.0-5.1.1的方法签名:
public String getSubscriberId(int subId) {try {return getSubscriberInfo().getSubscriberIdForSubscriber(subId);} catch (RemoteException ex) {return null;} catch (NullPointerException ex) {// This could happen before phone restarts due to crashingreturn null;}}
6.0.0-7.1.1的方法签名:
public String getSubscriberId(int subId) {try {IPhoneSubInfo info = getSubscriberInfo();if (info == null)return null;return info.getSubscriberIdForSubscriber(subId, mContext.getOpPackageName());} catch (RemoteException ex) {return null;} catch (NullPointerException ex) {// This could happen before phone restarts due to crashingreturn null;}
}
以上源码来自http://androidxref.com/
反射调用getSubscriberId
的代码如下,其中参数,subId的获取方式,在上面已经有了:
public String getSubscriberId(int subId) {TelephonyManager telephonyManager = (TelephonyManager) this.getSystemService(TELEPHONY_SERVICE);// 取得相关系统服务Class<?> telephonyManagerClass = null;String imsi = null;try {telephonyManagerClass = Class.forName("android.telephony.TelephonyManager");if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.LOLLIPOP) {Method method = telephonyManagerClass.getMethod("getSubscriberId", int.class);imsi = (String) method.invoke(telephonyManager, subId);} else if (android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.LOLLIPOP) {Method method = telephonyManagerClass.getMethod("getSubscriberId", long.class);imsi = (String) method.invoke(telephonyManager, (long) subId);}} catch (Exception e) {e.printStackTrace();}Log.d("Q_M", "IMSI--" + imsi);return imsi;}
最后: 这些操作都是在Android5.0以上版本的,并且第三方厂商没有修改这块的代码的情况下的操作。至于5.0以下的系统,怎么判断,好像要分平台单个处理
Android4.x上处理双卡
高通平台有单独的类android.telephony.MSimTelephonyManager
里面有方法,这个可以用反射的方式调用,经过验证在努比亚4.4.2的系统上是可以获取的:
- getSubscriberId(int simId)
- getSimSerialNumber(int simId)
测试代码如下:
/*** Author: liuqiang* Time: 2017-08-15 10:56* Description:* <p>* 高通的“android.telephony.MSimTelephonyManager”类*/private void getAPI19SimInfo() {Class<?> tm = null;try {tm = Class.forName("android.telephony.MSimTelephonyManager");Method getSubscriberIdMethod = tm.getMethod("getSubscriberId", int.class);Method getSimSerialNumberMethod = tm.getMethod("getSimSerialNumber", int.class);Object service = this.getSystemService("phone_msim");//0 代表卡1//1 代表卡2String s = (String) getSimSerialNumberMethod.invoke(service, 0);Log.d("Q_M", "-->" + s);} catch (ClassNotFoundException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}
至于MTK平台,据说有个aidl”com.mediatek.common.telephony.ITelephonyEx.aidl”类或者用Manager类com.mediatek.telephony.TelephonyManagerEx
,我这没有MTK平台的测试机,没法测。
【参考文章】
- http://blog.csdn.net/tencent_bugly/article/details/51911047
- http://www.cnblogs.com/flyme/p/4538002.html
- http://blog.csdn.net/peijiangping1989/article/details/20448007
Android 获取双卡手机IMEI,IMSI,ICCID相关推荐
- 获取双卡手机IMEI、IMSI
转载请注明出处: 现在 Android 手机越来越多的使用双卡双待,对于安装双卡的手机,有时我们想要获取两张卡的IMSI.IMEI等信息.我们知道 Android 中提供了相关 api,通过类 Tel ...
- android获取imei兼容_Android如何获取双卡手机IMEI的方法示例
前言: 项目中有个统计付费广告转化率的需求,需要获取用户手机的IMEI.但是网上最常见的方法有坑,也就是TelephonyManager.getDeviceId(),这方法有可能获取的是MEID或者空 ...
- 安卓iccid_Android 获取双卡手机IMEI,IMSI,ICCID
一.首先要添加权限 二.获取主卡的IMEI,IMSI,ICCID /** * Author: liuqiang * Time: 2017-08-14 15:28 * Description: * * ...
- android 获取双卡手机信息
android本身SDK不支持双卡,只能通过其他双卡的商家提供的API进行获取.听说android5.0的SDK开始支持双卡,等待中--
- 关于android各种双卡手机获取imei,imsi的处置(mtk,展讯,高通等)
2019独角兽企业重金招聘Python工程师标准>>> 关于android各种双卡手机获取imei,imsi的处理(mtk,展讯,高通等) 目前国内对于双卡智能手机的需求还是很大的, ...
- 获取双卡手机的两个卡的IMSI
/** * 获取双卡手机的两个卡的IMSI 需要 READ_PHONE_STATE 权限 * * @param context * 上下文 * @return 下标0为一卡的IMSI,下标1为二卡的I ...
- 序列号,IMEI,IMSI,ICCID的含义
序列号,IMEI,IMSI,ICCID的含义 什么是序列号? 序列号是一串标识你手机出生证明以及身材特征的信息,甚至还可用来识别是否为官方翻新机. 你可以简单的将这一串数字分割为:aabccdddee ...
- NB模组中序列号,IMEI,IMSI,ICCID的含义(一些知识科普)
概述 下面简述关于NB模组中使用,IMEI,IMSI,ICCID的含义. 什么是序列号? 序列号是一串标识你手机出生证明以及身材特征的信息,甚至还可用来识别是否为官方翻新机. 你可以简单的将这一串数字 ...
- C/C++获取安卓手机IMEI编号demo
C/C++获取安卓手机IMEI编号demo #include <stdlib.h> #include <exception> #include <fstream> ...
最新文章
- 94. 二叉树的中序遍历(迭代)
- webstorm javascript IDE调试
- leetcode算法题--煎饼排序
- 笔记-信息化与系统集成技术-人工智能的特点
- 编程中函数的返回值的定义问题的解决
- pandas常见的时间处理函数
- mysql中使用HAVING 筛选分组后的数据
- SQLServer事务的隔离级别
- 无法打开用户默认数据库,登录失败,用户‘sa’登录失败,错误:4064的解决方法...
- Weave Scope安装
- SpringBoot2.0 整合 RocketMQ ,实现请求异步处理
- 中国基因工程行业市场供需与战略研究报告
- Python+OpenCV:基于色彩空间转换的目标跟踪
- 机器学习工程师 - Udacity 强化学习 Part Eleven
- VSCode Debug API
- TabLayout的自定义
- python识别手写文字_深度学习---手写字体识别程序分析(python)
- 指数加权平均与动量梯度下降法
- 我的世界服务器修改id指令,我的世界指令怎么用,我的世界附魔指令id
- 用CHATGPT做微信视频号和抖音大神,就这几步!
热门文章
- java c 大学_为什么大多数大学先将C语言作为首学语言,看完你明白了吗?
- 2020 京东全民养红包 全民营业 自动任务 一键完成 一键抢红包
- java -jar xx.jar 时报错:Exception in thread main java.lang.UnsupportedClassVersionError:
- 【题解】信使(msner)
- LED显示屏上的毛毛虫的形成原因
- 远景能源电话面试总结
- [总结]软工文档验收
- 非常规应用之负电源稳压IC
- 民族瑰宝-台湾阿美族歌手-郭英男 介绍
- Python的GUI开发:小试wxPython(上)