29、权限与设备:READ_PHONE_STATE、设备标识符、OAID替代方案

说到设备标识符,这可能是Android权限里最让人头疼的一块了。我个人习惯把这块内容叫做「设备指纹的江湖」,因为这里面的水真的很深。

你想想看,从最早的IMEI随便读,到后来Google不断收紧政策,再到国内厂商搞出OAID这种替代方案。说白了,就是隐私保护和业务需求之间的博弈。

READ_PHONE_STATE 权限的前世今生

这个权限,老Android开发者肯定不陌生。以前我们获取IMEI,直接一行代码搞定:

TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
String imei = tm.getDeviceId();

嗯,这里要注意。从Android 10开始,非系统应用已经拿不到IMEI了。就算你声明了READ_PHONE_STATE权限,返回的也是null或者错误值。

我在项目中遇到过一件事:有个老项目迁移到targetSdk 29,结果用户反馈登录设备数统计全乱了。查了半天,原来是IMEI获取不到,设备指纹回退逻辑没写好。

⚠️ 避坑指南
我曾经踩过一个坑:在Android 11上,即使有READ_PHONE_STATE权限,getImei()方法也会直接抛SecurityException。所以千万别假设「有权限就能拿到」。

设备标识符的演进路线

Google其实给了我们一套清晰的演进路线。我整理了一下,大概是这样的:

Android版本 可用的设备标识 限制情况
Android 9及以下 IMEI、MEID、IMSI 基本无限制,有权限即可
Android 10 IMEI(受限) 非系统应用无法获取
Android 11+ 几乎不可用 getDeviceId() 返回 null
所有版本 Android ID (SSAID) 设备恢复出厂会重置

说白了,Google的态度很明确:别再用硬件标识符来追踪用户了。那业务方要统计设备数怎么办?这就是OAID登场的原因。

OAID:国内的替代方案

OAID全称是「匿名设备标识符」,由移动安全联盟(MSA)主导。国内主流手机厂商都支持了。

它的工作流程是这样的:

// 获取OAID的典型代码
// 需要集成MSA SDK
public String getOAID(Context context) {
    try {
        // 通过MSA接口获取
        String oaid = MsaClient.getOAID(context);
        return oaid;
    } catch (Exception e) {
        // 回退方案
        return getAndroidId(context);
    }
}
💡 个人经验
我建议你在项目中做一个设备标识符的「分级获取策略」。第一优先用OAID,拿不到就用Android ID,再不行就用UUID+本地存储。这样兼容性最好。

OAID vs IMEI 的核心区别

我画了一张图,帮你理解这两者的本质区别:

IMEI vs OAID 核心区别 IMEI(硬件标识) 🔒 永久不变 🔒 可追踪用户行为 🔒 跨应用共享 🔒 无法重置 🔒 需敏感权限 ❌ 已被Google封禁 OAID(匿名标识) ✅ 用户可重置 ✅ 不可跨应用 ✅ 无敏感权限 ✅ 符合隐私规范 ✅ 国内厂商支持 🌟 推荐替代方案 从硬件标识到匿名标识,隐私保护是核心驱动力

看到区别了吧?IMEI是「死」的,一旦泄露就永远跟着用户。OAID是「活」的,用户可以随时重置。这其实就是隐私保护的核心思想——把控制权还给用户。

实战中的设备标识方案

我在实际项目中,一般会这样设计设备标识的获取逻辑:

  1. 优先使用OAID:集成MSA SDK,获取匿名设备标识符
  2. 回退到Android ID:Settings.Secure.ANDROID_ID,注意恢复出厂会变
  3. 最后兜底:生成UUID并存入SharedPreferences
💡 小技巧
如果你需要跨进程共享设备标识,可以考虑用ContentProvider来管理。这样所有模块都通过同一个接口获取,方便统一管理和切换方案。

关于READ_PHONE_STATE的现状

说实话,现在这个权限的用途已经非常有限了。除了极少数场景(比如通话相关的App),大部分应用其实不需要它。

我建议你这样做:

  • 如果只是要设备标识,直接用OAID方案
  • 如果确实需要读取通话状态,做好权限申请的理由说明
  • 千万别在Manifest里声明了权限但实际不用,审核会被拒的
⚠️ 重要提醒
从Android 14开始,Google进一步收紧了READ_PHONE_STATE权限。即使你声明了权限,如果应用不是默认的电话应用,系统也会拒绝授权。所以,尽早迁移到OAID方案才是正道。

嗯,关于设备标识符这块,核心思路就是:放弃IMEI,拥抱OAID。这不仅是技术选择,更是合规要求。我在做App合规审查时,第一条就是看有没有非法获取设备标识符的行为。

说白了,用户隐私保护是大趋势。我们做技术的,既要满足业务需求,也要守住合规底线。OAID方案虽然要多集成一个SDK,但换来的是长期的稳定和合规,这笔账怎么算都划算。


公众号:蓝海资料掘金营,微信deep3321