Android中READ_PHONE_STATE权限配置及设备序列号获取问题求助
问题背景
需要在Android应用中收集设备序列号用于数据签名,尝试用getprop ro.serialno获取,但遇到以下问题:
- 仅在
AndroidManifest.xml添加READ_PHONE_STATE权限无效 - 复用原有权限请求代码后应用启动崩溃
- 读取序列号时出现权限拒绝错误
崩溃报错信息
--------- beginning of crash
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.strujomeri, PID: 17563
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.strujomeri/com.strujomeri.MainActivity}: java.lang.ArrayIndexOutOfBoundsException: length=0; index=0
AndroidManifest.xml权限配置
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" />
权限请求代码
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_PHONE_STATE}, STATE_REQUEST);
权限处理代码
if (requestCode == STATE_REQUEST) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, "State request granted", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "State request granted", Toast.LENGTH_SHORT).show(); } }
补充错误信息
E/libc: Access denied finding property "ro.serialno"
问题分析与解决
1. 应用启动崩溃原因(ArrayIndexOutOfBoundsException)
崩溃是因为权限处理代码直接访问grantResults[0],但用户可能直接拒绝权限请求,或系统未返回权限结果,导致grantResults数组长度为0。
修复代码:先判断数组有效性再访问索引
if (requestCode == STATE_REQUEST) { if (grantResults != null && grantResults.length > 0) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, "权限已授予", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "权限被拒绝", Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(this, "权限请求未完成", Toast.LENGTH_SHORT).show(); } }
2. READ_PHONE_STATE权限受阻原因
从Android 10(API 29)开始,READ_PHONE_STATE权限不再允许获取设备序列号,ro.serialno系统属性被限制访问,普通应用即使申请权限也无法读取该属性,会返回权限拒绝错误。Android 11(API 30)及以上权限机制更严格,部分厂商还会进一步限制该权限。
3. 替代获取设备唯一标识的方法
如果用于数据签名,推荐以下合规方案:
方案一:使用Android ID
设备首次启动生成的64位字符串,恢复出厂设置才会改变,无需特殊权限:
String androidId = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
方案二:生成应用内唯一标识
若无需跨应用的设备标识,可在应用首次启动时生成UUID并存储:
SharedPreferences sp = getSharedPreferences("device_info", MODE_PRIVATE); String deviceId = sp.getString("unique_id", null); if (deviceId == null) { deviceId = UUID.randomUUID().toString(); sp.edit().putString("unique_id", deviceId).apply(); }
方案三:使用Advertising ID
适合广告/统计场景,依赖Google Play Services,用户可在系统设置重置:
// 需添加依赖:implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1' AdvertisingIdClient.Info adInfo = AdvertisingIdClient.getAdvertisingIdInfo(getApplicationContext()); String adId = adInfo.getId(); boolean isLAT = adInfo.isLimitAdTrackingEnabled();
4. 权限请求的正确流程
若需使用READ_PHONE_STATE获取其他设备信息,需先检查权限状态:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_PHONE_STATE}, STATE_REQUEST); } else { // 权限已授予,执行对应操作 }
内容的提问来源于stack exchange,提问作者GeorgeRaspberry

