Android 11用File API访问自身外部私有存储报avc denied问题咨询
问题描述
升级至Android 11后,无法访问应用位于/sdcard/Android/data/<packageId>/files下的文件,触发如下SELinux权限拦截:
type=1400 audit(0.0:672): avc: denied { read } for name="sdcard" dev="tmpfs" ino=6474 scontext=u:r:untrusted_app_29:s0:c244,c256,c512,c768 tcontext=u:object_r:mnt_sdcard_file:s0 tclass=lnk_file permissive=0 app=com.example.myapp
即使在Manifest文件中申请并完成MANAGE_EXTERNAL_STORAGE权限授权,问题依然存在。
已知Android 11 会对/sdcard/Android/data类路径做访问限制,即使拥有全文件访问权限也不例外,但本次需要访问的是应用自身的数据目录,且希望在不使用SAF的前提下,通过File等传统API实现访问,寻求可落地的实现方案。
解决方案
拦截根源
日志中的SELinux报错并非针对应用私有目录本身的权限不足,而是拦截了代码对/sdcard根符号链接的直接读取操作:
/sdcard本身是tmpfs上的软链接,Android 11+的SELinux规则默认禁止普通应用直接读取这个链接本身,该限制和应用拥有的存储权限等级无关- 只要代码通过硬编码
/sdcard/开头的路径构造File对象,无论最终要访问下面的哪个子目录,在路径解析的第一步就会因为读取这个软链接被拦截,哪怕目标目录是应用自己的私有目录也不例外 - 应用对自身外部私有存储目录拥有系统级默认授予的完全读写权限,不需要申请任何存储权限,也不受Android 11存储分区模型的访问限制
具体实现步骤
- 移除代码中所有硬编码
/sdcard/Android/data/<packageId>/files的路径拼接逻辑 - 直接调用系统上下文提供的API获取目标目录的
File对象:// 获取应用外部私有存储的files根目录,与原硬编码路径指向完全一致 val targetDir = context.getExternalFilesDir(null)// Java 实现 File targetDir = context.getExternalFilesDir(null); - 拿到该
File对象后,可以直接用传统FileAPI完成所有读写操作,不需要申请任何存储权限,也不会触发原SELinux拦截 - 该API返回的是系统完成权限映射后的真实存储路径,路径解析过程不会经过
/sdcard根软链接的访问环节,可直接绕过原有拦截规则
常见注意事项
- 不需要为了访问自身私有目录申请
MANAGE_EXTERNAL_STORAGE权限:该权限设计面向需要全文件管理能力的应用类型,普通应用为访问私有目录申请该权限属于权限滥用,不仅对解决当前问题没有帮助,还会影响应用上架审核 - 不要通过硬编码真实存储路径(比如
/storage/emulated/0/Android/data/<packageId>/files)、反射改权限等方式规避限制:这类写法在部分厂商定制系统上依然会触发SELinux拦截,兼容性远低于官方API - 如果需要访问应用外部私有目录下的分类子目录(比如音乐、图片专属目录),可以给
getExternalFilesDir()传入对应的类型参数,比如Environment.DIRECTORY_MUSIC,拿到的目录同样拥有完全读写权限
内容的提问来源于stack exchange,提问作者user149408
相关产品推荐
相关产品推荐

