权限的目的是保护Android用户的隐私。
Android应用访问敏感的用户数据(如联系人和短信)以及某些系统功能(如相机和互联网)必须请求权限。 根据功能的不同,系统可能会自动授予权限,也可能会提示用户批准请求。
此页面概述了Android权限的工作方式,包括:如何向用户呈现权限,安装时和运行时权限请求之间的区别,权限的实施方式以及权限组的类型。 如果您只想获得使用应用权限的操作指南,请参阅请求应用权限。
一、许可批准
应用必须通过在应用清单中包含
标记来公布所需的权限。 例如,需要发送SMS消息的应用程序将在清单中包含以下行:
...
如果您的应用在其清单中列出了正常权限(即,不会对用户的隐私或设备操作造成太大风险的权限),系统会自动将这些权限授予您的应用。
如果您的应用在其清单中列出了危险权限(即可能会影响用户隐私或设备正常运行的权限),例如上面的SEND_SMS权限,则用户必须明确同意授予这些权限。
有关正常和危险权限的详细信息,请参阅保护级别。
1、请求提示危险权限
只有危险权限才需要用户同意。 Android要求用户授予危险权限的方式取决于用户设备上运行的Android版本以及应用所针对的系统版本。
运行时请求(Android 6.0及更高版本)
如果设备运行的是Android 6.0(API级别23)或更高版本,并且应用程序的targetSdkVersion为23或更高,则在安装时不会通知用户任何应用程序权限,您的应用必须要求用户在运行时授予危险权限。 当您的应用请求权限时,用户会看到一个系统对话框(如图1左侧所示),告诉用户您的应用尝试访问哪个权限组。 该对话框包括拒绝和允许按钮。
如果用户拒绝权限请求,则下次您的应用请求权限时,该对话框包含一个复选框,选中该复选框后,表示不希望再次提示用户获得权限(参见图2,右侧)。
图1.初始权限对话框(左)和次要权限请求以及关闭进一步请求的选项(右)
如果用户选中“永不再询问”框并点击“拒绝”,则系统不再提示用户以后是否尝试请求相同的权限。
即使用户授予您的应用程序所请求的权限,您也不能总是依赖它。 用户还可以选择在系统设置中逐个启用和禁用权限。 您应该始终在运行时检查并请求权限以防止运行时错误(SecurityException)。
有关如何处理运行时权限请求的详细信息,请参阅请求应用程序权限。
安装时请求(Android 5.1.1及更低版本)
如果设备运行Android 5.1.1(API级别22)或更低版本,或者应用程序的targetSdkVersion在任何版本的Android上运行时为22或更低,系统会自动要求用户在安装时为您的应用授予所有危险权限(见图2)。
图2.安装时权限对话框
如果用户单击“接受”,则授予应用程序请求的所有权限。 如果用户拒绝权限请求,系统将取消应用程序的安装。
如果应用更新包含对其他权限的需求,则会在更新应用之前提示用户接受这些新权限。
有关请求权限的建议,请参阅应用程序权限最佳实践。
要了解如何检查和请求用户的权限,请参阅请求应用程序权限。
二、可选硬件功能的权限
访问某些硬件功能(如蓝牙或相机)需要应用程序权限。 但是,并非所有Android设备都具有这些硬件功能。 因此,如果您的应用请求CAMERA
权限,那么您还需要在清单中包含
标记来声明是否确实需要此功能。 例如:
如果您为该功能声明了android:required =“false”
,那么Google Play允许您的应用安装在没有该功能的设备上。 然后,您必须通过调用PackageManager.hasSystemFeature()
检查当前设备是否在运行时具有该功能,并在该功能不可用时禁用该功能。
如果您未提供
标记,那么当Google Play看到您的应用请求相应的权限时,它会认为您的应用需要此功能。 因此,它会从没有该功能的设备中过滤您的应用,就像您在
标记中声明了android:required =“true”
一样。
有关详细信息,请参阅Google Play和基于功能的过滤。
三、许可执行
权限不仅适用于请求系统功能。 应用程序提供的服务可以强制执行自定义权限,以限制谁可以使用它们。 有关声明自定义权限的详细信息,请参阅定义自定义应用程序权限。
1、活动权限执行
使用android:permission
属性对清单中的
标记应用的权限限制了谁可以启动该Activity
。 在Context.startActivity()
和Activity.startActivityForResult()
期间检查权限。 如果调用者没有所需的权限,则从调用中抛出SecurityException
。
2、服务权限执行
使用android:permission
属性对清单中的
标记应用的权限限制谁可以启动或绑定到关联的服务。 在Context.startService()
,Context.stopService()
和Context.bindService()
期间检查权限。 如果调用者没有所需的权限,则从调用中抛出SecurityException
。
3、广播许可执行
使用android:permission
属性应用于
标签的权限限制了谁可以向关联的BroadcastReceiver
发送广播。 在Context.sendBroadcast()
返回后检查权限,因为系统尝试将提交的广播传递给给定的接收者。 因此,权限失败不会导致异常被抛回调用者; 它只是没有提供意图。
以同样的方式,可以向Context.registerReceiver()
提供权限,以控制谁可以向编程注册的接收者广播。 另一方面,在调用Context.sendBroadcast()
以限制允许哪些广播接收器接收广播时,可以提供权限。
请注意,接收方和广播公司都需要许可。 发生这种情况时,必须通过两个权限检查才能将意图传递给关联目标。 有关更多信息,请参阅限制具有权限的广播。
4、Content Provider
权限实施
使用android:permission
属性应用于
标记的权限限制了谁可以访问ContentProvider
中的数据。 (内容提供商有一个重要的额外安全设施,称为URI权限,这将在下面描述。)与其他组件不同,您可以设置两个单独的权限属性:android:readPermission
限制谁可以从提供程序读取,以及android: writePermission
限制谁可以写入它。请注意,如果提供程序受读取和写入权限保护,则仅保留写入权限并不意味着您可以从提供程序读取。
首次检索提供程序时将检查权限(如果您没有任何权限,则抛出SecurityException
),以及在提供程序上执行操作时。使用ContentResolver.query()
需要保持读取权限;使用ContentResolver.insert()
,ContentResolver.update()
,ContentResolver.delete()
需要写入权限。在所有这些情况下,未保留所需的权限会导致从调用中抛出SecurityException
。
5、URI权限
当与内容提供商一起使用时,到目前为止描述的标准许可系统通常是不够的。内容提供商可能希望使用读取和写入权限来保护自己,而其直接客户端还需要将特定URI传递给其他应用程序以供其操作。
一个典型的例子是电子邮件应用程序中的附件。对电子邮件的访问应受权限保护,因为这是敏感的用户数据。但是,如果图像附件的URI被提供给图像查看器,则该图像查看器不再具有打开附件的权限,因为它没有理由拥有访问所有电子邮件的权限。
此问题的解决方案是per-URI
权限:启动活动或将结果返回给活动时,调用者可以设置Intent.FLAG_GRANT_READ_URI_PERMISSION
和/或Intent.FLAG_GRANT_WRITE_URI_PERMISSION
。这授予接收活动权限访问意图中的特定数据URI,而不管它是否具有访问对应于意图的内容提供者中的数据的任何许可。
此机制允许一种通用的功能样式模型,其中用户交互(例如打开附件或从列表中选择联系人)驱动临时授予细粒度权限。这可以是将应用程序所需的权限仅限于与其行为直接相关的权限的关键工具。
要构建最安全的实现,使其他应用程序对您在yor应用程序中的操作负责,您应该以这种方式使用细粒度权限,并使用android:grantUriPermissions属性或
可以在Context.grantUriPermission()
,Context.revokeUriPermission()
和Context.checkUriPermission()
方法中找到更多信息。
6、其他许可执行
任何对服务的调用都可以强制执行任意细粒度的权限。 这是通过Context.checkCallingPermission()
方法完成的。 使用所需的权限字符串进行调用,并返回一个整数,指示是否已将该权限授予当前调用进程。 请注意,这只能在您执行来自其他进程的调用时使用,通常是通过从服务发布的IDL接口或以其他方式执行到另一个进程。
还有许多其他有用的方法可以检查权限。 如果您具有另一个进程的进程ID(PID),则可以使用Context.checkPermission()
方法检查针对该PID的权限。 如果您具有其他应用程序的程序包名称,则可以使用PackageManager.checkPermission()
方法来确定该特定程序包是否已被授予特定权限。
三、自动权限调整
随着时间的推移,可能会向平台添加新的限制,以便为了使用某些API,您的应用必须请求以前不需要的权限。 由于现有应用程序假定可以免费访问这些API,因此Android可以将新的权限请求应用于应用程序的清单,以避免破坏新平台版本上的应用程序(从而“为您的应用程序”授予“权限”)。 Android根据为 targetSdkVersion
属性提供的值,决定应用是否可能需要该权限。 如果该值低于添加权限的版本,则Android会添加权限。
例如,从API级别19开始强制执行READ_EXTERNAL_STORAGE
权限,以限制对共享存储空间的访问。 如果您的targetSdkVersion为18或更低,则此权限会在较新版本的Android上添加到您的应用中。
警告:如果您的应用自动添加了权限,则Google Play上的应用列表会列出这些附加权限,即使您的应用可能实际上并不需要这些权限。 要避免这种情况并删除您不需要的默认权限,请始终将targetSdkVersion更新为尽可能高。 您可以在
Build.VERSION_CODES
文档中查看每个版本添加了哪些权限。
四、保护级别
权限分为几个保护级别。 保护级别会影响是否需要运行时权限请求。
有三种保护级别会影响第三方应用程序:正常,签名和危险权限。
普通权限涵盖了应用程序需要访问应用程序沙箱外部数据或资源的区域,但用户隐私或其他应用程序操作的风险非常小。 例如,设置时区的权限是正常权限。
1、正常权限
如果应用程序在其清单中声明它需要正常权限,则系统会在安装时自动授予应用程序该权限。 系统不会提示用户授予正常权限,用户也无法撤消这些权限。
从Android 8.1(API级别27)开始,以下权限被归类为PROTECTION_NORMAL
:(参考Android官方文档,这里不做详细描述)。
2、签名权限
系统会在安装时授予这些应用程序权限,但仅限于尝试使用权限的应用程序使用与定义权限的应用程序相同的证书进行签名时。
注意:某些签名权限不适用于第三方应用程序。
从Android 8.1(API级别27)开始,第三方应用程序可以使用的以下权限归类为PROTECTION_SIGNATURE
:(参考Android官方文档,这里不做详细描述)。
3、危险的权限
危险权限涵盖应用程序需要涉及用户私人信息的数据或资源的区域,或者可能会影响用户存储的数据或其他应用程序的操作。 例如,读取用户联系人的权限是一种危险的权限。 如果应用声明它需要危险权限,则用户必须明确授予该应用的权限。 在用户批准该权限之前,您的应用无法提供依赖该权限的功能。
要使用危险权限,您的应用必须提示用户在运行时授予权限。 有关如何提示用户的更多详细信息,请参阅危险权限的请求提示。
有关危险权限的列表,请参阅下面的表1。
4、特殊权限
有一些权限不像正常和危险的权限。 SYSTEM_ALERT_WINDOW
和WRITE_SETTINGS
特别敏感,因此大多数应用程序不应使用它们。 如果应用程序需要其中一个权限,则必须在清单中声明权限,并发送请求用户授权的意图。 系统通过向用户显示详细的管理屏幕来响应意图。
有关如何请求这些权限的详细信息,请参阅SYSTEM_ALERT_WINDOW
和WRITE_SETTINGS
参考条目。
可以在Manifest.permission中找到Android系统提供的所有权限。
五、许可组
权限分组为与设备功能或功能相关的组。 在此系统下,权限请求在组级别处理,单个权限组对应于应用程序清单中的多个权限声明。 例如,SMS组包括READ_SMS和RECEIVE_SMS声明。 以这种方式对权限进行分组使用户能够做出更有意义和更明智的选择,而不会被复杂和技术许可请求所淹没。
所有危险的Android权限都属于权限组。 无论保护级别如何,任何权限都可以属于权限组。 但是,如果权限是危险的,则权限组仅影响用户体验。
如果设备运行的是Android 6.0(API级别23)且应用的targetSdkVersion为23或更高,则当您的应用请求危险权限时,以下系统行为适用:
- 如果应用程序当前在权限组中没有任何权限,系统会向用户显示描述应用程序要访问的权限组的权限请求对话框。 该对话框未描述该组中的特定权限。 例如,如果某个应用请求
READ_CONTACTS
权限,系统对话框只会说该应用需要访问该设备的联系人。 如果用户授予批准,系统将为应用程序提供其请求的权限。 - (2)如果应用程序已在同一权限组中被授予其他危险权限,则系统会立即授予权限,而不与用户进行任何交互。 例如,如果应用程序先前已请求并已获得
READ_CONTACTS
权限,然后它请求WRITE_CONTACTS
,则系统会立即授予该权限,而不向用户显示权限对话框。
警告:未来版本的
Android SDK
可能会将特定权限从一个组移动到另一个组。 因此,请勿将应用程序的逻辑基于这些权限组的结构。
例如,READ_CONTACTS
与Android 8.1(API级别27)中的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