原文Android Developer官方文档Android Q 隐私权变更:应用作用域和媒体作用域存储空间
重大隐私权变更
针对“共享的外部存储空间”中文件的访问权限的新限制。应用可以将文件存储在各自的沙盒中,也可以存储在媒体和下载内容的通用集合中。
从 Android Q 测试版 1 开始,此变更具有以下属性:
- 如果您访问和共享外部存储设备中的文件,则会影响您的应用
- 使用隔离的沙盒或媒体集合目录进行缓解
- 通过运行多个 ADB 命令启用行为
为了让用户更好地控制自己的文件,并限制文件混乱情况,Android Q 更改了应用访问设备外部存储空间中文件的方式。Android Q 用更精细的媒体特定权限替换了 READ_EXTERNAL_STORAGE
和 WRITE_EXTERNAL_STORAGE
权限,并且无需特定权限,应用即可访问自己在外部存储设备上的文件。这些变更会影响您的应用在外部存储设备中保存和访问文件的方式。
本指南介绍了如何更新应用,以使其可以继续共享、访问和更新保存在外部存储设备上的文件,还提供了兼容性注意事项,并说明了如何激活此行为变更。
本指南介绍了如何更新应用,以使其可以继续共享、访问和更新保存在外部存储设备上的文件,还提供了兼容性注意事项,并说明了如何激活此行为变更。
针对应用私有文件的隔离存储沙盒
Android Q 在外部存储设备中为每个应用提供了一个隔离存储沙盒
(例如 /sdcard
)。任何其他应用都无法直接访问您应用的沙盒文件。由于文件是您应用的私有文件,因此您不再需要任何权限即可在外部存储设备中访问和保存自己的文件。此变更可让您更轻松地保证用户文件的隐私性,并有助于减少应用所需的权限数量。
注意:如果用户卸载了您的应用,系统就会清理隔离存储沙盒中的文件。
在外部存储设备中存储文件的最佳位置是 Context.getExternalFilesDir()
返回的位置,因为此位置的行为方式在所有 Android 版本中都保持一致。使用此方法时,请在媒体环境中传递与您要创建或打开的文件类型对应的文件。例如,要访问或保存应用私有图片,请调用
Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)。
媒体文件的共享集合
如果您的应用创建了属于相应用户的文件,并且希望在卸载该应用时保留此用户,则将这些文件保存到某个通用媒体集合(也称为“共享集合”)中。共享集合包括:照片和视频、音乐和下载内容。
查看其他应用的文件所需的权限
您的应用无需请求任何权限即可在这些共享集合中创建和修改自己的文件。但是,如果您的应用需要创建和修改其他应用已创建的文件,则必须先请求相应的权限:
- 访问照片和视频共享集合中其他应用的文件时,需要
READ_MEDIA_IMAGES
或READ_MEDIA_VIDEO
权限(具体取决于您的应用需要访问的文件类型)。
- 访问音乐共享集合中其他应用的文件时,需要
READ_MEDIA_AUDIO
权限。
注意:未设置用于访问下载内容共享集合的权限。您的应用可以访问此集合中自己的文件。但是,要访问此集合中其他应用的文件,您必须允许用户使用系统的文件选择器应用来选择文件。注意:如果您的应用使用存储访问框架,则无需请求这些媒体作用域权限。
注意:对于 Android Q 上新安装的应用,对
getExternalStoragePublicDirectory()
的调用仅会提供对应用存储在其隔离存储沙盒中的文件的访问权限。要始终拥有对其他应用的文件的访问权限,请更新应用的逻辑以改用MediaStore
。
保留您的应用在共享集合中的文件
默认情况下,当用户卸载您的应用时,Android Q 会清理您保存到沙盒中的文件。要在卸载应用时保留这些文件,请使用存储访问框架,或将文件保存到共享集合中。
要保留共享集合中的文件,请在相关的 MediaStore
集合中新插入一行,并用以下方法填充此行对应的列:
- 至少应为
DISPLAY_NAME
和MIME_TYPE
列提供值。 - (可选)您可以使用
PRIMARY_DIRECTORY
和SECONDARY_DIRECTORY
列来影响文件在磁盘上的存储位置。 - 保留
DATA
列不定义。这样一来,平台便可以灵活地将文件保留在沙盒之外。
插入此行后,您可以使用 ContentResolver.openFileDescriptor()
之类的 API 读取新建文件的数据或向其中写入数据。
但是,如果用户稍后重新安装了您的应用,该应用将无法访问这些文件,除非它执行以下操作之一:
- 请求对集合的相应权限。
- 从存储访问框架向用户发送请求。
这种情况类似于一个应用尝试访问另一个应用的文件的情况。
照片的特别注意事项
Android Q 新增了多项增强功能,让用户可以更好地控制在外部存储设备中访问照片的方式。
访问照片中的位置信息
一些照片在其 Exif 元数据中包含位置信息,以便用户查看照片的拍摄地点。由于此位置信息很敏感,因此默认情况下 Android Q 会对该信息进行遮盖。这种对位置信息的限制与适用于相机特性的限制不同。
注意:如果您的应用是用户的默认照片管理器应用,则平台会自动让您的应用访问照片中的位置信息。
如果您的应用需要访问照片的位置信息,请完成以下步骤:
- 将新的
ACCESS_MEDIA_LOCATION
权限添加到您应用的清单中。 - 在 MediaStore 对象中,调用
setRequireOriginal()
并传入照片的 URI。
向用户显示照片库
如果您的应用是相机应用,则它无法直接访问保存在照片和视频共享集合中的照片(除非它是设备的默认照片管理器应用)。要引导用户找到图库应用,请使用 ACTION_REVIEW
intent。
使用其他应用的文件
本节介绍了您的应用如何与其他应用存储在共享集合中的文件进行互动。
访问其他应用创建的文件
要访问并读取其他应用已保存到外部存储设备中的媒体文件,请完成以下步骤:
- 根据包含您要访问的文件的共享集合请求必要的权限。
- 使用
ContentResolver
对象查找并打开该文件。
注意:ContentResolver
类包含一个新方法 loadThumbnail()
,它可为您的应用提供文件预览。最好先调用 loadThumbnail()
,以便用户可以查看媒体文件的快照,无需让您的应用自己加载所有文件。此方法还支持更灵活的请求,例如请求特定尺寸以及取消请求的功能。
向其他应用创建的文件写入数据
通过将文件保存到共享集合,您的应用会成为该文件的所有者。通常,只有当您是共享集合中某个文件的所有者时,您的应用才能向该文件写入数据。但是,如果为您的应用分配了正确的角色,您还可以向其他应用拥有的文件写入数据:
- 如果您的应用是用户的默认照片管理器应用,则可以修改其他应用保存到照片和视频共享集合中的图片文件。
- 如果您的应用是用户的默认音乐应用,则可以修改其他应用保存到音乐共享集合中的音频文件。
注意:无论您的应用是默认的照片管理器应用还是默认的音乐应用,它都应保持正常运行。
要修改其他应用最初保存到外部存储设备中的媒体文件,请完成下列步骤之一:
- 按照角色指南中的步骤查看您的应用是默认的照片管理器应用还是默认的音乐应用。
- 使用
ContentResolver
对象找到相应文件并直接进行修改。执行编辑/修改操作时,捕获RecoverableSecurityException
以便您可以请求用户授予您对该特定内容的写入权限。
访问特定文件
在某些用例中,您的应用可能需要打开或创建它无权访问的文件:
- 在照片编辑应用中,打开一张绘图。
- 在企业办公应用中,将文本文档保存到用户选择的位置。
对于这些情况,请使用存储访问框架(该框架允许用户选择要打开的特定文件,或选择特定位置来保存文件)。
配套应用文件共享
如果您要管理一套需要相互访问彼此文件的应用,请使用 content://
URI(我们已将其作为安全最佳做法推荐)。
如需了解详情,请参阅有关如何设置文件共享的文档。
升级设备上之前安装过的应用的兼容性模式
对访问外部存储设备中文件的限制仅适用于以 Android Q 为目标平台的应用,或者在运行 Android Q 的设备上新安装的应用。
当满足以下每个条件时,系统会将您应用的文件访问权限置于兼容性模式:
- 您的应用以 Android 9(API 级别 28)或更低版本为目标平台。
- 您的应用安装在从 Android 9 升级到 Android Q 的设备上。
当您的应用处于兼容性模式时,以下文件访问行为适用:
- 您的应用可以访问在
MediaStore
集合中存储的所有文件(甚至是您的应用尚未创建的文件)。 - 面向用户的存储权限可允许或禁止您的应用访问整个外部存储设备,而不是控制对单个共享集合(如照片和视频或音乐)的访问。
在首次卸载您的应用之前,此兼容性模式一直有效。
注意:如果稍后在同一设备上重新安装您的应用,则不会重新激活兼容性模式。
标识特定的外部存储设备
在 Android 9(API 级别 28)及更低版本中,所有存储设备上的所有文件都显示在单个 "external"
卷名称下。Android Q 为每个外部存储设备提供唯一的卷名称。此命名系统可帮助您高效地整理内容并将内容编入索引,还可让您控制新内容的存储位置。
注意:主外部存储设备始终使用卷名称 "external"
。
要唯一标识外部存储设备中的特定文件,您必须同时使用卷名称和 ID。例如,主存储设备上的文件是 content://media/external/images/media/12
,而名为 FA23-3E92
的辅助存储设备上的对应文件是 content://media/FA23-3E92/images/media/12
。
您可以通过将此卷名称传递到特定媒体集合(例如 MediaStore.Images.getContentUri()
)中来访问存储在特定卷中的文件。
获取外部存储设备列表
要获取所有当前可用卷的名称列表,请调用 MediaStore.getAllVolumeNames()
,如以下代码段所示:
Set volumeNames = MediaStore.getAllVolumeNames(context);
设置虚拟外部存储设备
在没有可移动外部存储设备的设备上,使用以下命令启用虚拟磁盘以进行测试:
adb shell sm set-virtual-disk true
测试行为变更
为了让您的应用与此新行为变更兼容,平台提供了多种方法来调整与此变更相关联的多个参数。
切换行为变更
要在 Android Q 测试版 1 中启用此行为变更,请在终端窗口中执行以下命令:
adb shell sm set-isolated-storage on
运行此命令后,设备将重启。如果设备未重启,请稍等片刻,然后再尝试重新运行此命令。
要确认行为变更是否已生效,请使用以下命令:
adb shell getprop sys.isolated_storage_snapshot
测试兼容性模式行为
测试您的应用时,您可以通过在终端窗口中运行以下命令来为外部文件存储访问权限启用兼容性模式:
adb shell cmd appops set your-package-name android:legacy_storage allow
要停用兼容性模式,请在 Android Q 上卸载然后重新安装您的应用,或在终端窗口中运行以下命令:
adb shell cmd appops set your-package-name android:legacy_storage default
以文件管理器的形式浏览外部存储设备
要以文件管理器的形式广泛访问外部存储设备中的目录,请使用 ACTION_OPEN_DOCUMENT_TREE
intent。有关示例,请参阅 GitHub 上的 android-DirectorySelection 示例。
注意:在 Android Q 中,StorageVolume
类中的 createAccessIntent()
方法已被弃用,因此您不应使用此方法浏览外部存储设备。如果您使用此方法浏览外部存储设备,运行 Android Q 设备的用户将无法在您的应用中查看保存在外部存储设备中的文件。
原文Android Developer官方文档Android Q 隐私权变更:应用作用域和媒体作用域存储空间