获取Android设备IMEI时触发安全异常的解决方案咨询
问题描述
需要获取Android设备的IMEI号码,应用仅用于企业内部个人使用,不会上架Google Play商店。目前获取IMEI时触发安全异常,已制作设备管理应用,但仍无法访问IMEI,寻求可行解决办法。
现有代码
class MainActivity : AppCompatActivity() { val REQUEST_CODE = 101 var imei: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 初始化变量 val telephonyManager = getSystemService(TELEPHONY_SERVICE) as TelephonyManager val imeiTextView = findViewById<TextView>(R.id.idTVIMEI) // 检查权限 if (ActivityCompat.checkSelfPermission( this, Manifest.permission.READ_PHONE_STATE ) != PackageManager.PERMISSION_GRANTED ) { // 申请权限 ActivityCompat.requestPermissions( this, arrayOf(Manifest.permission.READ_PHONE_STATE), REQUEST_CODE ) } // 将IMEI设置到TextView if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { imei = telephonyManager.imei } imeiTextView.setText(imei) } override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<String?>, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == REQUEST_CODE) { // 检查权限是否授予 if (grantResults.size != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, "权限已授予。", Toast.LENGTH_SHORT).show() } else { Toast.makeText(this, "权限已拒绝。", Toast.LENGTH_SHORT).show() } } } }
可行解决办法
1. 针对Android版本调整权限策略
Android 10(API 29)及以上:
READ_PHONE_STATE权限不再允许获取IMEI,除非应用满足以下任一条件:- 成为设备所有者应用或配置文件所有者应用:需要通过Android Enterprise的官方方式(如QR码批量配置、NFC触碰绑定、AFW模式)完成设备绑定,普通设备管理应用无法自动获得该权限。绑定成功后,无需申请
READ_PHONE_STATE,直接调用TelephonyManager.getImei()即可获取。 - 拥有
READ_PRIVILEGED_PHONE_STATE权限:该权限仅开放给系统应用或OEM签名的应用,适合企业有定制ROM的场景,可联系设备厂商申请权限并签名应用。
- 成为设备所有者应用或配置文件所有者应用:需要通过Android Enterprise的官方方式(如QR码批量配置、NFC触碰绑定、AFW模式)完成设备绑定,普通设备管理应用无法自动获得该权限。绑定成功后,无需申请
Android 9(API 28)及以下:确保已在
AndroidManifest.xml中声明READ_PHONE_STATE权限,且用户完成授权。同时注意现有代码的逻辑缺陷——申请权限后未等待用户授权就直接获取IMEI,会导致首次启动时触发异常。
2. 修复现有代码的逻辑漏洞
现有代码在权限申请流程中,没有在用户授权后重新获取IMEI,导致首次启动时权限未授予的情况下imei为空甚至触发异常。修改onRequestPermissionsResult方法,补充授权后的IMEI获取逻辑:
override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<String?>, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == REQUEST_CODE) { val telephonyManager = getSystemService(TELEPHONY_SERVICE) as TelephonyManager val imeiTextView = findViewById<TextView>(R.id.idTVIMEI) if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, "权限已授予。", Toast.LENGTH_SHORT).show() // 权限授予后重新获取IMEI imei = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { telephonyManager.imei } else { // Android O以下版本使用deviceId telephonyManager.deviceId } imeiTextView.text = imei } else { Toast.makeText(this, "权限已拒绝。", Toast.LENGTH_SHORT).show() } } }
3. 正确配置设备所有者应用
如果你的设备管理应用未成功成为设备所有者,可通过以下方式完成绑定:
- 测试设备:使用ADB命令直接设置:
dpm set-device-owner com.your.package/.YourDeviceAdminReceiver(替换为你的应用包名和设备管理接收器类名)。 - 批量设备:生成包含应用下载链接和配置信息的QR码,设备首次启动时扫描即可完成绑定。
- NFC绑定:通过已配置好设备所有者的设备,触碰目标设备完成快速绑定。
4. 替代标识符方案(无法获取IMEI时)
如果以上方法均无法实现,可考虑使用其他唯一标识替代:
- Android ID:
Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID),注意设备重置或刷机后会改变。 - 设备序列号:
Build.SERIAL(Android 8.0及以上需要READ_PHONE_STATE权限,设备所有者应用可直接获取)。 - 自定义UUID:应用首次启动时生成UUID并存储在本地SharedPreferences中,适合仅需要应用内唯一标识的场景。
内容的提问来源于stack exchange,提问作者Apurva Jariwala
相关产品推荐
相关产品推荐

