Android获取的IMEI只有14位问题解决

此文章已收入Android偶遇杂症合集(持续更新)

1、遇到的问题

在手机设置里的信息上IMEI有15位的数字,但通过代码获得却只有14位,少了最后一位数字。手机重新开机,代码获得了正确的15位数字。最终测试现象,获取方法不可控,返回值可能14位也可能15位,使用时很容易导致数据异常。

2、获取IMEI的常规方式

public static String getIMEI(Context context){
	String imei = "";
	try {
		TelephonyManager tm = (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
		if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){
			imei = tm.getDeviceId();
		}else {
			Method method = tm.getClass().getMethod("getImei");
			imei = (String) method.invoke(tm);
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
	return imei;
}

3、解决方式

3.1 IMEI的结构

第一部分 TAC,Type Allocation Code,类型分配码,由8位数字组成(早期是6位), 是区分手机品牌和型号的编码,该代码由GSMA及其授权机构分配。其中TAC码前两位又是分配机构标识 (Reporting Body Identifier),是授权IMEI码分配机构的代码,如01为美国CTIA,35为英国BABT,86为中国TAF。
第二部分 FAC,Final Assembly Code,最终装配地代码,由2位数字构成, 仅在早期TAC码为6位的手机中存在,所以TAC和FAC码合计一共8位数字。FAC码用于生产商内部区分生产地代码。
第三部分 SNR,Serial Number,序列号,由第9位开始的6位数字组成,区分每部手机的生产序列号。
第四部分 CD,Check Digit,验证码,由前14位数字通过 Luhn算法计算得出。

由此看出,最后一位是可以直接由前14位计算出来的,那么我们直接判断长度后再计算一遍就好了。

3.2 Luhn算法

  1. 将偶数位数字分别乘以2,分别计算个位数和十位数之和
  2. 将奇数位数字相加,再加上上一步算得的值
  3. 如果得出的数个位是0则校验位为0,否则为10减去个位数

如:35 89 01 80 69 72 41

偶数位乘以2得到

5*2=10 、 9*2=18 、 1*2=02 、 0*2=00 、 9*2=18 、 2*2=04 、 1*2=02

计算(奇数位数字)+(上一步计算的偶数位乘积的十位 + 个位),得到

3+(1+0)+8+(1+8)+0+(0+2)+8+(0+0)+6+(1+8)+7+(0+4)+4+(0+2)=63

最后位校验位为 10-3 = 7

所以完整是358901806972417

3.3 Java实现

public static String getIMEI14To15(String imei) {
	int last = 0, i = 0, temp;
	while (i < imei.length()){
		// 加上奇数位
		result += Integer.parseInt(imei.substring(i, ++i));
		// 偶数位乘以2
		temp = Integer.parseInt(imei.substring(i, ++i)) * 2;
		// 加上偶数位的十位和个位,这里做了优化操作
		// 偶数位乘2的乘积范围为[0, 18],小于10直接加,大于10则个位与十位的和等于乘积减9
		result += temp < 10 ? temp : temp - 9;
	}
	// 取最终和的个位
	result %= 10;
	// 若为0则为0,否则为10减去个位
	result = result == 0? 0 : 10 - result;
	return imei + result;
}

public static String getIMEI(Context context){
	String imei = "";
	try {
		TelephonyManager tm = (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
		if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){
			imei = tm.getDeviceId();
		}else {
			Method method = tm.getClass().getMethod("getImei");
			imei = (String) method.invoke(tm);
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
	if (imei != null && imei.length() == 14) {
		imei = getIMEI14To15(imei);
	}
	return imei;
}
posted @ 2020-11-11 19:47  coder-ice  阅读(637)  评论(0编辑  收藏  举报