Google 致力于为黑人社区推动种族平等。查看具体举措

权限概览

权限的作用是保护 Android 用户的隐私。Android 应用必须请求权限才能访问敏感的用户数据(例如联系人和短信)以及某些系统功能(例如相机和互联网)。系统可能会自动授予权限,也可能会提示用户批准请求,具体取决于访问的功能。

Android 安全架构的设计主旨是:在默认情况下,任何应用都没有权限执行会对其他应用、操作系统或用户带来不利影响的任何操作。这包括读取或写入用户的私有数据(例如联系人或电子邮件)、读取或写入其他应用的文件、执行网络访问、使设备保持唤醒状态等。

本页概述了 Android 权限的工作原理,包括:如何向用户提供权限、安装时权限请求和运行时权限请求之间的区别、如何强制执行权限,以及权限类型和权限组。如果您只需要关于如何使用应用权限的方法指南,请参阅请求应用权限

权限审批

应用必须通过在应用清单中添加 <uses-permission> 标记来公开所需的权限。例如,需要发送短信的应用会在清单中添加以下代码行:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.snazzyapp">

    <uses-permission android:name="android.permission.SEND_SMS"/>

    <application ...>
        ...
    </application>
</manifest>

如果您的应用在清单中列出普通权限(即不会给用户隐私或设备操作带来太大风险的权限),系统会自动将这些权限授予应用。

如果您的应用在清单中列出危险权限(即可能影响用户隐私或设备正常操作的权限),如上面的 SEND_SMS 权限,必须由用户明确同意授予这些权限。

如需详细了解普通权限和危险权限,请参阅保护级别

危险权限的请求提示

仅危险权限需要用户同意。Android 请求用户授予危险权限的方式取决于用户设备上搭载的 Android 版本和应用的目标系统版本。

运行时请求(Android 6.0 及更高版本)

如果设备搭载的是 Android 6.0(API 级别 23)或更高版本,并且应用的 targetSdkVersion 是 23 或更高版本,用户在安装时不会收到任何应用权限的通知。您的应用必须在运行时请求用户授予危险权限。当应用请求权限时,用户会看到一个系统对话框(如图 1 左图所示),告知用户应用正在尝试访问的权限组。该对话框包括拒绝允许按钮。

如果用户拒绝权限请求,当应用下次请求该权限时,该对话框将包含一个复选框,选中它即表示用户不想再收到权限提示(请参阅图 1 右图)。

图 1. 初始权限对话框(左)和包含关闭进一步请求的选项的二次权限请求(右)

如果用户选中不再询问复选框并点按拒绝,当您以后尝试请求相同权限时,系统不会再提示用户。

即使用户授予应用所请求的权限,您也不能指望始终拥有该权限。用户也可以选择在系统设置中逐一启用和停用权限。您应始终在运行时检查并请求权限,以防发生运行时错误 (SecurityException)。

如需详细了解如何处理运行时权限请求,请参阅请求应用权限

安装时请求(Android 5.1.1 及更低版本)

如果设备搭载的是 Android 5.1.1(API 级别 22)或更低版本,或者应用在任何版本的 Android 上运行时其 targetSdkVersion 是 22 或更低版本,系统将在安装时自动请求用户向应用授予所有危险权限(请参见图 2)。

图 2. 安装时权限对话框

如果用户点击接受,系统将授予应用请求的所有权限。如果用户拒绝权限请求,系统将取消安装应用。

如果应用更新包括额外权限需求,系统会在更新应用之前提示用户接受这些新权限。

有关请求权限的建议用户体验模式概览,请参阅应用权限最佳做法

如需了解如何检查并向用户请求权限,请参阅请求应用权限

访问敏感用户信息的请求提示

有些应用依赖于访问与通话记录和短信有关的敏感用户信息。如果您想请求特定于通话记录和短信的权限,并将应用发布到 Play 商店,您必须在请求这些运行时权限之前,提示用户将应用设置为核心系统功能的默认处理程序。

如需详细了解默认处理程序,包括有关如何向用户显示默认处理程序提示的指南,请参阅有关仅在默认处理程序中使用的权限的指南

可选硬件功能的权限

访问某些硬件功能(如蓝牙或相机)需要应用权限。但是,实际上并非所有 Android 设备都具备这些硬件功能。因此,如果您的应用请求 CAMERA 权限,请务必还要在清单中添加 <uses-feature> 标记,用于声明是否确实需要此功能。例如:

<uses-feature android:name="android.hardware.camera" android:required="false" />

如果您对该功能声明 android:required="false",那么 Google Play 就会允许将您的应用安装在没有该功能的设备上。然后,您必须在运行时通过调用 PackageManager.hasSystemFeature() 检查当前设备是否具备该功能,并在没有该功能的情况下将其妥善停用。

如果您未提供 <uses-feature> 标记,那么当 Google Play 发现您的应用请求相应权限时,就会假定您的应用需要此功能。因此,它会从没有该功能的设备中过滤掉您的应用,就像您在 <uses-feature> 标记中声明了 android:required="true" 一样。

如需了解详情,请参阅 Google Play 和根据功能进行过滤

单次授权

从 Android 11(API 级别 30)开始,每当应用请求与位置信息、麦克风或相机相关的权限时,面向用户的权限对话框会包含仅限这一次选项。如果用户在对话框中选择此选项,系统会向应用授予临时的单次授权。

然后,应用可以在一段时间内访问相关数据,具体时间取决于应用的行为和用户的操作:

  • 当应用的 Activity 可见时,应用可以访问相关数据。
  • 如果用户将应用转为后台运行,应用可以在短时间内继续访问相关数据。
  • 如果您在 Activity 可见时启动了一项前台服务,并且用户随后将您的应用转到后台,那么您的应用可以继续访问相关数据,直到该前台服务停止。
  • 如果用户撤消单次授权(例如在系统设置中撤消),无论您是否启动了前台服务,应用都无法访问相关数据。与任何权限一样,如果用户撤消了应用的单次授权,应用进程就会终止。

当用户下次打开应用并且应用中的某项功能请求访问位置信息、麦克风或摄像头时,系统会再次提示用户授予权限。

权限强制执行

权限不仅仅用于请求系统功能。应用提供的服务可强制执行自定义权限以限制谁能使用它们。如需详细了解如何声明自定义权限,请参阅定义自定义应用权限

Activity 权限强制执行

使用 android:permission 属性应用于清单中 <activity> 标记的权限可限制谁能启动该 Activity。系统会在 Context.startActivity()Activity.startActivityForResult() 期间检查权限。如果调用方没有所需的权限,则调用会抛出 SecurityException

服务权限强制执行

使用 android:permission 属性应用于清单中 <service> 标记的权限可限制谁能启动或绑定到关联的 Service。系统会在 Context.startService()Context.stopService()Context.bindService() 期间检查权限。如果调用方没有所需的权限,则调用会抛出 SecurityException

广播权限强制执行

使用 android:permission 属性应用于 <receiver> 标记的权限可限制谁能向关联的 BroadcastReceiver 发送广播。系统会在 Context.sendBroadcast() 返回后检查权限,因为系统会尝试将提交的广播传递到指定的接收器。因此,权限失效不会导致向调用方抛回异常;只是不会传递该 Intent

同样,可以向 Context.registerReceiver() 提供权限,用于控制谁能向以编程方式注册的接收器发送广播。另一方面,可以在调用 Context.sendBroadcast() 时提供权限来限制允许哪些广播接收器接收广播。

请注意,接收器和广播者可能都需要权限。发生这种情况时,两项权限检查都必须通过后方可将 intent 传递到关联的目标。如需了解详情,请参阅通过权限限制广播

内容提供程序权限强制执行

使用 android:permission 属性应用于 <provider> 标记的权限可限制谁能访问 ContentProvider 中的数据。(内容提供程序有重要的附加安全工具可供其使用,称为 URI 权限,将在后面介绍。)与其他组件不同,您可以设置两个单独的权限属性:android:readPermission 限制谁可以读取提供程序,android:writePermission 限制谁可以写入提供程序。请注意,如果提供程序有读取和写入权限保护,仅拥有写入权限并不表示您可以读取提供程序。

第一次检索提供程序时将会检查权限(如果没有任何权限,将会抛出 SecurityException),对提供程序执行操作时也会检查权限。使用 ContentResolver.query() 需要拥有读取权限;使用 ContentResolver.insert()ContentResolver.update()ContentResolver.delete() 需要写入权限。在所有这些情况下,没有所需的权限将导致调用抛出 SecurityException

URI 权限

内容提供程序仅仅使用到目前为止介绍的标准权限系统往往是不够的。内容提供程序可能需要通过读取和写入权限保护自己,而其直接客户端也需要将特定 URI 传给其他应用以便于它们运行。

电子邮件应用中的附件就是一个典型的示例。对电子邮件的访问应通过权限加以保护,因为这是敏感的用户数据。但是,如果将图像附件的 URI 提供给图像查看器,该图像查看器就不再有打开附件的权限,因为它没有理由拥有访问所有电子邮件的权限。

此问题的解决方法是采用按 URI 的权限机制:在启动 Activity 或返回结果给 Activity 时,调用方可以设置 Intent.FLAG_GRANT_READ_URI_PERMISSION 和/或 Intent.FLAG_GRANT_WRITE_URI_PERMISSION。这将向接收 Activity 授予访问 intent 中特定数据 URI 的权限,而不管它是否具有任何权限可访问 intent 对应的内容提供程序中的数据。

此机制支持常见的能力式模型,其中用户互动(如打开附件、从列表中选择联系人等)推动临时授予细化的权限。这是一项关键功能,可将应用所需的权限缩小至只与其行为直接相关的权限。

如需构建最安全的实现,使其他应用对其在您的应用中的操作负责,您应该以这种方式使用细化的权限,并使用 android:grantUriPermissions 属性或 <grant-uri-permissions> 标记声明您的应用对它的支持。

如需了解详情,请参阅 Context.grantUriPermission()Context.revokeUriPermission()Context.checkUriPermission() 方法。

其他权限强制执行

您可对任何服务调用强制执行任意细化的权限。这可通过 Context.checkCallingPermission() 方法完成。使用所需的权限字符串调用,它将返回一个整数,表示是否已向当前的调用进程授予权限。请注意,仅在执行从另一个进程传入的调用(通常是通过从服务发布的 IDL 接口或者指定给另一进程的某种其他方式完成)时才可使用此方法。

检查权限还有许多其他有用的方法。如果您有另一个进程的进程 ID (PID),您可以使用 Context.checkPermission() 方法检查针对该 PID 的权限。如果您有另一个应用的软件包名称,您可以使用 PackageManager.checkPermission() 方法了解是否已为该软件包授予特定权限。

自动重置未使用的应用的权限

如果应用以 Android 11(API 级别 30)或更高版本为目标平台并且数月未使用,系统会通过自动重置用户已授予应用的运行时敏感权限保护用户数据。此操作与用户在系统设置中查看权限并将应用的访问权限级别更改为拒绝的做法效果一样。

如果应用遵循了有关在运行时请求权限的最佳做法,那么您不必对应用进行任何更改。

请求用户停用自动重置功能

如果需要,您可以要求用户阻止系统重置应用的权限。如果用户希望应用主要在后台运行,即使用户不与应用互动,应用也能正常工作,那么此做法就非常有用。此类使用场景的示例如下:

图 3. 用户已停用为给定应用自动重置权限的功能。
  • 为家人的安全提供保障
  • 同步数据
  • 与智能设备通信
  • 与配套设备配对

如需将用户定向到系统设置中您应用的页面,请调用包含 Intent.ACTION_AUTO_REVOKE_PERMISSIONS intent 操作的 intent。在此屏幕中,用户可以通过执行以下操作来阻止系统重置应用的权限:

  1. 点按权限,系统会加载应用权限设置屏幕。
  2. 关闭如果未使用此应用,移除相关权限选项,如图 3 所示。

确定是否已停用自动重置功能

如需检查是否已针对应用停用自动重置功能,请调用 isAutoRevokeWhitelisted()。如果此方法返回 true,则系统不会自动重置应用的权限。

测试自动重置功能

如需验证系统是否重置了应用的权限,请执行以下操作:

  1. 保存系统重置应用权限所需等待的默认时长。这样,您就可以在测试后恢复此设置:

    threshold=$(adb shell device_config get permissions \
      auto_revoke_unused_threshold_millis2)
    
  2. 减少系统重置权限所需等待的时长。下面的示例对系统进行了修改,以致当您停止与应用互动后仅一秒钟,系统就会重置应用的权限:

    adb shell device_config put permissions \
      auto_revoke_unused_threshold_millis2 1000
    
  3. 手动调用自动重置进程,如以下代码段所示。在运行此命令之前,请确保测试设备已开启片刻(大约 45 秒钟)。

    adb shell cmd jobscheduler run -u 0 -f \
      com.google.android.permissioncontroller 2
    
  4. 验证应用能否处理自动重置事件。

  5. 恢复系统在自动重置应用权限之前所需等待的默认时长:

    adb shell device_config put permissions \
      auto_revoke_unused_threshold_millis2 $threshold
    

自动授予新权限

随着时间的推移,平台中可能会加入新的限制,以致您的应用为了使用特定 API,必须请求之前不需要的权限。因为现有应用假设可随意获取这些 API 的访问权限,所以 Android 可能会将新的权限请求应用到应用清单,以免在新平台版本上中断应用(从而使应用能够继承权限)。Android 将根据为 targetSdkVersion 属性提供的值决定应用是否需要权限。如果该值低于在其中添加权限的版本,Android 就会添加该权限。

例如,READ_EXTERNAL_STORAGE 权限从 API 级别 19 开始强制执行,用于限制对共享存储空间的访问。如果您的 targetSdkVersion 为 18 或更低版本,系统就会向更高版本的 Android 中运行的应用添加此权限。

警告:如果某权限自动添加到应用,即使您的应用可能实际并不需要这些附加权限,Google Play 上的应用详情也会列出它们。为避免这种情况并移除您不需要的默认权限,请始终将 targetSdkVersion 尽可能更新至最高版本。您可在 Build.VERSION_CODES 文档中查看各版本添加的权限。

保护级别

权限分为几个保护级别。保护级别影响着是否需要运行时权限请求。

有三种保护级别会影响第三方应用:普通、签名和危险权限。如需查看特定权限所拥有的保护级别,请访问权限 API 参考页面

普通权限

普通权限涵盖以下情况:应用需要访问其沙盒外部的数据或资源,但对用户隐私或其他应用的操作带来的风险很小。例如,设置时区的权限就是普通权限。

如果应用在清单中声明需要普通权限,系统会在安装时自动向应用授予该权限。系统不会提示用户授予普通权限,用户也无法撤消这些权限。

签名权限

系统在安装时授予这些应用权限,但仅会在尝试使用某权限的应用签名证书为定义该权限的同一证书时才会授予。

危险权限

危险权限涵盖以下情况:应用需要的数据或资源涉及用户隐私信息,或者可能对用户存储的数据或其他应用的操作产生影响。例如,能够读取用户的联系人属于危险权限。如果应用声明其需要危险权限,必须由用户向应用明确授予该权限。在用户批准该权限之前,应用无法提供依赖于该权限的功能。

为了使用危险权限,应用必须在运行时提示用户授予权限。如需详细了解如何提示用户,请参阅危险权限的请求提示

特殊权限

有几项权限的行为与普通权限及危险权限都不同。SYSTEM_ALERT_WINDOWWRITE_SETTINGS 特别敏感,因此大多数应用不应该使用它们。

在大多数情况下,应用必须在清单中声明 SYSTEM_ALERT_WINDOW 权限,并发送请求用户授权的 intent。系统将向用户显示详细管理屏幕,以响应该 intent。

从 Android 11(API 级别 30)开始,调用 ACTION_MANAGE_OVERLAY_PERMISSION intent 操作的应用不能指定软件包。当应用调用包含此 intent 操作的 intent 时,必须由用户先选择想要授予或撤消哪些应用的权限。此行为可以让权限的授予更有目的性,从而达到保护用户的目的。

如需详细了解如何请求这些权限,请参阅 SYSTEM_ALERT_WINDOWWRITE_SETTINGS 参考条目。

如需了解 Android 系统提供的所有权限,请参阅 Manifest.permission

权限组

权限根据设备的功能或特性分为多个组。在这一体系中,权限请求在组级别进行处理,一个权限组对应于应用清单中的多个权限声明。例如,短信组同时包括 READ_SMSRECEIVE_SMS 声明。通过这种方式对权限分组,用户就能做出更有意义和明智的选择,而不会对复杂和技术性权限请求感到无所适从。

所有危险的 Android 权限都属于权限组。任何权限都可以属于某个权限组,不管保护级别如何。但是,权限组仅当权限危险时才影响用户体验。

如果设备搭载的是 Android 6.0(API 级别 23),并且应用的 targetSdkVersion 是 23 或更高版本,系统会在应用请求危险权限时发生以下行为:

  • 如果应用当前在权限组中没有任何权限,系统会向用户显示权限请求对话框,描述应用要访问的权限组。对话框不描述该组内的具体权限。例如,如果应用请求 READ_CONTACTS 权限,系统对话框只会说明应用需要访问设备的联系人。如果用户批准,系统只会向应用授予其请求的权限。
  • 如果应用已在相同权限组中被授予另一危险权限,系统将立即授予该权限,不会与用户进行任何互动。例如,如果某应用之前已请求并且被授予了 READ_CONTACTS 权限,然后它又请求 WRITE_CONTACTS,系统将立即授予该权限,不向用户显示权限对话框。

注意:将来版本的 Android SDK 可能可以将特定权限从一个组移到另一个组。因此,不要根据这些权限组的结构设置应用的逻辑。

例如,自 Android 8.1(API 级别 27)起,READ_CONTACTS 位于与 WRITE_CONTACTS 相同的权限组中。如果您的应用请求 READ_CONTACTS 权限,然后请求 WRITE_CONTACTS 权限,不要假定系统可以自动授予 WRITE_CONTACTS 权限。

如果设备搭载的是 Android 5.1(API 级别 22)或更低版本,或者应用的 targetSdkVersion 是 22 或更低版本,系统会在安装时要求用户授予权限。再次强调,系统只会向用户告知应用需要的权限组,而不告知具体权限。例如,当应用请求 READ_CONTACTS 时,安装对话框会列出“联系人”组。当用户接受后,系统仅向应用授予 READ_CONTACTS 权限。

注意:您的应用仍需要明确请求其需要的每项权限,即使用户已授予同一组中的其他权限。此外,权限分组在将来的 Android 版本中可能会发生变化。代码的逻辑不应依赖于同一组中的一组特定权限。

查看应用的权限

您可以使用“设置”应用和 shell 命令 adb shell pm list permissions 查看系统中当前定义的所有权限。如需使用“设置”应用,请依次转至设置 > 应用。选择一个应用并向下滚动查看该应用使用的权限。对于开发者,adb '-s' 选项以类似于用户所见内容的形式显示权限:

$ adb shell pm list permissions -s
All Permissions:

Network communication: view Wi-Fi state, create Bluetooth connections, full
internet access, view network state

Your location: access extra location provider commands, fine (GPS) location,
mock location sources for testing, coarse (network-based) location

Services that cost you money: send SMS messages, directly call phone numbers

...

在模拟器中或测试设备上安装应用时,您也可以使用 adb -g 选项自动授予所有权限:

$ adb shell install -g MyApp.apk

其他资源

  • 请求应用权限:有关请求应用权限的方法指南。
  • 隐含功能要求的权限:介绍请求某些权限如何隐式将您的应用限制于包含相应硬件或软件功能的设备。
  • <uses-permission>:声明应用所需权限的清单标记的 API 参考文档。
  • 设备兼容性:介绍 Android 如何在不同类型的设备上工作,以及如何针对各种设备优化您的应用,或如何限制您的应用在不同设备上的可用性。
  • Android 安全性概览:详细介绍 Android 平台的安全模型。
  • “妈妈,我可以吗?”请求权限:2015 年 Android 开发者峰会上的这个视频介绍了请求权限的最佳做法。
  • Android M 权限:2015 年 Google I/O 大会上的这个视频说明了 Android 6.0 中对权限模型进行的更改。