Android存储访问框架(SAF)疑问:仅授予读权限的目录为何可执行文件删除操作
Android存储访问框架(SAF)疑问:仅授予读权限的目录为何可执行文件删除操作
嘿,你遇到的这个问题确实挺反直觉的,我之前也碰到过类似的情况,来帮你捋捋这里面的门道:
首先,咱们得先搞清楚SAF持久化权限和实际操作权限校验的逻辑差异:
- 你通过
takePersistableUriPermission给源目录只申请了读权限,日志也确认了持久化权限里源目录的isWritePermission是false,这部分操作是没问题的。但DocumentsContract.deleteDocument()这个方法的权限校验,并不完全严格遵循你申请的持久化权限,而是和对应的文档提供者(Document Provider)自身的规则直接相关。 - 比如系统默认的外部存储提供者(
com.android.externalstorage.documents),对于外部存储上的文件,它的权限逻辑其实更贴近传统的文件系统规则:如果你能访问到某个文件(也就是拥有读权限),它可能会默认允许你执行删除操作——这有点像Linux里,只要你对文件所在的目录有写权限就能删文件,不过SAF这里的判断逻辑更宽松一些。
然后说说SAF权限的实际作用边界:
- 你申请的持久化读权限,核心是用来限制应用能不能通过
ContentResolver读取该目录下的文件内容、查询文件列表;但删除、修改这类操作,有些文档提供者会认为“既然你能看到这个文件,就允许你操作它”,并不会严格卡你申请的读写flag。
那如果要实现你想要的“源目录只能读、不能删”的需求,目前只能在应用层面做额外校验:
- 先把源目录的URI存下来,当用户触发删除操作时,先判断要删除的文件所属的目录是不是源目录:
- 可以用
DocumentsContract.getTreeDocumentId()获取源目录的树ID,再解析要删除文件的URI对应的父目录ID,对比两者是否一致。 - 如果匹配源目录,就直接拦截删除操作,给用户提示或者直接不执行。
- 可以用
补充个小细节:你可以试试用ContentResolver的delete方法(resolver.delete(fileUri, null, null))来删除,不过大概率结果和DocumentsContract.deleteDocument()一致,因为底层都是调用文档提供者的delete接口,逻辑是一样的。
这种情况算是SAF设计里的一个小坑,不同的第三方文档提供者(比如某些文件管理器的SAF实现)可能会有不同的权限处理,但系统默认的外部存储提供者确实存在这种“读权限允许删除”的情况。
备注:内容来源于stack exchange,提问作者Robin




