Android开发过程中避免不了要获取一些与设备相关的信息,比如deviceId,判断网络类型等。有时候我们到手机设备信息界面看到一个陌生的代号也是一头雾水,虽然有一种似曾相识的感觉,但是确实不知道是干什么,本篇就为你揭开这层面纱。
科普几个概念
常规
- IMEI
- MEID
- IMSI
- ICCID
- 基带版本
- 内核版本
Android
- DeviceId
- AndroidId
- 序列号
IMEI
国际移动设备识别码(International Mobile Equipment Identity,IMEI),即通常所说的手机通信序列号、手机「串号」,用于在行动电话网络中识别每一部独立的手机等行动通讯装置,共有15位数字。
手机号码盘输入“*#06#”可显示当前手机的IMEI信息。IMEI作为手机通讯序列号,在某些不包含通信功能设备上自然也就不存在这个值了。另外双卡双待手机可以拿到两个IMEI序列号。
MEID
移动设备识别码(Mobile Equipment Identifier)是CDMA手机的身份识别码,也是每台CDMA手机或通讯平板唯一的识别码。通过这个识别码,网络端可以对该手机进行跟踪和监管。用于CDMA制式的手机。MEID的数字范围是十六进制的,和IMEI的格式类似。
因为MEID是CDMA的身份标识,某些手机不支持CDMA(电信)制式,因此在系统信息界面也就不会显示这个值了,也可以作为全网通和移动联通手机的区分。
IMSI
国际移动用户识别码(International Mobile Subscriber Identification Number)是区别移动用户的标志,储存在SIM卡中,可用于区别移动用户的有效信息,长度不超过15位,实际使用的IMSI的长度绝大部分都是15位。
IMSI结构如下:MCC+MNC+MIN
其中:
- MCC:Mobile Country Code,行动装置国家代码,共3位,中国为460;
- MNC:Mobile Network Code,移动设备网络代码,2位(欧洲标准)或3位(北美标准),中国电信CDMA系统使用03;
Android开发过程中区分网络运营商:
public static String getSimOperator() {
TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
String imsi = tm.getSubscriberId();
String operator = "";
if (!TextUtils.isEmpty(imsi)) {
if (imsi.startsWith("46000") || imsi.startsWith("46002") || imsi.startsWith("46007")) {
// 中国移动
operator = "中国移动";
} else if (imsi.startsWith("46001") || imsi.startsWith("46006")) {
// 中国联通
operator = "中国联通";
} else if (imsi.startsWith("46003") || imsi.startsWith("46005")) {
// 中国电信
operator = "中国电信";
}
}
return operator;
}
当然也可以采用TelephonyManager中的getSimOperator()方法来获取:
public static String getSimOperator() {
TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
String operatorNum = tm.getSimOperator();
String operator = "";
if (!TextUtils.isEmpty(operatorNum)) {
if ("46000".equals(operatorNum) || "46002".equals(operatorNum) || "46007".equals(operatorNum)) {
// 中国移动
operator = "中国移动";
} else if ("46001".equals(operatorNum) || "46006".equals(operatorNum)) {
// 中国联通
operator = "中国联通";
} else if ("46003".equals(operatorNum) || "46005".equals(operatorNum)) {
// 中国电信
operator = "中国电信";
}
}
return operator;
}
ICCID
集成电路卡识别码(Integrate circuit card identity),固化在手机SIM卡中 ICCID为IC卡的唯一识别号码,共有20位数字组成, 其编码格式为:XXXXXX 0MFSS YYGXX XXXX。分别介绍如下: 前六位运营商代码:中国移动的为:898600;898602;898604;898607 ,中国联通的为:898601、898606、898609,中国电信898603。SIM卡的ICCID不正规途径可以进行伪造。
ICCID和IMSI区别:
- 一张SIM卡,里面有ICCID,也有IMSI。 ICCID是卡的标识,IMSI是用户的标识。
- ICCID只是用来区别SIM卡,不作接入网络的鉴权认证。而IMSI在接入网络的时候,会到运营商的服务器中进行验证。
基带版本
基带版本就是手机的调制解调器使用的驱动版本号,调制解调器主要目的负责着手机的通信功能(打电话,发短信,数据交换等)。
基带(Baseband)是手机中的一块电路,负责完成移动网络中无线信号的解调、解扰、解扩和解码工作,并将最终解码完成的数字信号传递给上层处理系统进行处理。
内核版本
内核版本就是手机的硬件(如主板,GPS,摄像头,WiFi,蓝牙等)驱动集合体的版本号。
DeviceId
设备唯一标识,针对Android设备,它根据不同的手机设备返回IMEI,MEID或者ESN码。手机制式为GSM时,返回手机的IMEI;手机制式为CDMA时,返回手机的MEID或ESN。
开发过程中一般用DeviceId来标识唯一设备,需要预先申请电话权限,获取deviceId方法如下:
public static String getDeviceID() {
TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
return tm.getDeviceId();
}
但是某些情况下获取DeviceId可能为空,具体原因有:
- 只有拥有通讯打电话功能的android设备才有IMEI号,WIFI平板获取不到。
- Android6.0以上设备获取deviceId需要申请系统信息相关权限,用户不同意权限同样拿不到。
- 即使有打电话功能,国内部分厂商自己的系统可能会把deviceId值改变,从而得到的是空值、”null”或类似00…相同的值。
AndroidId
在设备首次启动时,系统会随机生成一个64位的数字,并把这个数字以16进制字符串的形式保存下来,这个16进制的字符串就是ANDROID_ID,来标识当前系统的唯一性,当设备被恢复出厂设置后该值会被重置。
如果想要采用AndroidId作为设备唯一ID使用,获取androidId方法如下:
public static String getAndroidId() {
return android.provider.Settings.Secure.getString(
mContext.getContentResolver(),
android.provider.Settings.Secure.ANDROID_ID);
}
但需要考虑以下几种情况:
- 在Android <=2.1 or Android >=2.3的版本是可靠、稳定的,但在2.2的版本并不是100%可靠的
。 - 厂商定制系统的Bug:不同的设备可能会产生相同的ANDROID_ID,有些设备返回的值为null。
- 设备差异:对于CDMA设备,ANDROID_ID和TelephonyManager.getDeviceId() 返回相同的值。
并且,如果某个Andorid手机被Root过的话,这个ID也可以被改变。
序列号
Serial Number,或叫SN,即设备生产时确定的一串序列号,例如5JPDU17519001099。
设备序列号也可作为设备唯一标识,获取SN号方法如下:
public static String getSerialNumber() {
return android.os.Build.SERIAL;
}
但有些设备获取SN也不准确(汗!!!):
- 对CDMA设备,返回的是一个空值。
- 在少数的一些设备上,会返回垃圾数据。对于没有通话功能的设备,它可能会返回一个固定的值。
Android获取唯一标识
某一天产品经理告诉我必须要有一个值来确定唯一设备,但是根据上边的deviceId、androidId、SN号,似乎都不能准备来确定唯一的值,无奈啊。你也可能会说,我也可以根绝wifi MAC地址,蓝牙MAC地址来确定,但是我也可以明确的告诉你,针对wifi MAC地址和蓝牙MAC地址也是受系统影响的,在某时候关闭wifi和蓝牙并不能获取到MAC地址,而且在Android6.0的时候google将wifi MAC地址封闭了,各个设备返回的都是“02:00:00:00:00:00”常量值。
google的官方文档明确指出wifi地址问题:
Most notably, Local WiFi and Bluetooth MAC addresses are no longer available. The getMacAddress() method of a WifiInfo object and the BluetoothAdapter.getDefaultAdapter().getAddress() method will both return 02:00:00:00:00:00 from now on.
这种情况下我们应该怎么办呢?我也只能说“如果拿不到只能自己造了”,以下是我给出的一种方案,似乎也能满天过海吧。
思想:
- App启动闪屏页或登录页首先检测电话管理权限和读写权限(为后边获取存储唯一ID做准备);
- 分别判断SD卡和Sharepreference是否保存有之前保存的设备唯一ID:
- SP和SD都有,判断是否一致,不一致,复制SP到SD,取SP值。
- SP和SD有且只有其中一个有,相互复制,然后取值。
- SP和SD都没有:
- 获取deviceId,若为空,向下取;
- 获取SN,若为空,向下取;
- UUID生成一个,分别保存到SD和SP。
注意:SD卡上保存的时候,路径隐蔽一些,防止被删除。
以上方法,只要不刷机,获取到的值都是唯一的。
public static String getDeviceId0() {
final String DEVICE_ID_SP = "DEVICE_ID";
//文件名前边加个“.”可隐藏此文件。
final String DEVICE_ID_SD_SDR = "Android/data/.device";
String spDeviceId = (String) SPUtil.get(SPUtil.SP_GLOBAL, DEVICE_ID_SP);
String sdDeviceId = FileUtil.readFileFromSD(DEVICE_ID_SD_SDR);
String deviceId;
boolean isSaveSP = false;
boolean isSaveSD = false;
//获取deviceId;
if (!IsEmpty.string(spDeviceId) && !IsEmpty.string(sdDeviceId)) {
deviceId = spDeviceId;
if (!spDeviceId.equals(sdDeviceId)) {
isSaveSD = true;
}
} else if (!IsEmpty.string(spDeviceId) && IsEmpty.string(sdDeviceId)) {
deviceId = spDeviceId;
isSaveSD = true;
} else if (IsEmpty.string(spDeviceId) && !IsEmpty.string(sdDeviceId)) {
deviceId = sdDeviceId;
isSaveSP = true;
} else {
deviceId = getDeviceID();
if (IsEmpty.string(deviceId)) {
deviceId = "sn" + getSerialNumber();
if (IsEmpty.string(deviceId)) {
UUID id = UUID.randomUUID();
String[] idd = id.toString().split("-");
deviceId = "uu" + idd[0] + idd[1] + idd[2];
}
}
isSaveSP = true;
isSaveSD = true;
}
//存储deviceId;
if (isSaveSP) {
SPUtil.put(SPUtil.SP_GLOBAL, DEVICE_ID_SP, deviceId);
}
if (isSaveSD) {
FileUtil.writeFileToSD(DEVICE_ID_SD_SDR, deviceId);
}
return deviceId;
}
Tip
- 这对手机制式GSM、CDMA、WCDMA等本篇不做过多阐述,可自行研究了解。
- Android开发过程中根据TelephonyManager的getLine1Number有些时候并不一定能拿到电话号码,此种情况只能够读取写入到SIM卡的手机号,有些电话卡上的电话号码只存在运营商服务器上(例如移动)。
转载请注明出处,我是toperc.