随着Google对隐私的重视以及Android10的逐渐普及,获取设备的唯一标识越来越来难,在Android10以前,Android设备唯一标识包含IMEI、AndroidID、DeviceID、Mac地址等,下面收集了一些唯一ID的获取方案:
1. DEVICE_ID、IMEI
获取方法:
TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
String DEVICE_ID = tm.getDeviceId();
//String DEVICE_ID = tm.getImei();
这是Android系统为开发者提供的标识手机设备串号的方法,局限性:
- Android6以后需要
READ_PHONE_STATE
权限才能获取到,请求这个权限会提示给用户需要读取手机、电话状态,很多用户会拒绝授权 - 极少数手机上该实现有漏洞,会返回垃圾
2. Mac地址
可以使用手机WiFi或者蓝牙的Mac地址作为设备标识,Android 6.0以后通过 WifiManager 获取到的mac将是固定的:02:00:00:00:00:00 ,
再后来连读取 /sys/class/net/wlan0/address 也获取不到了。
现在只剩下面这种方法可以获取(没有开启wifi也可以获取到):
/**
* 获取手机Mac地址
*
* @return
*/
public static String getMacAddress(Context mContext) {
WifiManager wifiMgr = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInf = wifiMgr.getConnectionInfo();
// wifiInf.getMacAddress().getMacAddress方法在安卓6.0系统上获取到的Mac 都是 02:00:00:00:00:00。
String invalidMacAddress = "02:00:00:00:00:00";
if (wifiInf.getMacAddress().equals(invalidMacAddress)) {
String ret = null;
try {
ret = getAdressMacByInterface();
if (ret != null) {
return ret;
} else {
ret = getAddressMacByFile(wifiMgr);
return ret;
}
} catch (IOException e) {
Log.e("TAG", "Erreur lecture propriete Adresse MAC");
} catch (Exception e) {
Log.e("TAG", "Erreur lecture propriete Adresse MAC ");
}
} else {
return wifiInf.getMacAddress();
}
return invalidMacAddress;
}
/**
* 获取6.0以上系统的mac值
* @throws Exception
*/
private static String getAddressMacByFile(WifiManager wifiMan) throws Exception {
String fileAddressMac = "/sys/class/net/wlan0/address";
String ret;
int wifiState = wifiMan.getWifiState();
wifiMan.setWifiEnabled(true);
File fl = new File(fileAddressMac);
FileInputStream fin = new FileInputStream(fl);
ret = crunchifyGetStringFromStream(fin);
fin.close();
boolean enabled = WifiManager.WIFI_STATE_ENABLED == wifiState;
wifiMan.setWifiEnabled(enabled);
return ret;
}
/**
* 获取6.0以上系统的mac值
* @throws Exception
*/
private static String crunchifyGetStringFromStream(InputStream crunchifyStream) throws IOException {
if (crunchifyStream != null) {
Writer crunchifyWriter = new StringWriter();
char[] crunchifyBuffer = new char[2048];
try {
Reader crunchifyReader = new BufferedReader(new InputStreamReader(crunchifyStream, "UTF-8"));
int counter;
while ((counter = crunchifyReader.read(crunchifyBuffer)) != -1) {
crunchifyWriter.write(crunchifyBuffer, 0, counter);
}
} finally {
crunchifyStream.close();
}
return crunchifyWriter.toString();
} else {
return "No Contents";
}
}
private static String getAdressMacByInterface() {
try {
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (nif.getName().equalsIgnoreCase("wlan0")) {
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) {
Log.e("TAG", "Erreur lecture propriete Adresse MAC ");
}
return null;
}
局限性:
- 没有WiFi或蓝牙硬件的条件下获取不到
- 如果WiFi没有打开过,也不能获取到硬件地址;蓝牙只有在打开的时候才能获取到硬件地址
- 不稳定,Google官方对这个方法限制越来越严,后面新版本可能就取不到了
3. ANDROID_ID
设备首次启动时,系统会随机生成一个64位的数字,并将这个数字以16进制的形式保存下来,flutter官方组件device_info就是通过这个方式获取的,获取方式:
String ANDROID_ID = Settings.System.getString(getContentResolver(), Settings.System.ANDROID_ID);
局限性:
- 厂商定制的bug,不同的设备可能会产生相同的ANDROID_ID:9774d56d682e549c,甚至有些设备会返回null
- 设备被重置以后会重新生成
- AndroidID会根据APP的签名来生成,这也就导致不同的APP获取到的AndroidID是不一致的
4. 设备序列号
获取方式:
String serial = android.os.Build.SERIAL;
局限性:
- 厂商不规范,设备序列号+Build.MANUFACTURER应该是能唯一标识设备的,但并不是所有厂商都按照这个规范来做的,尤其是早期的设备
- Android 8.0 以上,android.os.Build.SERIAL 总返回 “unknown”;若要获取序列号,可调用Build.getSerial() ,但是需要申请
READ_PHONE_STATE
权限,这个权限之前介绍了,很多用户会拒绝授权 - 到了Android10以上,跟IMEI一样被禁用了
结合以上几点来看,单独采用其中某一个方案都不是很完美,所以移动安全联盟MSA搞了一个OAID,这个本质上也是一个设备的唯一标识,目前已经支持的厂商包括:华为、小米、OPPO、vivo、中兴、努比亚、魅族、联想、三星等
5. OAID
目前已经开发完成,项目地址:https://gitlab.xsyxsc.com/xsfe_flutter/application/plugin/xsyx_mitt_plugin
支持终端版本:
厂商 | 版本 |
---|---|
小米 | MIUI10.2 及以上 |
vivo | FuntouchOS 9 及以上 |
华为 | 全版本 |
OPPO | Color OS 7.0 及以上 |
Lenovo | ZUI 11.4 及以上 |
华硕 | Android Q |
魅族 | 已支持(具体版本号查询官网) |
三星 | 已支持(具体版本号查询官网) |
中兴 | 已支持(具体版本号查询官网) |
努比亚 | 已支持(具体版本号查询官网) |
插件说明:OAID支持Android10以上的设备,以前的老设备以及没有更新的设备获取不到。获取到OAID为空的情况下,会自动尝试获取IMEI号,如果用户没有授权或者获取不到IMEI的情况下,会尝试获取MAC地址,再获取不到的时候采用兜底方案AndroidID
获取顺序:
OAID -> IMEI(需要授权) -> MAC地址 -> AndroidID