Android设备有唯一的ID吗?如果有,使用Java访问它的简单方法是什么?


当前回答

这是一个简单的问题,没有简单的答案。

此外,这里所有现有的答案要么过时,要么不可靠。

因此,如果您正在寻找2020年后的解决方案。

以下是需要记住的几点:

所有基于硬件的标识符(IMEI、MAC、序列号等)对于非谷歌设备(Pixels和Nexuses除外)都是不可靠的,从统计上看,这些设备是世界上大多数android活动设备。因此,官方Android标识符最佳实践明确指出:

避免使用硬件标识符,如IMEI、MAC地址等。。。

这使得这里的大多数答案无效。同样由于不同的android安全更新,其中一些需要更新和更严格的运行时权限,用户可以简单地拒绝。

例如,CVE-2018-9489影响上述所有基于WIFI的技术。

这使得这些标识符不仅不可靠,而且在许多情况下无法访问。

所以简单地说:不要使用这些技术。

这里的许多其他答案都建议使用AdvertisingIdClient,这也是不兼容的,因为它是专为广告分析而设计的。官方参考文件中也有说明

仅将广告ID用于用户分析或广告用例

它不仅对设备识别不可靠,而且您还必须遵守有关广告跟踪的用户隐私政策,该政策明确规定用户可以随时重置或阻止它。

所以也不要使用它。

因为您无法获得所需的静态全局唯一和可靠的设备标识符。Android的官方参考建议:

在所有其他使用情况下,尽可能使用Firebase安装ID(FID)或私人存储的GUID,支付欺诈预防和电话除外。

它对于设备上的应用程序安装来说是独一无二的,因此当用户卸载应用程序时,它会被删除,因此它不是100%可靠,但它是次佳选择。

注意:截至目前,FirebaseInstanceId已弃用,您应改用FirebaseInstallations。

要使用FirebaseInstallations,请将最新的firebase消息传递依赖项添加到您的等级中

implementation 'com.google.firebase:firebase-messaging:23.0.0'

并使用以下代码获取firebase ID:

FirebaseInstallations.getInstance().getId().addOnCompleteListener(task -> {
     if (task.isSuccessful()) {
        String firebaseIdentifier = task.getResult();
        // Do what you need with firebaseIdentifier
     }
});

如果您需要将设备标识存储在远程服务器上,那么不要按原样存储(纯文本),而是使用带盐的哈希。

今天,这不仅是一种最佳实践,实际上,您必须根据GDPR标识符和类似法规依法进行。

其他回答

如果添加:

Settings.Secure.getString(context.contentResolver,
    Settings.Secure.ANDROID_ID)

Android Lint将向您发出以下警告:

不建议使用getString获取设备标识符。检查信息:不建议使用这些设备标识符除了用于高价值欺诈预防和高级电话之外使用案例。对于广告用例,请使用AdvertisingIdClient$Info#getId,用于分析,请使用InstanceId#getId。

所以,你应该避免使用这个。

如Android开发者文档中所述:

1:避免使用硬件标识符。在大多数使用情况下,您可以避免使用硬件标识符,例如SSAID(Android ID)和IMEI,而不限制所需的功能。2:仅在用户分析或广告用例中使用广告ID。使用广告ID时,始终尊重用户的选择关于广告跟踪。此外,请确保标识符不能连接到个人身份信息(PII),并避免桥接广告ID重置。3:在所有其他用例中,尽可能使用实例ID或私有存储的GUID,但支付欺诈预防和电话除外。对于绝大多数非广告用例,实例ID或GUID应足够。4:使用适合您的用例的API,以最大限度地降低隐私风险。使用DRM API保护高价值内容用于滥用保护的SafetyNet API。SafetyNet API是确定设备是否为正品的最简单方法隐私风险。

通常,我的应用程序使用设备唯一id。但有时我会使用IMEI。两者都是唯一的数字。

获取IMEI(国际移动设备标识符)

public String getIMEI(Activity activity) {
    TelephonyManager telephonyManager = (TelephonyManager) activity
            .getSystemService(Context.TELEPHONY_SERVICE);
    return telephonyManager.getDeviceId();
}

获取设备唯一id

public String getDeviceUniqueID(Activity activity){
    String device_unique_id = Secure.getString(activity.getContentResolver(),
            Secure.ANDROID_ID);
    return device_unique_id;
}

Android在Android O之后限制硬件相关Id,Android_Id是唯一Id的解决方案,但当反射设备,它将生成新的android_id以克服此问题问题是我们可以使用DRUMID。

val WIDEVINE_UUID = UUID(-0x121074568629b532L, -0x5c37d8232ae2de13L)
val drumIDByteArray = MediaDrm(WIDEVINE_UUID).getPropertyByteArray(MediaDrm.PROPERTY_DEVICE_UNIQUE_ID)

val drumID = android.util.Base64.encodeToString(drumIDByteArray,android.util.Base64.DEFAULT)

只是提醒大家阅读更多最新信息。对于Android O,系统管理这些ID的方式有一些变化。

https://android-developers.googleblog.com/2017/04/changes-to-device-identifiers-in.html

tl;dr Serial将需要PHONE权限,Android ID将根据不同的应用程序的包名和签名进行更改。

此外,谷歌还编制了一份很好的文档,提供了有关何时使用硬件和软件ID的建议。

https://developer.android.com/training/articles/user-data-ids.html

API级别9(Android 2.3-姜饼)中的Build类中添加了一个Serial字段。文档表示它代表硬件序列号。因此,如果设备上存在,它应该是唯一的。

我不知道API级别>=9的所有设备是否都支持(=不为空)。