如何使用Android唯一标识符
1、TelephonyManager.getDeviceId()
在Android10系统下调用此方法会直接抛异常。
部分原文引用如下:
返回唯一的设备ID,例如,GSM的IMEI和CDMA电话的MEID或ESN。如果设备ID不可用,则返回null。
从API级别29开始,永久设备标识符受到附加限制的保护,建议应用程序使用可重置标识符(请参阅最佳实践中的唯一标识符)。如果已授予调用应用程序READ_PRIVILEGED_PHONE_STATE权限则可以调用此方法,这是特权权限,只能授予设备上预加载的应用程序。
如果调用应用程序不满足这些要求之一,则此方法将表现如下:
如果调用应用程序的目标SDK为API级别28或更低,并且该应用程序具有READ_PHONE_STATE权限,则返回null。
如果调用应用程序的目标SDK是API级别28或更低,并且该应用程序没有READ_PHONE_STATE权限,或者如果调用应用程序的目标是API级别29或更高,则抛出SecurityException。
这个方法在API26(Android8.0)中弃用,说明应该是在Android8.0开始就获取不到deviceId了,具体没有实验过。
2、官方推荐方式
原文部分引用如下:
使用Android标识符时,请遵循以下最佳做法:
避免使用硬件标识符。在大多数情况下,您可以避免使用硬件标识符,例如SSAID(Android ID),而不会限制所需的功能。
Android 10(API级别29)为不可重置的标识符(包括IMEI和序列号)增加了限制。您的应用必须是设备或个人资料所有者应用,具有特殊的运营商权限或READ_PRIVILEGED_PHONE_STATE特权权限才能访问这些标识符。
仅将广告ID用于用户配置文件或广告用例。使用广告ID时,请始终尊重用户对广告跟踪的选择。另外,请确保标识符不能与个人身份信息(PII)连接,并避免桥接广告ID重置。
对于所有其他用例,请尽可能使用实例ID或私有存储的GUID,但要防止付款欺诈和打电话。对于绝大多数非广告用例,实例ID或GUID应该足够。
使用适合您的用例的API,以最大程度地降低隐私风险。使用 DRM API进行高价值内容保护,并使用 SafetyNet API进行滥用保护。SafetyNet API是确定设备是否为真品而不引起隐私风险的最简便方法。
目前不知道广告ID是什么鬼,暂时没有了解,看说明应该是说大多数应用应该使用实例ID或私有存储的GUID
关于实例ID和GUID的原谅引用如下:
标识设备上运行的应用程序实例的最直接的解决方案是使用实例ID,这是在大多数非广告用例中推荐的解决方案。仅为其提供了资源的应用程序实例可以访问此标识符,并且(相对)可以轻松重置该标识符,因为它仅在安装了该应用程序后才持续存在。
因此,与不可重置的设备范围的硬件ID相比,实例ID提供了更好的隐私属性。有关更多信息,请参阅 FirebaseInstanceIdAPI参考。
如果实例ID不可行,您还可以使用自定义全局唯一ID(GUID)来唯一标识应用程序实例。最简单的方法是使用以下代码生成自己的GUID:
var uniqueID = UUID.randomUUID().toString()
由于标识符是全局唯一的,因此可用于标识特定的应用程序实例。为避免与跨应用程序链接标识符相关的担忧,请将GUID存储在内部存储中,而不是外部(共享)存储中。有关更多信息,请参见数据和文件存储概述页面。
搞不清这里说的实例ID和GUID的区别,GUID还是看得懂的,就是创建UUID后持久化保存,推荐保存在内部存储,应该是应用一卸载UUID就跟着没有了,如果希望卸载了还有,则只能保存在外部(共享)存储中了,可以加密后存储。
关于可重置性和持久性
原文引用如下:
可重置性和持久性定义了标识符的寿命,并说明了如何重置它。常见的重置触发器包括:应用内重置,通过系统设置重置,启动时重置以及安装时重置。Android标识符的寿命可能会有所不同,但是寿命通常与ID的重置方式有关:
* 仅会话:每次用户重新启动应用程序时,都会使用一个新ID。
* Install-reset:每次用户卸载并重新安装应用程序时,都会使用一个新ID。
* FDR重置:每次用户出厂重置设备时都会使用一个新的ID。
* FDR持久:该ID在恢复出厂设置后仍然有效。
可重置性使用户能够创建与现有配置文件信息无关的新ID。标识符持续的时间越长且越可靠,例如在工厂重置期间仍然存在的标识符,则用户可能受到长期跟踪的风险就越大。如果在重新安装应用程序时重置了标识符,则即使没有明确的用户控制从应用程序或“系统设置”中将其重置,也可以减少持久性并提供一种用于重置ID的方法。
3 、使用MAC地址
如果希望不管怎样,即不论用户是格式化SDCard或者重装系统都能获取到一个一样的唯一识别码怎么处理?可通过获取wifi的mac地址来实现,但是在Anroid6.0时,无法获取wifi的mac地址,原文链接在此,原文引用如下:
为给用户提供更严格的数据保护,从此版本开始,对于使用 WLAN API 和 Bluetooth API 的应用,Android 移除了对设备本地硬件标识符的编程访问权。WifiInfo.getMacAddress() 方法和 BluetoothAdapter.getAddress() 方法现在会返回常量值 02:00:00:00:00:00。
原文引用如下:
MAC地址是全局唯一的,不能由用户重置,并且可以恢复出厂设置。由于这些原因,通常不建议将MAC地址用于任何形式的用户标识。运行Android 10(API级别29)及更高版本的设备会向不是设备所有者应用程序的所有应用程序随机报告MAC地址。
在Android 6.0(API级别23)和Android 9(API级别28)之间,本地设备MAC地址(例如Wi-Fi和蓝牙)无法通过第三方API使用。该 WifiInfo.getMacAddress() 方法与 BluetoothAdapter.getDefaultAdapter().getAddress() 双方返回方法02:00:00:00:00:00。
WifiInfo.getMacAddress() 这种方式获取不到MAC地址了,但是还有别的方式可以获取到,如下:
/** 通过NetworkInterface的方式获取Wifi的Mac地址,形式为:9c:20:1b:5d:59:98,如果获取不到返回null
* 注:此方法如果用作应用唯一标识码也不可可靠,在Anroid10上测试,连接wifi后,和没连wifi时拿到的mac地址不一样。
*/ private fun getWifiMacByNetworkInterface(): String? { var wifiMac: String? = null try { // wlan0为wifi的网络接口名称 val wifiNetworkInterface = NetworkInterface.getByName("wlan0") ?: return null val sb = StringBuffer() wifiNetworkInterface.hardwareAddress?.forEachIndexed { index, byte -> if (index > 0) sb.append(':') sb.append(byteToHex(byte)) } if (sb.isNotEmpty()) wifiMac = sb.toString() } catch (e: Exception) { Timber.e(e, "通过网络接口的方式获取Wifi的mac地址时出现异常") } return wifiMac } /** 把字byte转换为十六进制的表现形式,如ff */ fun byteToHex(byte: Byte): String { // 2表示总长度为两位,0表示长度不够时用0补齐,x表示以十六进制表示,与上0xFF是预防byte转int前面多补的1清空为0,只保留1个低字节 return String.format("%02x", byte.toInt() and 0xFF) }
在种方式在Android10上是有问题的(Android8.0和9.0没有实验过),Android10在连接wifi时默认使用一个随机分配的mac地址,在一台华为的手机上测试连接不同的wifi或者断开再连同一个wifi,发现连接wifi后获取的mac地址都一样啊,但是它不是真实的mac地址,在没有连接wifi时获取的mac地址才是真实的mac地址,所以有一种做法是可以用代码控制关闭wifi后获取mac地址,获取到之后持久化保存,然后再代码控制打开wifi,没实验过,不知道是否可行,如果真的可行,感觉这样用户体验可能也不太好,不过如果能实现也不错,持久化之后就读取保存的mac地址即可,只需要第一次时关一下wifi,不需要每次都关wifi。随机分配MAC地址官方连接在此,原文引用如下:
从 Android 8.0 开始,Android 设备在未连接到网络的情况下探测新网络时,会使用随机分配的 MAC 地址。在 Android 9 中,您可以启用一个开发者选项(默认处于停用状态),使设备在连接到 WLAN 网络时使用随机分配的 MAC 地址。
在 Android 10 中,默认为客户端模式、SoftAp 和 WLAN 直连启用随机分配 MAC 地址功能。
随机分配 MAC 地址可防止监听器根据 MAC 地址生成设备活动的历史记录,从而加强对用户隐私的保护。
此外,在 WLAN 感知和 WLAN RTT 操作中也会使用随机分配的 MAC 地址。
在Android10中关闭随机分配WIFI MAC地址,在华为手机上的截图如下:
点击隐私即可打开选择是否使用设备MAC,如下:
4、使用Android ID
Settings.Secure.getString(App.sContext.contentResolver, Settings.Secure.ANDROID_ID)
在华为的Android10上验证了竟然可以获取到。
网上别人说这个不可靠,原因如下:
ANDROID_ID可以作为设备标识,但需要注意:
1、厂商定制系统的Bug:不同的设备可能会产生相同的ANDROID_ID:9774d56d682e549c。
2、厂商定制系统的Bug:有些设备返回的值为null。
3、设备差异:对于CDMA设备,ANDROID_ID和TelephonyManager.getDeviceId() 返回相同的值。
关于Settings.Secure.ANDROID_ID的文档连接在此。
原文引用如下:
在Android 8.0(API级别26)和更高版本的平台上,一个64位数字(表示为十六进制字符串)对于应用程序签名密钥,用户和设备的每种组合都是唯一的。的值ANDROID_ID受签名密钥和用户的限制。如果在设备上执行了出厂重置或APK签名密钥更改,则该值可能会更改。有关平台如何ANDROID_ID 在Android 8.0(API级别26)及更高版本中进行处理的更多信息,请参阅 Android 8.0行为更改。
注意:对于在将设备更新到Android 8.0(API级别26)或更高版本之前安装的应用程序,ANDROID_ID如果应用程序已卸载然后在OTA之后重新安装,则更改的值。要在Android 8.0或更高版本的OTA之后进行卸载时保留值,开发人员可以使用 键/值备份。
在低于Android 8.0(API级别26)的平台版本中,当用户首次设置设备时会随机生成一个64位数字(表示为十六进制字符串),并且在用户设备的生命周期内应保持不变。在具有 多个用户的设备上 ,每个用户都显示为完全独立的设备,因此该ANDROID_ID值对于每个用户都是唯一的。
注意:如果呼叫者是Instant App,则ID的范围仅限于Instant App,它是在首次安装Instant App时生成的,如果用户清除了Instant App,则会重置ID。
个人感觉,官方到了Android10都没禁用这个Android ID,说明还是可以用的,网上说的那些可能重复的问题可以不管,遇到了再说(哈哈),我们可以在获取不到Android ID的情况下再使用GUID。
不知道为什么在android官网标识符那一章内容里没有介绍到Android ID的使用。
原文引用如下:
Android 8.0 对平台做出了以下与隐私性有关的变更。
现在,平台改变了标识符的处理方式。
对于在 OTA 之前安装到某个版本 Android 8.0(API 级别 26)的应用,除非在 OTA 后卸载并重新安装,否则 ANDROID_ID 的值将保持不变。要在 OTA 后在卸载期间保留值,开发者可以使用密钥/值备份关联旧值和新值。
对于安装在运行 Android 8.0 的设备上的应用,ANDROID_ID 的值现在将根据应用签署密钥和用户确定作用域。应用签署密钥、用户和设备的每个组合都具有唯一的 ANDROID_ID 值。因此,在相同设备上运行但具有不同签署密钥的应用将不会再看到相同的 Android ID(即使对于同一用户来说,也是如此)。
只要签署密钥相同(并且应用未在 OTA 之前安装到某个版本的 O),ANDROID_ID 的值在软件包卸载或重新安装时就不会发生变化。
即使系统更新导致软件包签署密钥发生变化,ANDROID_ID 的值也不会变化。
要借助一个简单的标准系统实现应用获利,请使用广告 ID。广告 ID 是 Google Play 服务针对广告服务提供的唯一 ID,此 ID 可由用户重置。
官方的博客:8.0变更
原文引用如下:
Android O引入了一些改进,以帮助用户控制标识符的使用。这些改进包括:
限制使用不可重置的设备范围标识符
结合对Pixel,Pixel XL和Nexus 5x手机使用的Wi-Fi芯片组固件的更改来更新Android O Wi-Fi堆栈,以随机化探测请求中的MAC地址
更新应用程序请求帐户信息的方式,并提供更多面向用户的控制
设备标识符更改
以下是Android O的一些设备标识符更改:
Android ID
在O中,对于每个应用程序和设备上的每个用户,Android ID(Settings.Secure.ANDROID_ID或SSAID)具有不同的值。需要设备范围标识符的开发人员应改用可重置的标识符,例如广告ID,以使用户拥有更多控制权。广告ID还提供了面向用户的设置,以限制广告跟踪。
此外,在Android O中:
只要软件包名称和签名密钥相同,ANDROID_ID值在软件包卸载/重新安装时不会更改。应用程序可以依靠此值来维护重新安装的状态。
如果将应用程序安装在运行早期版本Android的设备上,则将设备更新到Android O时,Android ID保持不变,除非已卸载并重新安装了该应用程序。
仅当设备恢复出厂设置或签名密钥在卸载和重新安装事件之间旋转时,Android ID值才会更改。
只有使用Google Play服务和广告ID发货的设备制造商才需要进行此更改。其他设备制造商可能会提供替代的可重置ID或继续提供ANDROID ID。
构建串行
为了与访问IMEI所需的运行时权限一致,对于以Android O或更高版本为目标的应用程序,不建议使用android.os.Build.SERIAL。相反,他们可以使用新的Android O API Build.getSerial(),只要调用方拥有PHONE权限,该API 就会返回实际的序列号。在未来的Android版本中,定位到Android O的应用程序将Build.SERIAL视为“未知”。为避免破坏旧版应用程序的功能,与以前的版本相同,定位到Android早期版本的应用程序将继续查看设备的序列号。
网络主机名
Net.Hostname提供设备的网络主机名。在早期版本的Android中,网络主机名的默认值和DHCP主机名选项的值包含Settings.Secure.ANDROID_ID。在Android O中,net.hostname为空,并且DHCP客户端不再按照IETF RFC 7844 (匿名配置文件)发送主机名。
Widevine ID
对于带有O的新设备,Widevine客户端ID为每个应用程序包名称和网站来源(对于网络浏览器应用程序)返回不同的值。
独特的系统和设置属性
除了Build.SERIAL,还有其他设置和系统属性在Android O中不可用。这些属性包括:
ro.runtime.firstboot:上次擦除或最近一次引导后首次引导的毫秒级精确时间戳
htc.camera.sensor.front_SN:摄像机序列号(某些HTC设备上可用)
persist.service.bdroid.bdaddr:蓝牙MAC地址属性
Settings.Secure.bluetooth_address:设备蓝牙MAC地址。在O中,此功能仅对拥有LOCAL_MAC_ADDRESS权限的应用可用。
Wi-Fi探测请求中的MAC地址随机化
我们与安全研究人员1合作,针对Google Pixel和Nexus 5X设备中的芯片组固件产生的Wi-Fi扫描流量设计了可靠的MAC地址随机化。然后,Android Connectivity团队与制造商合作,更新了这些设备使用的Wi-Fi芯片组固件。
Android O将这些固件更改集成到Android Wi-Fi堆栈中,以便使用具有更新的固件并运行Android O或更高版本的这些芯片组的设备可以利用它们。
以下是在运行O +时对Pixel,Pixel XL和Nexus 5x固件所做的一些更改:
对于每次与接入点断开连接的Wi-Fi扫描,手机都会使用新的随机MAC地址(设备是否处于待机状态)。
每次扫描的初始数据包序列号也被随机化。
不必要的探测请求信息元素已被删除:信息元素仅限于SSID和DS参数集。
5、网上的一些文章
https://blog.csdn.net/ckq5254/article/details/102555930
本文地址:https://blog.csdn.net/android_cai_niao/article/details/107790557