Android 11 基于 Android 早期版本构建,增加了多种功能和更新,以保障用户安全并提高透明度和可控性。所有开发者都应查看隐私功能并测试他们的应用。具体影响可能会因每个应用的核心功能、目标平台和其他因素而异。
如需详细了解 Android 11 中的主要变更,请查看以下部分。
重大隐私权变更
下表汇总了 Android 11 中与隐私权相关的主要变更。
隐私权变更 | 受影响的应用 | 缓解策略 | |
---|---|---|---|
分区存储强制执行 以 Android 11 为目标平台的应用始终会受分区存储行为的影响 | 以 Android 11 为目标平台的应用,以及以 Android 10 为目标平台且未将 requestLegacyExternalStorage 设为 true 以停用分区存储的应用 | 更新您的应用以使用分区存储 详细了解分区存储变更 | |
一次性权限 通过一次性权限,用户可以向位置、麦克风和摄像头授予临时访问权限 | 以任何版本为目标平台且请求位置信息、麦克风或摄像头权限的应用 | 在尝试访问受某项权限保护的数据之前,检查您的应用是否具有该权限 遵循请求权限方面的最佳做法 | |
自动重设权限 如果用户在 Android 11 上几个月未与应用互动,系统会自动重设应用的敏感权限 | 以 Android 11 为目标平台且在后台执行大部分工作的应用 | 要求用户阻止系统重置应用的权限 详细了解自动重设权限 | |
后台位置信息访问权限 Android 11 更改了用户向应用授予后台位置信息权限的方式 | 以 Android 11 为目标平台且需要访问后台位置信息的应用 | 通过对权限请求方法的多次单独调用,逐步请求前台(粗略或精确)和后台位置权限。必要时,说明用户授予该权限所能得到的益处 详细了解 Android 11 中的后台位置信息访问权限 | |
软件包可见性 Android 11 更改了应用查询同一设备上的其他已安装应用及与之互动的方式 | 以 Android 11 为目标平台且与设备上的其他已安装应用交互的应用 | 将 <queries> 元素添加到应用的清单详细了解软件包可见性 | |
前台服务类型 Android 11 更改了前台服务访问摄像头和麦克风数据的方式 | 以 Android 11 为目标平台且在前台服务中访问摄像头或麦克风的应用 | 分别在访问摄像头数据和麦克风数据的服务中声明 camera 和 microphone 前台服务类型详细了解新的前台服务类型 |
一. Android 11 中的存储机制更新
Android 11 进一步增强了平台功能,为外部存储设备上的应用和用户数据提供了更好的保护。预览版引入了多项去年在 Android 开发者峰会上宣布的增强功能,例如可主动选择启用的媒体原始文件路径访问机制、面向媒体的批量修改操作,以及存储访问框架的界面更新。
为方便过渡到使用分区存储,该平台为开发者引入了进一步的改进。如需详细了解如何将应用迁移到根据应用的用例使用分区存储,请参阅本页面的分区存储部分,以及 Android 存储用例和最佳做法指南。
-
分区存储强制执行
-
管理设备存储空间
-
外部存储设备上的应用专用目录
-
媒体文件访问权限
-
访问其他应用的私有目录
-
文档访问限制
-
权限
-
所有文件访问权限
二. Android 11 中的权限更新
在 Android 11 中,用户能够针对位置信息、麦克风和摄像头指定更精细的权限。此外,系统会重置以 Android 11 为目标平台的未使用应用的权限,如果应用读取与电话号码相关的信息,则可能需要更新其声明的权限。
-
单次授权
-
自动重置未使用的应用的权限
-
权限对话框的可见性
-
电话号码
三. Android 11 中的位置信息更新
为了进一步保护用户隐私,Android 11 增加了单次位置信息访问权限,并更改了用户授予后台位置信息访问权限的方式。这些更新会影响到 Android 11 上运行的所有应用。
1.单次访问权限
在 Android 11 中,每当应用请求访问前台位置信息时,系统权限对话框都包含一个名为仅限这一次的选项。(ps:这对于需要持续使用用户位置的应用可能有影响,需要引导用户一直允许,这其中的文案需要好好打磨打磨)
2.后台位置信息访问权限
Android 11 更改了应用中的功能获取后台位置信息访问权限的方式。本部分介绍了上述各项变更。
注意:如果应用中的某项功能从后台访问位置信息,请验证此类访问是否有必要,并考虑以其他方式获取该功能所需的信息。如需详细了解后台位置信息访问权限,请参阅在后台访问位置信息页面。
2.1单独请求在后台访问位置信息
正如有关如何在运行时请求位置信息访问权限的指南中所述,您应该执行递增位置信息请求。如果应用以 Android 11 为目标平台,系统会强制执行此最佳做法。如果您同时请求前台位置信息和后台位置信息,系统会抛出异常。
2.2权限对话框的变更
在搭载 Android 11 的设备上,当应用中的某项功能请求在后台访问位置信息时,用户看到的系统对话框不再包含用于启用后台位置信息访问权限的按钮。如需启用后台位置信息访问权限,用户必须在设置页面上针对应用的位置权限设置一律允许选项,如图 2 所示。
在针对后台位置信息请求运行时权限时,您可以遵循最佳做法,帮助用户导航到此设置页面。授予权限的过程取决于应用的目标 SDK 版本。
以 Android 11 为目标平台的应用
如果 shouldShowPermissionRationale()
返回 true
,请向用户显示包含以下内容的指导界面:
- 明确说明应用功能需要在后台访问位置信息的原因。
- 用于授予后台位置权限的设置选项(例如,图 2 中的一律允许)的用户可见标签。您可以调用
getBackgroundPermissionOptionLabel()
获取此标签。此方法的返回值会根据用户设备的语言偏好设置进行本地化。 - 供用户拒绝授予权限的选项。如果用户拒绝应用在后台访问位置信息,他们应该能够继续使用应用。
以 Android 10 或更低版本为目标平台的应用
当应用中的某项功能请求后台位置信息访问权限时,用户会看到一个系统对话框。此对话框包含一个选项,可用于导航到设置页面上的应用位置权限选项。
如果应用已遵循有关请求位置权限的最佳做法,您无需对应用进行任何更改即可让此新行为生效。
四. Android 11 中的软件包可见性
Android 11 更改了应用查询用户已在设备上安装的其他应用以及与之交互的方式。使用新的 <queries>
元素,应用可以定义一组自身可访问的其他应用。通过告知系统应向您的应用显示哪些其他应用,此元素有助于鼓励最小权限原则。此外,此元素还可帮助 Google Play 等应用商店评估应用为用户提供的隐私权和安全性。
如果应用以 Android 11 为目标平台,您可能需要在应用的清单文件中添加 <queries>
元素。在 <queries>
元素中,您可以按软件包名称或按 intent 签名指定应用。
注意:在某些情况下,即使您的应用以 Android 11 为目标平台,您也根本不需要更新自己的应用来支持这一变更。
返回其他应用相关结果的 PackageManager
方法(如 queryIntentActivities()
)会根据发起调用的应用的 <queries>
声明进行过滤。与其他应用的显式交互(如 startService()
)还要求目标应用与 <queries>
中的某项声明相符。
-
设置您的环境
-
查询特定软件包及与之交互
-
在给定 intent 过滤器的情况下查询应用及与之交互
-
查询所有应用及与之交互
-
软件包过滤的日志消息
-
测试变更
-
不受变更影响的用例
-
为 Activity 开始时间添加限制
五. 数据访问审核
为了让应用及其依赖项访问用户私密数据的过程更加透明,Android 11 引入了数据访问审核功能。借助此流程得出的见解,您可以更好地识别和纠正可能出现的意外数据访问。您的应用可以注册 AppOpsManager.OnOpNotedCallback
实例,该实例可在每次发生以下任一事件时执行相应操作:
- 应用的代码访问私密数据。为了帮助您确定应用的哪个逻辑部分调用了事件,您可以按归因标记审核数据访问。
- 依赖库或 SDK 中的代码访问私密数据。
数据访问审核是在发生数据请求的线程上调用的。这意味着,如果应用中的第三方 SDK 或库调用访问私密数据的 API,您的 OnOpNotedCallback
可以调用数据访问审核检查有关该调用的信息。通常,此回调对象可以通过查看应用的当前状态(例如当前线程的堆栈轨迹)以判断调用是来自您的应用还是来自 SDK。
1.记录数据访问
注意:如果您的应用在多个组件(例如在前台服务和后台任务)中访问数据,请创建自定义子类 Application
,并在子类的 onCreate()
方法中定义 AppOpsManager.OnOpNotedCallback
。
以下代码段定义了在单个 Activity 中用于审核数据访问的 AppOpsManager.OnOpNotedCallback
@Override
public void onCreate(@Nullable Bundle savedInstanceState,
@Nullable PersistableBundle persistentState) {
AppOpsManager.OnOpNotedCallback appOpsCallback =
new AppOpsManager.OnOpNotedCallback() {
private void logPrivateDataAccess(String opCode, String trace) {
Log.i(MY_APP_TAG, "Private data accessed. " +
"Operation: $opCode\nStack Trace:\n$trace");
}
@Override
public void onNoted(@NonNull SyncNotedAppOp syncNotedAppOp) {
logPrivateDataAccess(syncNotedAppOp.getOp(),
Arrays.toString(new Throwable().getStackTrace()));
}
@Override
public void onSelfNoted(@NonNull SyncNotedAppOp syncNotedAppOp) {
logPrivateDataAccess(syncNotedAppOp.getOp(),
Arrays.toString(new Throwable().getStackTrace()));
}
@Override
public void onAsyncNoted(@NonNull AsyncNotedAppOp asyncNotedAppOp) {
logPrivateDataAccess(asyncNotedAppOp.getOp(),
asyncNotedAppOp.getMessage());
}
};
AppOpsManager appOpsManager = getSystemService(AppOpsManager.class);
if (appOpsManager != null) {
appOpsManager.setOnOpNotedCallback(getMainExecutor(), appOpsCallback);
}
}
在特定情况下,需要调用 onAsyncNoted()
和 onSelfNoted()
方法:
-
如果数据访问并非发生在应用调用 API 期间,需要调用
onAsyncNoted()
。最常见的一个例子就是,在您的应用注册了监听器后,每次调用该监听器的回调时都会发生数据访问。传递到
onAsyncNoted()
中的AsyncNotedOp
参数包含名为getMessage()
的方法。此方法提供了有关数据访问的详细信息。如果是位置信息回调,该消息将包含监听器的系统身份哈希值。 -
在极少数情况下,如果应用将自身的 UID 传递到
noteOp()
,需要调用onSelfNoted()
。
2.按归因标记审核数据访问
您的应用可能有几种主要用例,例如允许用户拍照并与联系人分享这些照片。如果您开发的是一款多用途应用,可以在审核应用的数据访问时对应用的各部分使用归因标记。在传递到 onNoted()
调用的对象中会返回 attributionTag
上下文。这可以帮助您更轻松地将数据访问回溯至代码的逻辑部分。
如需在应用中定义归因标记,请完成以下部分中的步骤
2.1 创建归因标记
如果您在某个 Activity 中访问数据(例如请求位置信息或访问用户的联系人列表),请在该 Activity 的 onCreate()
方法中调用 createAttributionContext()
,并传入您希望与应用的一部分相关联的归因标记。
以下代码段展示了如何为应用的“照片位置信息分享”部分创建归因标记:
public class SharePhotoLocationActivity extends AppCompatActivity {
private Context attributionContext;
@Override
public void onCreate(@Nullable Bundle savedInstanceState,
@Nullable PersistableBundle persistentState) {
attributionContext = createAttributionContext("sharePhotos");
}
public void getLocation() {
LocationManager locationManager =
attributionContext.getSystemService(LocationManager.class);
if (locationManager != null) {
// Use "locationManager" to access device location information.
}
}
}
2.2 在访问日志中包含归因标记
更新您的 AppOpsManager.OnOpNotedCallback
回调,以将您定义的归因标记的名称包含于应用的日志中。
注意:如果归因标记的返回值为 null
,意味着当前的 Context
对象与您应用的主要部分相关联。
以下代码段展示了记录归因代码的更新逻辑:
@Override
public void onCreate(@Nullable Bundle savedInstanceState,
@Nullable PersistableBundle persistentState) {
AppOpsManager.OnOpNotedCallback appOpsCallback =
new AppOpsManager.OnOpNotedCallback() {
private void logPrivateDataAccess(String opCode,
String attributionTag, String trace) {
Log.i("MY_APP_TAG", "Private data accessed. " +
"Operation: $opCode\n " +
"Attribution Tag:$attributionTag\nStack Trace:\n$trace");
}
@Override
public void onNoted(@NonNull SyncNotedAppOp syncNotedAppOp) {
logPrivateDataAccess(syncNotedAppOp.getOp(),
syncNotedAppOp.getAttributionTag(),
Arrays.toString(new Throwable().getStackTrace()));
}
@Override
public void onSelfNoted(@NonNull SyncNotedAppOp syncNotedAppOp) {
logPrivateDataAccess(syncNotedAppOp.getOp(),
syncNotedAppOp.getAttributionTag(),
Arrays.toString(new Throwable().getStackTrace()));
}
@Override
public void onAsyncNoted(@NonNull AsyncNotedAppOp asyncNotedAppOp) {
logPrivateDataAccess(asyncNotedAppOp.getOp(),
asyncNotedAppOp.getAttributionTag(),
asyncNotedAppOp.getMessage());
}
};
AppOpsManager appOpsManager = getSystemService(AppOpsManager.class);
if (appOpsManager != null) {
appOpsManager.setNotedAppOpsCollector(appOpsCollector);
}
}
六. Android 11 中的前台服务类型
从 Android 9 开始,应用仅限于在前台访问摄像头和麦克风。为了进一步保护用户,Android 11 更改了前台服务访问摄像头和麦克风相关数据的方式。如果您的应用以 Android 11 为目标平台并且在某项前台服务中访问这些类型的数据,您需要在该前台服务的声明的 foregroundServiceType
属性中添加新的 camera
和 microphone
类型。
注意:为了给开发者更多的时间适应此变更,只有在您的应用以 Android 11 为目标平台时,此变更才会生效。
如果某项前台服务需要访问位置信息、摄像头和麦克风,请按以下代码段所示声明该服务:
<manifest>
...
<service ...
<!-- 如果是多类型,用|追加-->
android:foregroundServiceType="location|camera|microphone" />
</manifest>