分区存储
为了让用户更好地控制自己的文件并减少混乱,Android 10 针对应用推出了一种新的存储范例,称为分区存储。分区存储改变了应用在设备的外部存储设备中存储和访问文件的方式。以 Android 10(API 级别 29)及更高版本为目标平台的应用在默认情况下被授予了对外部存储空间的分区访问权限(即分区存储)。此类应用只能访问外部存储空间上的应用专属目录,以及本应用所创建的特定类型的媒体文件。
...
每个应用向自己的私有目录读写文件,不需要读写权限。私有文件目录具体路径: storage/emulated/0/android/data/packageName/ ,获取方法: Context#getExternalFilesDir()
应用即使获取了读写权限,也无法访问其他应用的私有目录。
当应用需要获取媒体文件时,通过 MediaStore API 向公共存储目录DCIM、Music或者Movie获取。同样写媒体文件也是如此。并且读写自己的文件时不需要申请权限。 只有读其他应用的媒体文件时才会需要申请READ_EXTERNAL_STORAGE权限。
(更新:Android11为目标平台时,可以使用文件直接路径去访问媒体,这是在Android10上没有的,应用的性能会略有下降,还是推荐使用MediaStore )当应用需要获取其他非媒体文件时,比如doc、pdf文件,需要使用 系统的文件选择器SAF 来进行访问。
所以WRITE_EXTERNAL_STORAGE权限,在未来的Android11版本里,会被废弃。 (写文件不需要权限,只能在私有目录和公共目录写文件)
下面是关于分区存储权限和其他相关项目的表格。
类型 | 位置 | 访问应用自己生成的文件 | 访问其他应用生成的的文件 | 访问方法 | 卸载应用是否删除文件 |
---|---|---|---|---|---|
外部存储 | 应用特定的目录 | 无需权限 | 无法直接访问 | getExternalFilesDir()获取到属于应用自己的文件路径 | 是 |
外部存储 | Downloads | 无需权限 | 无需权限 | 通过存储访问框架SAF,加载系统文件选择器 | 否 |
外部存储 | Photo/ Video/ Audio/ | 无需权限 | 需要权限READ_EXTERNAL_STORAGE | MediaStore Api | 否 |
将分区存储与 FUSE 搭配使用
Android 11 或更高版本支持用户空间中的文件系统 (FUSE),这使 MediaProvider 模块可以检查用户空间中的文件操作并根据允许、拒绝或隐去访问权限的政策限制对文件的访问。分区存储中使用 FUSE 的应用可获得分区存储的隐私功能以及通过直接文件路径访问文件的功能(让 File API 继续在应用中运行)。
...
3、不同userid对应的相同应用的uid不同,根据 2中规则,即可实现 相同应用程序(相同包名)不同用户( ${userid} )的目录的权限隔离。
文件访问的三种方式
1. MediaStore API,2. 存储访问框架(SAF),3. File API(android 11 共享文件夹,私有缓存文件夹)。
Android系统针对文件类型进行了分类,图片、音频、视频这三类文件将可以通过MediaStore API来进行访问,而其他类型的文件则需要使用系统的文件选择器来进行访问。
存储访问框架(SAF)
既可以选取多媒体文件,也可以选取其他类型文件。当你需要访问非多媒体文件时,或者访问根目录下的非共享文件夹时(可能是连接电脑后,在电脑上创建的),那么只能使用SAF。
Android外部存储空间的实现比较复杂,主要是借助了Linux文件系统的机制,运用到的关机技术有sdscard_fs、mnt namespace、bind mount。sdcard_fs接管了底层文件系统的权限控制,通过bind mount避免了为每个进程mount带来的内核文件系统节点开销,mnt namespace保证每个应用进程有自己的挂载点,并在运行时通过在vold中为进程切换新的mount namespace来实现了动态授予权限的母的。
参考文献
https://source.android.com/docs/core/storage/scoped?hl=zh-cn
...
https://www.jianshu.com/p/be8b5ff35200
关键文件路径
代码块 |
---|
packages/providers/MediaProvider/src/com/android/providers/media/util/FileUtils.java. 918 packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java 800 base/core/java/android/os/Environment.java |