权限的目的是保护Android用户的隐私。
Android应用必须请求访问敏感用户数据(如联系人和短信)以及某些系统功能(如相机和互联网)的权限。根据功能,系统可能会自动授予权限,或者可能会提示用户批准请求。
Android安全体系结构的核心设计点是,默认情况下,任何应用程序都无权执行任何会对其他应用程序,操作系统或用户产生负面影响的操作。这包括读取或写入用户的私人数据(如联系人或电子邮件),读取或写入其他应用程序的文件,执行网络访问,保持设备唤醒等等。
此页面概述了Android权限的工作方式,包括:如何向用户显示权限,安装时和运行时权限请求之间的区别,权限的实施方式以及权限及其组的类型。如果您只想获得使用应用程序权限的操作指南,请参阅请求应用程序权限。
权限批准
应用必须通过在应用清单中包含<uses-permission>标记来宣传其所需的权限。 例如,需要发送SMS消息的应用程序将在清单中包含以下行:
<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)或更低版本,或者应用程序的targetSdkVersion在任何版本的Android上运行时为22或更低,系统会自动要求用户在安装时为您的应用授予所有危险权限 - 时间(见图2)。
图2.安装时权限对话框
如果用户单击“接受”,则会授予应用程序请求的所有权限。 如果用户拒绝权限请求,系统将取消应用程序的安装。
如果应用更新包含对其他权限的需求,则会在更新应用之前提示用户接受这些新权限。
有关用于请求权限的建议用户体验模式的概述,请参阅应用程序权限最佳实践。
要了解如何检查和请求用户的权限,请参阅请求应用程序权限。
可选硬件功能的权限
访问某些硬件功能(如蓝牙或相机)需要应用程序权限。 但是,并非所有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:permission属性对清单中的<activity>标记应用的权限限制了谁可以启动该Activity。 在Context.startActivity()和Activity.startActivityForResult()期间检查权限。 如果调用者没有所需的权限,则从调用中抛出SecurityException。
服务权限执行
使用android:permission属性对清单中的<service>标记应用的权限限制谁可以启动或绑定到关联的服务。 在Context.startService(),Context.stopService()和Context.bindService()期间检查权限。 如果调用者没有所需的权限,则从调用中抛出SecurityException。
广播权限执行
使用android:permission属性应用于<receiver>标签的权限限制了谁可以向关联的BroadcastReceiver发送广播。 在Context.sendBroadcast()返回后检查权限,因为系统尝试将提交的广播传递给给定的接收者。 因此,权限失败不会导致异常被抛回调用者; 它只是没有提供意图。
以同样的方式,可以向Context.registerReceiver()提供权限,以控制谁可以向编程注册的接收者广播。 另一方面,在调用Context.sendBroadcast()时可以提供权限以限制允许哪些广播接收器接收广播。
请注意,接收方和广播公司都需要许可。 发生这种情况时,必须通过两个权限检查才能将意图传递给关联目标。 有关更多信息,请参阅限制具有权限的广播。
Content Provider权限执行
使用android:permission属性对<provider>标签应用的权限限制了谁可以访问ContentProvider中的数据。 (内容提供商有一个重要的额外安全设施,称为URI权限,下面将对此进行描述。)与其他组件不同,您可以设置两个单独的权限属性:android:readPermission限制谁可以从提供程序读取,以及android: writePermission限制谁可以写入它。请注意,如果提供程序受读取和写入权限保护,则仅保留写入权限并不意味着您可以从提供程序读取。
首次检索提供程序时将检查权限(如果您没有任何权限,则抛出SecurityException),以及在提供程序上执行操作时。使用ContentResolver.query()需要保持读取权限;使用ContentResolver.insert(),ContentResolver.update(),ContentResolver.delete()需要写入权限。在所有这些情况下,未保留所需的权限会导致从调用中抛出SecurityException。
URI权限
当与内容提供商一起使用时,到目前为止描述的标准许可系统通常是不够的。内容提供商可能希望使用读取和写入权限来保护自己,而其直接客户端还需要将特定URI交给其他应用程序以供其操作。
一个典型的例子是电子邮件应用程序中的附件。应该通过权限来保护对电子邮件的访问,因为这是敏感的用户数据。但是,如果向图像查看器提供图像附件的URI,则该图像查看器不再具有打开附件的权限,因为它没有理由拥有访问所有电子邮件的权限。
此问题的解决方案是per-URI权限:启动活动或将结果返回给活动时,调用者可以设置Intent.FLAG_GRANT_READ_URI_PERMISSION和/或Intent.FLAG_GRANT_WRITE_URI_PERMISSION。这授予接收活动权限访问意图中的特定数据URI,而不管它是否具有访问对应于意图的内容提供者中的数据的任何许可。
此机制允许一种通用的功能样式模型,其中用户交互(例如打开附件或从列表中选择联系人)驱动临时授予细粒度权限。这可以是将应用程序所需的权限仅限于与其行为直接相关的权限的关键工具。
要构建最安全的实现,让其他应用程序对您在yor应用程序中的操作负责,您应该以这种方式使用细粒度权限,并使用android:grantUriPermissions属性或<grant-uri-permissions>标记声明您的应用程序对它的支持。
可以在Context.grantUriPermission(),Context.revokeUriPermission()和Context.checkUriPermission()方法中找到更多信息。
其他权限执行
任何对服务的调用都可以强制执行任意细粒度的权限。 这是通过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文档中查看每个版本添加了哪些权限。
保护级别
权限分为几个保护级别。 保护级别会影响是否需要运行时权限请求。
有三种保护级别会影响第三方应用程序:正常,签名和危险权限。
正常权限
普通权限涵盖应用程序需要访问应用程序沙箱外部数据或资源的区域,但用户隐私或其他应用程序操作的风险很小。 例如,设置时区的权限是正常权限。
如果应用程序在其清单中声明它需要正常权限,则系统会在安装时自动授予应用程序该权限。 系统不会提示用户授予正常权限,用户也无法撤消这些权限。
从Android 9(API级别28)开始,以下权限被归类为PROTECTION_NORMAL:
- ACCESS_LOCATION_EXTRA_COMMANDS
- ACCESS_NETWORK_STATE
- ACCESS_NOTIFICATION_POLICY
- ACCESS_WIFI_STATE
- BLUETOOTH
- BLUETOOTH_ADMIN
- BROADCAST_STICKY
- CHANGE_NETWORK_STATE
- CHANGE_WIFI_MULTICAST_STATE
- CHANGE_WIFI_STATE
- DISABLE_KEYGUARD
- EXPAND_STATUS_BAR
- FOREGROUND_SERVICE
- GET_PACKAGE_SIZE
- INSTALL_SHORTCUT
- INTERNET
- KILL_BACKGROUND_PROCESSES
- MANAGE_OWN_CALLS
- MODIFY_AUDIO_SETTINGS
- NFC
- READ_SYNC_SETTINGS
- READ_SYNC_STATS
- RECEIVE_BOOT_COMPLETED
- REORDER_TASKS
- REQUEST_COMPANION_RUN_IN_BACKGROUND
- REQUEST_COMPANION_USE_DATA_IN_BACKGROUND
- REQUEST_DELETE_PACKAGES
- REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
- SET_ALARM
- SET_WALLPAPER
- SET_WALLPAPER_HINTS
- TRANSMIT_IR
- USE_FINGERPRINT
- VIBRATE
- WAKE_LOCK
- WRITE_SYNC_SETTINGS
签名权限
系统会在安装时授予这些应用程序权限,但仅限于尝试使用权限的应用程序使用与定义权限的应用程序相同的证书进行签名时。
注意:某些签名权限不适用于第三方应用程序。
从Android 8.1(API级别27)开始,第三方应用可以使用的以下权限归类为PROTECTION_SIGNATURE:
- BIND_ACCESSIBILITY_SERVICE
- BIND_AUTOFILL_SERVICE
- BIND_CARRIER_SERVICES
- BIND_CHOOSER_TARGET_SERVICE
- BIND_CONDITION_PROVIDER_SERVICE
- BIND_DEVICE_ADMIN
- BIND_DREAM_SERVICE
- BIND_INCALL_SERVICE
- BIND_INPUT_METHOD
- BIND_MIDI_DEVICE_SERVICE
- BIND_NFC_SERVICE
- BIND_NOTIFICATION_LISTENER_SERVICE
- BIND_PRINT_SERVICE
- BIND_SCREENING_SERVICE
- BIND_TELECOM_CONNECTION_SERVICE
- BIND_TEXT_SERVICE
- BIND_TV_INPUT
- BIND_VISUAL_VOICEMAIL_SERVICE
- BIND_VOICE_INTERACTION
- BIND_VPN_SERVICE
- BIND_VR_LISTENER_SERVICE
- BIND_WALLPAPER
- CLEAR_APP_CACHE
- MANAGE_DOCUMENTS
- READ_VOICEMAIL
- REQUEST_INSTALL_PACKAGES
- SYSTEM_ALERT_WINDOW
- WRITE_SETTINGS
- WRITE_VOICEMAIL
危险的权限
危险权限涵盖应用程序需要涉及用户私人信息的数据或资源的区域,或者可能会影响用户存储的数据或其他应用程序的操作。 例如,读取用户联系人的权限是一种危险的权限。 如果应用声明它需要危险权限,则用户必须明确授予该应用的权限。 在用户批准该权限之前,您的应用无法提供依赖该权限的功能。
要使用危险权限,您的应用必须提示用户在运行时授予权限。 有关如何提示用户的更多详细信息,请参阅危险权限的请求提示。
有关危险权限的列表,请参阅下面的表1。
特殊权限
有一些权限不像正常和危险的权限。 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权限,系统对话框只会说该应用需要访问该设备的联系人。如果用户授予批准,系统将为应用程序提供其请求的权限。
- 如果应用程序已在同一权限组中被授予其他危险权限,则系统会立即授予权限,而不与用户进行任何交互。例如,如果应用程序先前已请求并已获得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版本中发生变化。 您的代码不应具有依赖于同一组中的一组特定权限的逻辑。
表1.危险权限和权限组。
查看应用的权限
您可以使用“设置”应用程序和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