Root设备Shell截图后无法访问图片:权限拒绝及Screen overlay问题
嘿,我来帮你拆解这个棘手的问题——你碰到的其实是Root环境下的权限双重坑:一个是悬浮窗叠加导致权限申请回调失效,另一个是Shell生成文件的属主权限问题。咱们一步步来解决:
解决Root设备截图文件读取权限+权限申请回调异常问题
一、先搞定「Screen overlay detected」导致的权限申请失效
这个提示就是权限回调异常的罪魁祸首!Android 6.0+系统规定:如果当前APP或其他应用开启了悬浮窗权限(SYSTEM_ALERT_WINDOW),普通的权限申请弹窗会被系统拦截,哪怕你点击了「允许」,onRequestPermissionsResult也会返回未授予状态。
解决方法分两种情况:
- 如果你的APP不需要悬浮窗功能:直接引导用户手动关闭权限——打开系统设置 → 应用管理 → 找到你的APP → 权限管理 → 关闭「悬浮窗」/「显示在其他应用上层」权限,再重新申请存储权限。
- 如果你的APP确实需要悬浮窗:先申请悬浮窗权限,再去请求存储权限,代码示例:
务必等悬浮窗权限申请完成后,再处理存储权限,否则回调异常的问题还会存在。private static final int OVERLAY_PERMISSION_REQUEST = 1002; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!Settings.canDrawOverlays(this)) { Intent overlayIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); startActivityForResult(overlayIntent, OVERLAY_PERMISSION_REQUEST); } else { // 悬浮窗权限已开启,再触发存储权限申请 requestStoragePermissions(); } }
二、解决Shell生成截图的「EACCES」读取权限问题
用Root Shell执行截图命令生成的文件,默认属主是root或shell用户,而你的APP运行在普通应用UID(比如u0_a123)下——就算你拿到了存储权限,也没有权限读取其他UID所属的文件,这就是BitmapFactory.decodeFile报错的核心原因。
这里提供两种可靠的解决方式:
方法1:截图后直接修改文件权限
在执行截图命令的同时,追加修改权限的Shell命令,让所有用户都能读写该文件:
# 假设截图保存路径是/sdcard/screenshot.png screencap -p /sdcard/screenshot.png && chmod 666 /sdcard/screenshot.png
chmod 666会把文件权限设置为所有用户可读可写,这样APP就能正常通过BitmapFactory.decodeFile读取了。
方法2:用Root权限直接读取文件内容
如果不想修改文件权限,可以通过Root Shell读取文件字节流,再转成Bitmap,绕过普通文件权限限制:
public Bitmap getRootScreenshot(String filePath) { try { // 用Root执行cat命令读取文件内容 Process process = Runtime.getRuntime().exec("su -c cat " + filePath); InputStream inputStream = process.getInputStream(); Bitmap bitmap = BitmapFactory.decodeStream(inputStream); inputStream.close(); process.waitFor(); return bitmap; } catch (Exception e) { e.printStackTrace(); return null; } }
这种方法不需要依赖存储权限,直接借助Root权限读取内容,适合Root环境下的场景。
三、最后验证动态权限申请的正确性
确保你的权限申请代码没有逻辑漏洞,这里给个标准示例:
private static final int REQUEST_STORAGE = 1001; private void requestStoragePermissions() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { boolean hasRead = checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; boolean hasWrite = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; if (!hasRead || !hasWrite) { requestPermissions( new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_STORAGE ); } else { // 权限已授予,直接加载截图 loadScreenshot(); } } else { // 低于6.0版本无需动态申请,直接加载 loadScreenshot(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_STORAGE) { boolean allGranted = true; for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { allGranted = false; break; } } if (allGranted) { loadScreenshot(); } else { Toast.makeText(this, "存储权限未授予,无法读取截图", Toast.LENGTH_SHORT).show(); } } }
内容的提问来源于stack exchange,提问作者papaBart




