1. 使用场景

1. 业务角度

在业务的推广中,需要对每台设备每个用户进行一个唯一识别码来支持业务场景,但对于每一台设备或者每一个用户的定义各有不同。那针对于不同的业务角度,如何识别一台新设备与一个新的用户加入对App来说是有意义的。

2. 数据采集及分析

对App的数据采集,及后台的数据分析有一个对应关系。

3. 应用系统

应用系统使用,可以定义游客与登录客户的映射关系,或者游客相关的记录存档。

2. 设备ID可选值及问题

目前开发者需要找到获取设备唯一ID的方式,目前从设备来看,可以作为设备唯一性的属性主要有:

Given below are the different types of identifying devices ID in android devices.

Unique number (IMEI, MEID, ESN, IMSI)

MAC Address

Serial Number,SN(设备序列号)

ANDROID_ID

由于国内复杂的ROM定制环境,用户权限拒绝,DeviceID在使用过程中并不能百分百获取到。各种有各种不同的优缺点,具体优缺点分析:

  • devices ID

Android设备需要有电话服务,如果刷机了该值不会更改,即使该手机可能刷机给别人正在使用,就可能造成误解,该设备为同一个人使用。

缺点:

如果用户禁用掉相关权限,不能获取到deviceId

使用该deviceId,该识别符为设备硬件相关,即使刷机也不会改变该值。

  • The IMEI, MEID, ESN, IMSI can be defined as follows:

IMEI( International Mobile Equipment Identity )
    国际移动设备识别码(International Mobile Equipment Identity,IMEI),即通常所说的手机序列号、手机“串号”,用于在移动电话网络中识别每一部独立的手机等移动通信设备,相当于移动电话的身份证。IMEI是与手机绑定的。

目前GSM/WCDMA/LTE手机终端需要使用IMEI号码,在单卡工程中一个手机号对应一个IMEI号,双卡手机则会对应两个IMEI号,一张是手机卡对应一个。

MEID(Mobile Equipment IDentifier)
    Mobile Equipment IDentifier(MEID)是一个全球唯一的56bit移动终端标识号,在手机键盘直接键入*#06#可获得。标识号会被输入终端里,以后可以被修改。可用来对移动式设备进行身份识别和跟踪。由于ESN号段是有限的资源,基本上耗尽,可能还有少量回收利用的号段,所以制定了56位的MEID号段,用来取代32位的ESN号段。MEID主要分配给CDMA制式的手机。

 

IMSI(International Mobile Subscriber Identity)
   GSM与UMTS网络下移动用户识别码。国际移动用户识别码(IMSI:International Mobile Subscriber Identification Number)是区别移动用户的标志,储存在SIM卡中,可用于区别移动用户的有效信息。

  • Mac地址:

它为设备网卡地址,返回一个Wi-Fi或者蓝牙硬件的Mac地址,但是它不能作为一个唯一设备。

缺点:

必须具备硬件,并非所有设备都有Wi-Fi功能

如果设备有Wi-Fi,但也应该打开否则也获取不到地址

在Android6.0以后 google 为了运行时权限对geMacAddress();作出修改通过该方法得到的mac地址永远是一样的,

  • SN:

从Android 2.3(“Gingerbread”)版本后可以通过android.os.Build.SERIAL获取,SN能识别例如MIDs (Mobile Internet Devices) or PMPs (Portable Media Players)没有电话服务的。也可以通过读取” ro.serialno”获取。

缺点:

不是所有设备可用,有些手机出现无用数据。比如红米手机返回的就是连续的非随机数。有些ROM定制机或者rooted设备有时会生成无效数据。

  • ANDROID_ID:

通过Settings.Secure.ANDROID_ID获取,一串64位(16进制)的字符串,设备first boot时随机生成,如果恢复出厂设置,或者刷机ROM就是改变该值。

缺点:

手机恢复出厂设置以后该值会发生变化

在国内Android定制的大环境下,有些设备是不会返回ANDROID_ID的

存在一个BUG,在部分设备厂商可能会有相同的ANDROID_ID

综上:

因为Android设备的多系统版本,定制系统复杂,Google Bug,root操作,定制ROM系统,多用户支持。Android设备目前没有统一的一个获取设备ID的方法,不能依靠一个简单的方式获取设备唯一识别码。

3. 生成方案:

  • 根据ID生命周期:

deviceID: 与设备强关联,设备不变该值永远不会改变,部分设备也可能为空。

ANDROID_ID:与操作系统关联,如果恢复出厂设置或者root,则该值会改变。

SoftInstalIID: 与应用相关,应用安装即生成SoftInstall的ID,应用卸载后重新安装则该值会改变。

  • 根据场景:

原则上来说,刷新手机或者恢复出厂设置都可以让旧用户所有数据丢失,使用可以认为是一台新的机器,可以认定为新的用户使用。

  • 尽量ID值不改变

实现:根据SN+ANDROID_ID+BRAND_INFO, 再统一HASH_CODE格式,生成设备ID,在手机SD卡中新建隐藏文件进行存储,再次进入时如果有,则使用之前的,没有则生成新的(生成新的ID的关键因子不变,则生成的新ID也不会改变)。

4. 关键代码:

获取序列号:

private static String getSerialId() {
    String androidSerialId = null;
    try {
        // try to get device serial number
        androidSerialId = Build.SERIAL;
    } catch (NoSuchFieldError ignored) {
    }
    return androidSerialId;
}

 

获取ANDROID_ID:

private static String getAndroidId(Context context) {
    String androidId = null;
    String androidSecureId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
    if (!TextUtils.isEmpty(androidSecureId) && !EMULATOR_ANDROID_ID.equals(androidSecureId) && !isBadDeviceId(androidSecureId)
            && androidSecureId.length() == EMULATOR_ANDROID_ID.length()) {
        androidId = androidSecureId;
    }
    return androidId;
}

 

获取手机关键字:

private static String getZacBrandId() {
    return Build.BOARD
           
+ Build.BRAND
           
+ Build.CPU_ABI
           
+ Build.DEVICE
           
+ Build.MANUFACTURER
           
+ Build.MODEL
           
+ Build.PRODUCT;
}

 

最终生成设备ID号:

public static String readDeviceId(Context context) {
    String id1 = getSerialId();

    String id2 = getAndroidId(context);

    String id3 = getZacBrandId();

    String id;
    if(TextUtils.isEmpty(id1) && TextUtils.isEmpty(id2)) {
        id = SoftInstallationId.id(context) + id3;
    } else {
        id = id1 + id2 + id3;
    }
    return new UUID(id.hashCode(), id3.hashCode()).toString();
}