Android设备唯一标识符(适配Android Q)

 

目录

Android设备唯一标识符(适配Android Q)

一、需求场景

二、Android设备信息

1、DeviceId(IMEI)

2、AndroidId

3、Serial Number

4、Wlan或者蓝牙的MAC地址

5、SIM Serial Number

6、IMSI

三、唯一识别符方案

1、设计原则

2、方案实现


一、需求场景

目前常见的使用场景:

1、标识唯一设备,用于数据统计或者后台服务精准下发

2、用于账号与设备绑定

例如,数据上报到自己的统计服务器;

根据特定用户下发奖励或者其他;

会员账号只能绑定3个设备终端

基于以上需求,我们需要可以获取到当前设备的唯一标识符

二、Android设备信息

1、DeviceId(IMEI)

对于GSM手机来说,DeviceId为IMEI,而对于CDMA手机而言则为MEID,目前国内大部分手机主要是IMIE

IMEI是国际移动设备识别码,即通常说的手机串号,用于标识在移动电话网络中每一部独立的手机等移动通信设备。

(1)获取方式

  /**     * 获取设备ID,GSM手机为IMEI、CDMA手机为MEID     *     * @param context     * @return     */    public static String getDeviceId(Context context) {        if (context == null) {            return "";        }        try {            TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);            if (PermissionUtils.checkPermissionForApi23(context, Manifest.permission.READ_PHONE_STATE)) {                return telephonyManager.getDeviceId();            } else {                LogUtils.e(TAG, "getDeviceId error: permission denied");            }        } catch (Exception e) {            e.printStackTrace();            LogUtils.e(TAG, "getDeviceId error: " + e.getMessage());        }        return "";    }

(2)缺点

  • 在Android6.0以及之后,需要动态获取android.permission.READ_PHONE_STATE 权限。因此存在用户拒绝授权的可能,此外首次启动后就上报设备ID时也可能影响启动速度
  • 可能存在获取不到DeviceId的可能,存在返回null或者000000的垃圾数据可能
  • 只对有电话功能的设备有效(无需插卡,但是需要有对应硬件模块)。例如在部分pad上可能无法获取到DeviceId

 

 

2、AndroidId

设备首次启动后系统会随机生成一个64位的数字,用16进制字符串的形式表示,例如:4351daa4516303b3,4351 daa4 5163 03b3

(1)获取方式

/** * 获取AndroidId * * @param context * @return */public static String getAndroidId(Context context) {    if (context == null) {        return "";    }    String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);    return (TextUtils.isEmpty(androidId) ? "" : androidId);}

(2)缺点

  • 恢复出厂或者刷机后会被重置
  • 部分厂商定制系统中,可能为空,也可能是不同设备中会产生相同的值
  • 对于CDMA设备汇总,AndroidId和DeviceId会返回相同的值

 

3、Serial Number

产品序列号,这里需要区分DeviceId。

(1)获取方式

/** * 获取android序列号 * * @return id或者空串 */private static synchronized String getSerialNumber() {    String serialNumber = null;    try {        Class<?> clazz = Class.forName("android.os.SystemProperties");        if (clazz != null) {            Method method_get = clazz.getMethod("get", String.class, String.class);            if (method_get != null) {                serialNumber = (String) (method_get.invoke(clazz, "ro.serialno", ""));            }        }    } catch (Exception e) {        if (DEBUG) {            e.printStackTrace();        }    }    return serialNumber != null ? serialNumber : "";}

(2)缺点

  • 部分设备无法获取到
  • 部分红米手机都会返回无用值0123456789ABCDEF

 

4、Wlan或者蓝牙的MAC地址

(1)获取方式

/** * 获取MAC地址 * * @param context * @return */public static String getMacAddress(Context context) {    String mac;    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {        mac = getMacDefault(context);    } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {        mac = getMacAddressM();    } else {        mac = getMacFromHardware();    }    return mac;}/** * Android  6.0 之前(不包括6.0) * 必须的权限   * @param context * @return */private static String getMacDefault(Context context) {    String mac = "02:00:00:00:00:00";    if (context == null) {        return mac;    }    WifiManager wifi = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);    if (wifi == null) {        return mac;    }    WifiInfo info = null;    try {        info = wifi.getConnectionInfo();    } catch (Exception e) {        e.printStackTrace();    }    if (info == null) {        return mac;    }    mac = info.getMacAddress();    if (!TextUtils.isEmpty(mac)) {        mac = mac.toUpperCase(Locale.ENGLISH);    }    return mac;}/** * Android 6.0(包括) - Android 7.0(不包括) * @return */private static String getMacAddressM() {    String wifiaddress = "02:00:00:00:00:00";    try {        wifiaddress = new BufferedReader(new FileReader(new File("/sys/class/net/wlan0/address"))).readLine();    } catch (IOException e) {        e.printStackTrace();    }    return wifiaddress;}/** * 遍历循环所有的网络接口,找到接口是 wlan0 * 必须的权限  * @return */private static String getMacFromHardware() {    try {        List all = Collections.list(NetworkInterface.getNetworkInterfaces());        for (NetworkInterface nif : all) {            if (!nif.getName().equalsIgnoreCase("wlan0")) {                continue;            }            byte[] macBytes = nif.getHardwareAddress();            if (macBytes == null) {                return "";            }            StringBuilder res1 = new StringBuilder();            for (byte b : macBytes) {                res1.append(String.format("%02X:", b));            }            if (res1.length() > 0) {                res1.deleteCharAt(res1.length() - 1);            }            return res1.toString();        }    } catch (Exception e) {        e.printStackTrace();    }    return "02:00:00:00:00:00";}

 

(2)缺点:

  • 需要设备具备蓝牙或者wifi硬件
  • 蓝牙Mac,在蓝牙未打开时是无法获取到mac地址的
  • wlan的mac,需要打开过wlan
  • 需要蓝牙、wifi权限

 

5、SIM Serial Number

SIM卡的序列码

(1)获取方式

TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE); String SimSerialNumber = tm.getSimSerialNumber(); 

(2)缺点

对于CDMA设备而言,返回的是空值

6、IMSI

国际移动用户识别码,用于区分移动用户,存储在SIM卡中。

(1)获取方式

/** * 获取SIM卡的IMSI码 * 

* SIM卡唯一标识:IMSI 国际移动用户识别码(IMSI:International Mobile Subscriber Identification Number)是区别移动用户的标志, * 储存在SIM卡中,可用于区别移动用户的有效信息。IMSI由MCC、MNC、MSIN组成,其中MCC为移动国家号码,由3位数字组成, * 唯一地识别移动客户所属的国家,我国为460;MNC为网络id,由2位数字组成, * 用于识别移动客户所归属的移动网络,中国移动为00、02,中国联通为01,中国电信为03;MSIN为移动客户识别码,采用等长11位数字构成。 * 唯一地识别国内GSM移动通信网中移动客户。所以要区分是移动还是联通,只需取得SIM卡中的MNC字段即可 * * @param context * @return */public static String getSubscriberId(Context context) { if (context == null) { return null; } try { TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); if (PermissionUtils.checkPermissionForApi23(context, Manifest.permission.READ_PHONE_STATE)) { return telephonyManager.getSubscriberId(); } else { LogUtils.e(TAG, "getSubscriberId error: permission denied"); } } catch (Exception e) { e.printStackTrace(); } return null;}

(2)缺点

  • 需要有电话功能
  • Android6.0以及以上需要获取权限

 

三、唯一识别符方案

上面列举了我们可以获取到的设备信息,而我们唯一的标识符就需要利用上面的信息。

1、设计原则

设计方案需要考虑以下因素:

  • 卸载重装或者清除数据
  • 刷机或者恢复出厂
  • 是否需要联网或者打开蓝牙

由于国内厂商的定制Rom等因素以及Android 系统本身唯一ID设计问题,我们没有办法保证各种场景下都100%唯一,因此我们的原则就是要在以上几种情况中,尽可能保证同一设备的用户是唯一ID,不会因为卸载或者重装等导致用户Id改变

2、方案实现

(1)Android Q以下

针对Android Q以下(不包括Android Q),这里我们提供一种实现思路:IMEI+Android Id+Serial Number。接下来我们具体来分析

对于设备A来说

信息

取值

影响因素

 

IMEI

Null,或者""或者垃圾值或者具体的有效值

与设备有关,与具体应用无关,不受应用卸载或者重装系统、刷机等影响

 

Android Id

Null或者“”或者垃圾值或者具体有效值

与系统首次启动有关,不受应用卸载重装影响,但是会受到重装或者恢复出厂影响

 

Serial Number

Null或者“”或者垃圾值或者具体有效值

与设备有关,不受应用卸载影响,不受系统重装或者恢复出厂影响

 

 

对于Android Q以下,根据我们上面的设备信息分析,可以根据IMEI+AndroidID+Serial Number来确定唯一设备ID,基本上可以将重复设备的比例降到很低,具体比例没有研究过,但是目前我们的应用采用的就是这套方案

具体如下:

private static synchronized String calcWid(Context ctx) {    mImei = DeviceUtils.getDeviceId(ctx);    String s = mImei + getAndroidId(ctx) + getSerialNumber();    mWid = md5(s.getBytes());    return mWid;}

缺点:

(1)系统刷机或者恢复出厂后就是新设备ID,对于恢复出厂和刷机都属于极端操作,概率很低

(2)极端情况下IMI、AndroidID或者SerialNumber均获取失败或者多个设备获取到的都是相同值,那么就会出现多台设备是同一Deviceid,但是这样的概率很低

 

综合分析,以上方案除了极低概率出现问题外,可以解决唯一设备标识符问题

(2)Android Q以上(包含Android Q)

在Android Q以前,我们通过权限

可以获取到IMEI和Serial Number,但是在Android Q中我们获取该权限会被Denied,按照官方API可以通过权限

来获取,但是这个权限只有在系统应用中才可以使用,因此也是无用

 

因此我们无法使用IMEI以及Serial Number,那么只能考虑牺重复率来适配Android Q

 

360浏览器插件中对于Android Q的适配中,使用AndroidId作为唯一设备ID,由于目前了解到的适配方案较少,暂时我们这里可以借鉴采用这种方案,当然带来的问题是用户恢复出厂或者刷机后该值会发生改变。

具体获取AndroidId的方法在前面的内容中这里不做赘述。

 

 

联系方式:

QQ:719074460

 

 

 

 

 

更多相关文章

  1. Android获取存储设备挂载路径
  2. Android设置权限问题
  3. Android有两种方法检测USB设备插入
  4. 【Android开发学习01】与Android实体设备的连接
  5. android蓝牙BLE(三) —— 广播
  6. android apk的签名和权限问题
  7. android基础知识17:Android设备常见问题与测试要领
  8. delphi XE开发微信支付Android获取手机存储权限、Android获取短

随机推荐

  1. android技术文章网址
  2. Android(安卓)高效的SQLite型数据库green
  3. Android(安卓)API与Android版本的关系
  4. Android(安卓)path环形
  5. Android改变wifi状态必须要的权限
  6. android中ui添加水平线
  7. Android使用EditText小技巧汇总
  8. android关于getLayoutParams()方法源码译
  9. Android之自定义View
  10. Android之简单的拍照功能