项目场景:
这两天项目升级Android 编译版本,将build.gradle
中的 compileSdkVersion
升级到 29后,发现APP在Android 10 及Android 11设备上在调用获取设备信息的时候崩溃了。
问题描述:
Android 编译版本升级为29后,在Android 10 和Android 11 手机上获取设备信息崩溃,报错
java.lang.SecurityException: getImeiForSlot: The user 10282 does not meet the requirements to access device identifiers.
Caused by: java.lang.SecurityException: getImeiForSlot: The user 10282 does not meet the requirements to access device identifiers.
at android.os.Parcel.createExceptionOrNull(Parcel.java:2376)
at android.os.Parcel.createException(Parcel.java:2360)
at android.os.Parcel.readException(Parcel.java:2343)
at android.os.Parcel.readException(Parcel.java:2285)
at com.android.internal.telephony.ITelephony$Stub$Proxy.getImeiForSlot(ITelephony.java:11511)
at android.telephony.TelephonyManager.getImei(TelephonyManager.java:2060)
at android.telephony.TelephonyManager.getImei(TelephonyManager.java:2015)
at com.bthvi.myapplication.PhoneStateHelper.getDeviceId(PhoneStateHelper.kt:82)
原因分析:
首先,贴下我的代码:
val telephonyManager = context.getSystemService(Activity.TELEPHONY_SERVICE) as TelephonyManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
telephonyManager.imei
} else {
telephonyManager.deviceId
}
这里崩溃是发生在获取 IMEI ,其实获取 deviceId 也是会崩溃的。
通过查看 Google Android开发者官方文档《唯一标识符最佳做法》发现
自 Android 10(API 级别 29)起,您的应用必须是设备或个人资料所有者应用,具有特殊运营商许可,或具有 READ_PRIVILEGED_PHONE_STATE 特权,才能访问不可重置的设备标识符。
解决方案:
1、降低targetSdkVersion
版本
我们可以将支持版本降低到 29 一下,也就是 targetSdkVersion=28
这样就可以解决问题了。但是这种做法不建议,毕竟后面还是要升级到新版本的。
2、使用官方推荐方法
也就是我们前面在文档中看到的,使用SSAID,实例ID、广告ID,随机生成的ID等。具体可以看下文档,这里贴出我的改造方案。
if (context.applicationInfo.targetSdkVersion >= 29 && Build.VERSION.SDK_INT >= 29 ){
//大于等于29使用特殊方法
getUniqueID(context);
}
private fun getUniqueID(context: Context): String? {
var id: String? = null
val androidId = Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID)
if (!Tool.isEmpty(androidId) && "9774d56d682e549c" != androidId) {
try {
val uuid = UUID.nameUUIDFromBytes(androidId.toByteArray(charset("utf8")))
id = uuid.toString()
} catch (e: Exception) {
e.printStackTrace()
}
}
if (Tool.isEmpty(id)) {
id = getUUID()
}
return if (Tool.isEmpty(id)) UUID.randomUUID().toString() else id
}
private fun getUUID(): String? {
var serial: String? = null
val m_szDevIDShort = "35" + Build.BOARD.length % 10 + Build.BRAND.length % 10 + (if (null != Build.CPU_ABI) Build.CPU_ABI.length else 0) % 10 + Build.DEVICE.length % 10 + Build.DISPLAY.length % 10 + Build.HOST.length % 10 + Build.ID.length % 10 + Build.MANUFACTURER.length % 10 + Build.MODEL.length % 10 + Build.PRODUCT.length % 10 + Build.TAGS.length % 10 + Build.TYPE.length % 10 + Build.USER.length % 10 //13 位
if (Build.VERSION.SDK_INT <= 29) {
try {
serial = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Build.getSerial()
} else {
Build.SERIAL
}
//API>=9 使用serial号
return UUID(m_szDevIDShort.hashCode().toLong(), serial.hashCode().toLong()).toString()
} catch (exception: java.lang.Exception) {
serial = "serial" // 随便一个初始化
}
} else {
serial = Build.UNKNOWN // 随便一个初始化
}
//使用硬件信息拼凑出来的15位号码
return UUID(m_szDevIDShort.hashCode().toLong(), serial.hashCode().toLong()).toString()
}