保护 Android 用户隐私,从这些事做起
△ 主动为用户提供全面的隐私保护
Bilibili 视频链接
https://www.bilibili.com/video/BV1g3411g7rC/
隐私保护三原则
△ 隐私保护的三个原则
提升数据访问透明度: 让用户知道应用在什么时间访问了哪些数据; 简化用户控制隐私访问的方式: 让用户方便地控制应用能访问更多或更少的用户数据; 专注最小化数据访问: 去除不必要的数据访问,减少应用的权限范围,避免应用泄露用户的数据。
隐私保护最佳实践
△ 集成隐私保护的三个最佳实践
基于隐私保护三个核心原则,我们会持续为您提供各种各样的工具和指导,帮助您在应用中高效地集成隐私保护功能。这里要向您分享的是在移动应用开发中可以考虑的三个最佳实践,您会了解如何提升隐私访问的透明度,了解如何在尊重用户选择权的前提下提供隐私访问的选项,以及了解如何通过最小化隐私访问尽可能减少不必要用户数据的获取。
关注数据访问
您需要考虑的第一个最佳实践是要仔细斟酌应用对用户数据的访问。一方面是由于 Android 12 让用户更直观地看到自己的隐私如何被访问,另一方面则是出于您尊重用户意愿的考量。
应用访问传感器时的系统提示
随着 Android 的透明度越来越高,您也越来越需要关注自己的应用何时对用户数据进行了访问。Android 12 让用户更清楚地知晓应用何时访问了麦克风和摄像头。每当麦克风或摄像头被访问时,用户可以通过从屏幕右上角下拉打开快捷设置,然后点击对应的指示图标来实时查看是哪些应用在访问数据。如果用户发现自己对此次访问并不知情,可以很方便地跳转到应用的权限设置界面并撤销相关权限。所以需要您仔细审查应用中涉及麦克风、摄像头访问的代码,移除那些意外的访问操作。例如,您应该确保在用户触发需要访问有关传感器的功能前,应用不会去获取这些设备的数据。
应用的隐私数据访问记录
我们常常收到用户的反馈,他们希望了解应用究竟使用了哪些数据。对此我们做出了一些努力,全新的隐私信息中心让用户可以通过一个简单清晰的时间线视图来了解到过去 24 小时中,哪些应用访问了设备的麦克风、摄像头和地理位置数据。另外,用户还可以查看到应用是否在过去 24 小时里使用其他运行时权限访问过相关数据。
建议您对应用中相关的代码路径进行仔细审查,确保对每一处隐私数据的访问都能做合理的解释说明。由于使用到的第三方 SDK 也会被当作您应用的一部分进行统计,所以需要为它们的隐私访问提供正当的用例说明。
应用的数据使用说明
用户非常关心您的应用是出于怎样的原因访问了隐私数据,所以当用户在 Play 商店浏览应用时,会看到一个专门的数据安全条款,它为用户提供了直观易懂的应用数据使用的相关信息,从而帮助用户在知情的前提下自主决定要安装哪些应用。这样一来,用户会有更多的安全感,并且更能相信开发者们会负责任地使用用户数据。关于如何在 Play 商店的数据安全条款提供恰当的说明,您可以查看视频: 为 Google Play 中的 "数据安全" 条款做好准备:
应用读取剪贴板时的通知
相信您一定有过这样的经验,好友向您发送了一条消息,比如用户名和密码,您常常会复制这些信息,然后粘贴到另一个应用中使用。在这个过程中,这些关键数据会被存放在剪贴板中,任何应用都可以读取这些数据,产生了潜在的隐私泄露风险。
每当有应用从剪贴板读取数据时,Android 都会通知用户。每当应用调用 ClipboardManager#getPrimaryClip() 方法时,Android 会判断写入和读取剪贴板数据的是不是同一个应用,当两者来源不同时,系统会通过一个消息框来提示用户;当两者来自同一个应用时,则不会产生这样的提示。所以我们建议您的应用首先调用 ClipboardManager#getPrimaryClipDescription() 方法来获取剪贴板中数据的基本信息,并根据其类型判断是否需要进一步读取,从而最大限度减少对剪贴板数据的访问。另外,我们还建议您不要随意访问剪贴板,如果有必要,也应该在用户知情和许可的前提下进行。
为更加透明的隐私访问做好准备
为了更好支持 Android 12 带来的隐私透明特性,我们建议您仔细审查应用的代码是否还存在意外的隐私访问操作。您可以借助审计 (auditing) API 来更好地发现潜在的隐私数据读取操作以及第三方 SDK 对隐私数据的访问。这个 API 可以在您的应用访问敏感数据时调用一个应用内的回调函数,并向其提供所访问的数据类型,这样您就可以轻松地发现应用在何时、何种情况下读取了隐私数据。
您也可以通过权限 intent API 来向用户说明为什么您的应用需要访问地理位置、摄像头和麦克风,从而帮助他们理解和判断是否要给予这些权限。您通过此 API 提供的这些信息将会在隐私访问信息面板及应用的权限管理界面中向用户展示。
<!-- 添加一个 activity,它可以在启动时向用户说明为什么您的应用会读取某种类型的数据 -->
<!-- 如果您的应用要针对 Android 12 构建,还必须标注 android:exported 属性 -->
<activity android:name="DataAccessRationaleActivity"
android:permission="android.permission.START_VIEW_PERMISSION_USAGE"
android:exported="true">
<!-- VIEW_PERMISSION_USAGE 会在应用的权限管理界面显示一个可勾选的图标;VIEW_PERMISSION_USAGE_FOR_PERIOD 会在系统的隐私访问面板显示一个可勾选的图标。-->
<intent-filter>
<action android:name="android.intent.action.VIEW_PERMISSION_USAGE"/>
<action android:name="android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD"/>
</intent-filter>
</activity>
上面的代码展示了您添加权限 intent 时需要进行的配置。这段代码在您的应用中添加了一个 activity,它可以在启动时告知用户为什么要访问数据。您需要把 android:permission 属性设置为 START_VIEW_PERMISSION_USAGE。如果您的应用是针对 Android 12 进行构建的,那么还需要添加 android:exported="true" 属性。接着添加一个 intent-filter 标签,随后根据您的需要,分别将 android.intent.action.VIEW_PERMISSION_USAGE (在应用的权限管理界面显示) 和 android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD (在隐私访问面板显示) 添加到这个标签里。这样一来,用户就可以在您的应用名称旁看到一个与应用的 intent-filter 对应的图标。如果需要详细了解数据访问审计 API 和权限 intent 的更多内容,请参阅: 使用隐私信息中心提高用户透明度。
综上所述,在开发应用时,对于数据访问需要注意以下几点:
只有在必要时才访问隐私数据。不必要的数据访问不仅会让用户感到困惑,还会增加隐私泄露的风险; 注意引入第三方库时添加的权限声明。您可以通过合并 manifest 来查看引入的第三方库声明了哪些权限。别让引入的 SDK 和第三方库导致您应用被下架!这篇文章为您提供了更多的参考信息; 切勿过度访问用户数据。对用户隐私数据的读取如果超过了用户使用的需要,就是对用户知情权和控制权的侵犯。
尊重用户选择
我们要分享的第二个最佳实践关乎着用户的选择。Android 用户可以自行掌控哪些应用可以访问他们的敏感数据,以及这些数据被应用访问的程度。对于开发者来说,掌握好这个度非常重要。
研究表明,用户对应用需要访问数据的原因了解得越充分,那么他们认可这些访问的可能性就越大。您需要通过提供安全的默认参数来平衡用户的掌控权和应用的访问权限,所以您应该向用户提供一些易于理解的选项,并尊重他们的意志。
更细致的位置权限选项
△ 选择大致位置能让应用减少访问用户的位置信息
我们在 Android 12 中引入了粒度更细的位置权限选项,使得用户可以自行决定是否只向应用提供粗略的位置信息。我们建议您仔细检查应用中所有需要访问位置信息的用例,如果精确的定位不是必须的,请改为申请 ACCESS_COARSE_LOCATION 权限。
无论何种情况下,您都应该具体地向用户说明为什么需要访问位置信息,并且按照具体的精度需求逐渐向用户申请更精确定位信息的访问权限。同时,您需要考虑到用户仅允许应用获取粗略位置的情况,不能因为位置信息不精确就拒绝让用户继续使用。
如下示例代码包含了两个功能,其中一个是只需要访问大概位置,而另一个则是需要获取精确定位。当用户给予应用获取大致位置的权限时,您需要通过 shouldShowRequestPermissionRationale API 来检查是否需要向用户显示必要的权限申请说明。如果返回了 true,则需要展示您的说明,同时显示申请大致位置的弹框 (请求 ACCESS_COARSE_LOCATION 权限)。
// 请求 ACCESS_COARSE_LOCATION 权限
requestPermissions(
Context,
arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION),
REQUEST_CODE
)
△ 请求 ACCESS_COARSE_LOCATION 权限
if (grantResults[1] == PackageManager.PERMISSION_GRANTED) {
// ACCESS_COARSE_LOCATION 权限已获授权。
}
△ 处理请求的结果
当用户以后用到需要获取精确定位的功能时,您就可以通过向用户显示申请更精确位置信息的弹框来获得 ACCESS_FINE_LOCATION 权限了。您同样需要确保用户了解您的位置用例后再发起请求。
// 请求 ACCESS_FINE_LOCATION 权限
requestPermissions(
Context,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
REQUEST_CODE
)
△ 请求 ACCESS_FINE_LOCATION 权限
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// ACCESS_FINE_LOCATION 权限已获授权。
}
全局权限开关
Android 12 中引入了这样两个开关,分别对应摄像头和麦克风的全局访问:
这两个开关允许用户迅速切断整台设备上所有应用对摄像头或者麦克风的访问。如果用户在应用中使用了某个需要访问摄像头、麦克风的功能,那么系统将会向用户询问是否需要立即开放传感器的访问。这组开关与直接拒绝访问权限是不同的,因为获取访问权的整个过程是由系统来处理的,提醒用户启用设备也是由系统展示的,应用无需进行任何额外的操作。
此外值得一提的是,我们在这次更新中还增加了对运动传感器采样率的限制 (200Hz 以内)。
通知显示权限
我们常常听到用户关于设备上通知过多的抱怨,所以我们在新版本中,要求应用需要向用户申请通知显示权限,只有当用户希望或允许收到通知时,应用才可以向用户发送通知。
如果您的应用需要向用户发送通知,请记得在清单文件中添加 POST_NOTIFICATIONS 权限的声明。下面的代码展示了如何申请通知的权限:
// 请求权限后向用户发送通知
requestPermissions(
Context,
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
REQUEST_CODE
)
在访问数据前,让用户充分了解您读取数据的原因;
做好用户和系统拒绝权限时的操作,当用户二次拒绝时,您应该尊重用户的意愿;
按需逐级获取隐私数据,不要一次性申请所有权限;
当用户拒绝或是撤销某项权限时,您需要让应用能无缝回退到无需权限即可运行的状态。
最大程度减少权限使用
第三个最佳实践是最大程度减少权限的使用。一方面您应当对用户的意愿和选择保持尊重,另一方面您还可以使用 Android 的替代 API,在简化敏感数据访问的同时提供更好的隐私控制。以下几个方案可以帮助您最大限度减少数据访问。
使用新的附近设备访问权限
可穿戴设备在最近几年发展迅猛,大量的应用需要与这些设备进行交互。在以前,应用必须先申请位置访问权限才能与配套设备进行蓝牙连接。开发者们向我们反馈了这种不恰当的设计,尤其是当应用只需要获取蓝牙访问权限而不需要获得设备位置的时候。过度的权限申请也使得用户对应用的行为正当性产生怀疑,这些反馈敦促着我们改进权限的对应关系。
构建目标为 API 30 及以前版本 Android 的应用,需要同时申请 BLUETOOTH_ADMIN 和 BLUETOOTH 权限,以及位置权限才能实现发现、配对和连接外部设备。比如下方的代码是您的应用在较早设备上需要进行的权限声明:
<!-- 在较早的设备上申请蓝牙有关权限 -->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
当您切换到构建 API 31 的应用时,可以在上述权限声明的基础上添加一个 maxSDKVersion 属性:
<!-- 在较早的设备上申请蓝牙有关权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="30" />
<!-- 请求扫描附近蓝牙设备的权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
<!-- 请求与已经配对的蓝牙设备通信的权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- 请求使用当前设备可供发现的权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
△ 兼容新旧设备的前提下申请蓝牙有关权限
随后,您需要添加 BLUETOOTH_SCAN 权限声明,并且使用 neverForLocation 标记来向系统说明您不会使用这个权限来推算设备的位置信息。同时您需要声明 BLUETOOTH_CONNECT 权限来与蓝牙设备交互、通过 BLUETOOTH_ADVERTISE 来将当前设备信息广播给附近的蓝牙设备。
为应用设置智能应用休眠功能
△ Android 11 引入的权限自动重置功能
2021 年,在权限自动重置功能的基础上,我们推出了智能应用休眠功能。Android 会自动将长期没有使用的应用进行休眠,从而优化设备存储、改善性能和提高安全性。系统不仅会撤销用户此前的授权,还会强制停止应用,收回内存、存储空间及其他临时资源。
当应用进入休眠后,系统会阻止应用在后台运行任务,或者接收推送通知。而结束应用休眠的方式也非常简单,用户只需要启动应用即可。与权限自动重置类似,应用进入休眠时,用户会收到相应的通知,同时用户也可以在设置中选择关闭休眠。
用好分区存储策略
Android 10 的发布首次引入了分区存储机制,它为隐私保护提供了一种新的存储方案。随着后续几个版本的迭代更新,其他应用不再能访问某个应用的外部目录了。您也可以在不请求任何权限的基础上添加和编辑本应用的文件,或是在用户知情同意的前提下编辑第三方应用产生的文件。而如果您将文件添加到共享存储目录中,则无需任何权限申请操作。
未来我们会发布一个照片选择器 (Photo Picker),它可以无需任何请求即可读取用户选择的照片或视频。您可以在其中选择设备本地存储的照片或视频,也可以访问到来自 Google Photos 等云提供商的照片或视频。这个新的选择器会替换以前申请权限的访问方式,简化应用的权限声明。
照片选择器 (Photo Picker) https://developer.android.google.cn/about/versions/13/features/photopicker
通过 Google Play 系统更新,Android 11 及以后的设备都可以使用这个新的照片选择器。下面是一个使用这款新照片选择器的例子:
// 要发送的 intent
val intent = Intent(MediaStore.ACTION_PICK_IMAGES).apply {
putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, 15)
putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/*", "video/*"))
}
// 处理返回的 intent
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
// 遍历所有的 URI 结果
for (i in 0 until data.clipData.itemCount) {
val uri = data.clipData.getItemAt(i).uri
val inputStream = contentResolver.openInputStream(uri)
}
}
存储访问框架 https://developer.android.google.cn/guide/topics/providers/document-provider
新的存储权限划分策略
<!-- 在 API 31 和更早的设备上请求权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="31"/>
<!-- 请求图片和视频访问权限 -->
<uses-permission android:name="android.permission.READ_IMAGES"/>
<!-- 请求音频访问权限 -->
<uses-permission android:name="android.permission.READ_AUDIO"/>
<!-- 请求所有共享文件访问权限(仅用于受允许的用例) -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
请注意,MANAGE_EXTERNAL_STORAGE 权限可以用于那些核心功能就是要与大量的文件进行交互的应用。但此权限受限于 Google Play 关于使用 "所有文件访问权限" 的政策。关于此权限的使用,请参阅管理存储设备上的所有文件:
以上这些建议概括如下几点:
尽可能减少位置数据访问,并且尽量只在应用处于前台时访问粗略的位置信息; 使用新的蓝牙权限来更好地提供隐私保护; 尽可能通过新的照片选择器来避免请求共享存储空间。
开发者可降级权限
一些应用可能不再需要某些之前由用户授予过的权限,这些权限曾用于开启某项特定功能或保留旧的 Android 版本中的敏感权限。在 Android 13 中,我们提供了新的 API,让您的应用通过降级以前被授予的运行时权限来保护用户隐私。
您可以通过下方二维码或在文章底部私信,向我们提交反馈,分享您喜欢的内容、发现的问题。您的反馈对我们非常重要,感谢您的支持!
推荐阅读