QQ空间 新浪微博 腾讯微博 微信 更多
  

安卓基础:应用权限

25安卓基础:应用权限

权限概述

目录Permission approvalRequest prompts for dangerous permissionsRequest prompts to access sensitive user informationPermissions for optional hardware features

权限 的目的是保护Android用户的隐私。Android应用必须获得访问敏感用户数据(例如联系人和SMS)以及某些系统功能(例如摄像头和互联网)的权限。根据功能的不同,系统可能会自动授予权限,或者可能提示用户批准请求。

Android安全体系结构的中心设计要点是,默认情况下,没有任何应用程序有权执行任何会对其他应用程序,操作系统或用户产生不利影响的操作。这包括读取或写入用户的私人数据(例如联系人或电子邮件),读取或写入其他应用程序的文件,执行网络访问,使设备保持苏醒等。

该页面概述了Android权限的工作方式,包括:如何向用户显示权限,安装时和运行时权限请求之间的区别,权限的执行方式以及权限的类型及其组。如果您只想要使用应用程序权限的使用指南,请参阅“ 请求应用程序权限”

权限批准

应用程序必须通过 应用程序清单中包含标签 来公开其所需的权限 。例如,需要发送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,右)。

img

图1.初始许可对话框(左)和辅助许可请求,带有关闭其他请求的选项(右)

如果用户选中“ 不再询问”框并点击 “拒绝”,则系统不再提示用户是否在以后尝试请求相同的权限。

即使用户向您的应用授予了它请求的权限,您也不能总是依靠它。用户还可以选择在系统设置中一对一地启用和禁用权限。您应该始终在运行时检查并请求权限,以防止运行时错误(SecurityException)。

有关如何处理运行时权限请求的详细信息,请参阅“ 请求应用程序权限”

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

如果设备运行的是Android 5.1.1(API级别22)或更低版本,或者targetSdkVersion 在任何版本的Android上运行时 应用程序的版本为22或更低版本,系统会在安装时自动要求用户授予您的应用程序所有危险权限(见图2)。

img

图2.安装时权限对话框

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

如果应用程序更新包括对其他权限的需求,则会提示用户在更新应用程序之前接受这些新权限。

有关建议的请求权限的用户体验模式的概述,请参阅“ 应用程序权限最佳实践”

要了解如何检查用户的权限并向其请求权限,请参阅“ 请求应用程序权限”

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

某些应用程序依赖对与呼叫日志和SMS消息相关的敏感用户信息的访问。如果要请求特定于呼叫日志和SMS消息的权限并将应用程序发布到Play商店,则必须提示用户将应用程序设置为核心系统功能的默认处理程序,然后再请求这些运行时权限。

有关默认处理程序的更多信息,包括有关向用户显示默认处理程序提示的指南请参阅仅在默认处理程序中使用的权限指南

可选硬件功能的权限

要访问某些硬件功能(例如蓝牙或摄像头),必须获得应用程序许可。但是,并非所有的Android设备实际上都具有这些硬件功能。因此,如果您的应用请求该 CAMERA权限,则还必须``在清单中包含标记,以声明是否确实需要此功能,这一点很重要 。例如:

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

如果您声明android:required="false"使用该功能,则Google Play允许将您的应用安装在不具有该功能的设备上。然后,您必须通过调用来检查当前设备在运行时是否具有该功能 PackageManager.hasSystemFeature(),并在该功能不可用时优雅地禁用该功能。

如果您未提供 ](https://developer.android.com/guide/topics/manifest/uses-feature-element)标签,则当Google Play看到您的应用请求相应的权限时,即表示您的应用需要此功能。因此,它会从没有功能的设备中过滤您的应用,就像您`android:required="true"`在[标记中声明 的一样 。

有关更多信息,请参阅 Google Play和基于功能的过滤

权限执行

权限不仅用于请求系统功能。应用程序提供的服务可以强制执行自定义权限,以限制谁可以使用它们。有关声明自定义权限的更多信息,请参见定义自定义应用程序权限

活动许可执行

使用android:permission属性对清单中的标签应用的权限会限制谁可以启动该标签。在和 期间检查许可 。如果呼叫者没有所需的权限,则会 从呼叫中抛出。 [``](https://developer.android.com/guide/topics/manifest/activity-element)`Activity``Context.startActivity()``Activity.startActivityForResult()``SecurityException`

服务许可执行

使用android:permission属性应用于清单中标记的权限限制了谁可以启动或绑定到相关的权限。在权限检查 , 和 。如果呼叫者没有所需的权限,则会从呼叫中抛出。 [``](https://developer.android.com/guide/topics/manifest/service-element)Service``Context.startService()``Context.stopService()``Context.bindService()``SecurityException

广播许可执行

使用android:permission属性对标签应用的权限限制了谁可以将广播发送到关联的。返回会检查权限,因为系统会尝试将提交的广播传递给给定的接收者。结果,权限失败不会导致异常被抛出给调用者。它只是不提供。 [``](https://developer.android.com/guide/topics/manifest/receiver-element)BroadcastReceiver `Context.sendBroadcast()``Intent`

以相同的方式,可以提供许可Context.registerReceiver()以控制谁可以广播到以编程方式注册的接收器。反之,当呼叫Context.sendBroadcast()以限制允许哪些广播接收者接收广播时,可以提供许可。

请注意,接收者和广播者都可能需要许可。发生这种情况时,必须通过两次权限检查,才能将意图传递给关联的目标。有关更多信息,请参见 使用权限限制广播

内容提供者权限执行

使用android:permission属性对标记应用的权限限制了谁可以访问内的数据 。(内容提供者有一个重要的附加安全工具可供使用,称为 URI权限,下面将对其进行介绍。)与其他组件不同,可以设置两个单独的许可权属性: 限制可以从提供者读取的人,以及限制可以向提供 者进行写信的人。它。请注意,如果提供者同时受到读和写权限的保护,则仅持有写许可并不意味着您可以从提供者中进行读取。 ``ContentProvider android:readPermission android:writePermission

首次检索提供程序时会检查权限(如果您没有任何权限,SecurityException 则会引发a),并在提供程序上执行操作时会检查权限。使用 ContentResolver.query()需要拥有读取权限;使用 ContentResolver.insert()ContentResolver.update()ContentResolver.delete() 需要写权限。在所有这些情况下,未持有所需的许可权将导致SecurityException呼叫被抛出。

URI权限

到目前为止,与内容提供者一起使用时,到目前为止描述的标准许可系统通常是不够的。内容提供商可能希望通过读写权限来保护自己,而其直接客户端还需要将特定的URI交给其他应用程序才能进行操作。

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

该问题的解决方案是按URI权限:启动活动或将结果返回活动时,调用者可以设置 Intent.FLAG_GRANT_READ_URI_PERMISSION和/或 Intent.FLAG_GRANT_WRITE_URI_PERMISSION。这授予接收活动许可访问该意图中的特定数据URI,而不管它是否有权访问与该意图相对应的内容提供程序中的数据。

这种机制允许使用通用的功能样式模型,其中用户交互(例如打开附件或从列表中选择联系人)驱动细粒度权限的临时授予。这可能是一种关键功能,用于将应用程序所需的权限减少到仅与行为直接相关的权限。

为了构建最安全的实现,使其他应用对其应用中的操作负责,您应该以这种方式使用细粒度的权限,并使用属性或 标记声明应用对其的支持 。 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会添加权限。

例如,READ_EXTERNAL_STORAGE 从API级别19开始强制执行该权限,以限制对共享存储空间的访问。如果您 targetSdkVersion未满18岁,则此权限会添加到较新版本的Android上的应用中。

警告:如果将权限自动添加到您的应用程序,即使您的应用程序实际上不需要它们,您在Google Play上的应用程序列表也会列出这些附加权限。为避免这种情况并删除不需要的默认权限,请始终将其更新 targetSdkVersion为尽可能高的级别。您可以在Build.VERSION_CODES文档中查看每个发行版中添加了哪些权限。

防护等级

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

有三种影响第三方应用程序的保护级别: 正常签名危险权限。要查看特定权限具有的保护级别,请访问 权限API参考页

普通权限

普通权限涵盖了您的应用需要访问其沙盒外部的数据或资源的区域,但这些区域对用户的隐私或其他应用的操作造成的风险很小。例如,设置时区的权限是普通权限。

如果某个应用程序在其清单中声明需要正常权限,则系统会在安装时自动向该应用程序授予该权限。系统不会提示用户授予普通权限,并且用户无法撤消这些权限。

签名权限

系统会在安装时授予这些应用程序权限,但仅当尝试使用权限的应用程序与定义该权限的应用程序使用相同的证书签名时。

注意:某些签名权限不适用于第三方应用程序。

危险权限

危险权限涵盖应用程序需要涉及用户私人信息的数据或资源的区域,或可能影响用户存储的数据或其他应用程序的操作的区域。例如,读取用户联系人的功能是危险的权限。如果应用程序声明需要危险的权限,则用户必须向该应用程序明确授予该权限。在用户批准许可之前,您的应用无法提供依赖于该许可的功能。

要使用危险权限,您的应用必须提示用户在运行时授予权限。有关如何提示用户的更多详细信息,请参阅 请求危险权限提示

特殊权限

有一些权限的行为与正常和危险权限不同。SYSTEM_ALERT_WINDOW并且WRITE_SETTINGS特别敏感,因此大多数应用都不应使用它们。如果应用程序需要这些权限之一,则它必须在清单中声明该权限,发送请求用户授权的意图。系统通过向用户显示详细的管理屏幕来响应此意图。

有关如何请求这些权限的详细信息,请参见SYSTEM_ALERT_WINDOWWRITE_SETTINGS参考条目。

Android系统提供的所有权限都可以在找到 Manifest.permission

权限组

权限分为与设备功能或功能相关的组。在此系统下,权限请求在组级别处理,并且单个权限组对应于应用清单中的多个权限声明。例如,SMS组同时包含READ_SMSRECEIVE_SMS声明。通过这种方式对权限进行分组,使用户能够做出更有意义和更明智的选择,而不会因复杂而技术性的权限请求而烦恼。

img

所有危险的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

...

-g在模拟器或测试设备上安装应用程序时,还可以使用adb 选项自动授予所有权限:
$ adb shell install -g MyApp.apk

额外资源

  • 请求应用程序权限:如何在应用程序中请求权限的指南。
  • 隐含功能要求的权限:有关如何请求某些权限的信息将您的应用程序隐式地限制为包含相应硬件或软件功能的设备。
  • ``:清单标签的API参考,用于声明您的应用所需的权限。
  • 设备兼容性:有关Android如何在不同类型的设备上工作的信息,以及如何为每个设备优化应用程序或将应用程序的可用性限制为不同设备的介绍。
  • Android安全概述:有关Android平台的安全模型的详细讨论。
  • “妈妈,我可以吗?” 要求权限:来自Android Dev Summit 2015的视频介绍了请求权限的最佳做法。
  • Android M权限:来自Google I / O 2015的视频介绍了对Android 6.0中的权限模型所做的更改。

请求应用权限

目录向清单添加权限检查权限请求权限解释为什么应用需要权限

每款 Android 应用都在访问受限的沙盒中运行。如果应用需要使用其自己的沙盒外的资源或信息,则必须请求相应权限。 要声明您的应用需要某项权限,您可以在应用清单中列出该权限,然后在运行时请求用户批准每项权限(适用于 Android 6.0 及更高版本)。

本页介绍如何使用 Android 支持库来检查和请求权限。Android 框架从 Android 6.0(API 级别 23)开始提供类似的方法,但如果使用支持库,将会更容易与较低的 Android 版本实现兼容性。

向清单添加权限

对于所有 Android 版本,要声明应用需要某项权限,请在应用清单中添加 uses-permisson 元素,作为顶级 manifest 元素的子项。例如,需要访问互联网的应用需在清单中添加以下代码行:

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

        <uses-permission android:name="android.permission.INTERNET"/>
        <!-- other permissions go here -->

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

系统在您声明权限之后的行为取决于权限的敏感程度。有些权限被视为“常规”权限,因此系统会在安装应用时立即授予这些权限。还有些则被视为“危险”权限,因此需要用户明确向您的应用授予相应访问权限。如需详细了解不同类型的权限,请参阅保护级别

检查权限

如果应用需要一项危险权限,那么每次执行需要该权限的操作时,您都必须检查自己是否具有该权限。从 Android 6.0(API 级别 23)开始,用户可随时从任何应用撤消权限,即使应用以较低的 API 级别为目标平台也是如此。因此,即使应用昨天使用了相机,也不能认为它今天仍具有该权限。

要检查您是否具有某项权限,请调用 ContextCompat.checkSelfPermission() 方法。例如,以下代码段展示了如何检查 Activity 是否具有向日历写入数据的权限:

    if(ContextCompat.checkSelfPermission(thisActivity,Manifest.permission.WRITE_CALENDAR)
            != PackageManager.PERMISSION_GRANTED){
        //未授予权限
    }
    

如果应用具有此权限,该方法将返回 PERMISSION_GRANTED,并且应用可以继续操作。如果应用不具备此权限,该方法将返回 PERMISSION_DENIED,且应用必须明确要求用户授予权限。

请求权限

当您的应用从 checkSelfPermission() 收到 PERMISSION_DENIED 时,您需要提示用户授予该权限。Android 为您提供了几种可用来请求权限的方法(如 requestPermissions()),如下面的代码段所示。调用这些方法时,会显示一个无法自定义的标准 Android 对话框。

如何向用户显示此内容取决于设备的 Android 版本以及应用的目标版本,如权限概览中所述。

解释为什么应用需要权限

在某些情况下,您需要帮助用户理解为什么您的应用需要某项权限。例如,如果用户启动一款摄影应用,用户或许不会对该应用请求使用相机的权限感到惊讶,但用户可能不理解为什么该应用想要访问用户的位置或联系人。在您的应用请求权限之前,不妨考虑向用户提供解释。请注意,您肯定不希望您的解释让用户不胜其烦;如果您提供太多解释,用户可能会觉得这款应用很麻烦,因而将其移除。

您可以使用的一种方法是只在用户之前拒绝过该权限请求的情况下才提供解释。Android 提供了一种实用程序方法,即 shouldShowRequestPermissionRationale()。如果用户之前拒绝了该请求,该方法将返回 true;如果用户之前拒绝了某项权限并且选中了权限请求对话框中的不再询问选项,或者如果设备政策禁止该权限,该方法将返回 false

如果用户不断尝试使用需要某项权限的功能,但一直拒绝权限请求,这或许意味着,用户不理解为什么应用需要该权限才能提供这项功能。在这种情况下,显示解释或许是不错的做法。

应用权限最佳做法中提供了更多建议,指导您如何在请求权限时创造良好的用户体验。

在必要时请求成为默认处理程序

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

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

请求您需要的权限

如果您的应用还不具备它需要的权限,那么它必须调用一个 requestPermissions() 方法来请求相应权限。您的应用将传递它想要的权限,以及您指定用于识别此权限请求的整数请求代码。此方法异步运行。它会立即返回结果,并且在用户响应提示后,系统会利用相应结果来调用应用的回调方法,在调用过程中传递的请求代码与应用传递给 requestPermissions() 的请求代码相同。

以下代码将检查应用是否具有读取用户联系人的权限。如果没有该权限,它会检查是否应显示需要该权限的解释,如果不需要解释,它会请求该权限:

    // Here, thisActivity is the current activity
    if (ContextCompat.checkSelfPermission(thisActivity,
            Manifest.permission.READ_CONTACTS)
            != PackageManager.PERMISSION_GRANTED) {

        // Permission is not granted
        // Should we show an explanation?
        if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
                Manifest.permission.READ_CONTACTS)) {
            // Show an explanation to the user *asynchronously* -- don't block
            // this thread waiting for the user's response! After the user
            // sees the explanation, try again to request the permission.
        } else {
            // No explanation needed, we can request the permission.
            ActivityCompat.requestPermissions(thisActivity,
                    arrayOf(Manifest.permission.READ_CONTACTS),
                    MY_PERMISSIONS_REQUEST_READ_CONTACTS)

            // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
            // app-defined int constant. The callback method gets the
            // result of the request.
        }
    } else {
        // Permission has already been granted
    }
    

系统显示的提示说明了您的应用需要访问的权限组,而不是具体权限。

注意:当您的应用调用 requestPermissions() 时,系统会向用户显示一个标准对话框。您的应用无法配置或更改该对话框。如果您需要向用户提供任何信息或解释,您应该在调用 requestPermissions() 之前这样做,如解释为什么应用需要权限中所述。

处理权限请求响应

当用户响应您应用的权限请求时,系统会调用您应用的 onRequestPermissionsResult() 方法,在调用过程中向其传递用户响应。您的应用必须替换该方法,以查明是否已向其授予相应权限。在回调过程中传递的请求代码与传递给 requestPermissions() 的请求代码相同。例如,如果应用请求 READ_CONTACTS 访问权限,则它可能采用以下回调方法:

    override fun onRequestPermissionsResult(requestCode: Int,
            permissions: Array<String>, grantResults: IntArray) {
        when (requestCode) {
            MY_PERMISSIONS_REQUEST_READ_CONTACTS -> {
                // If request is cancelled, the result arrays are empty.
                if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                    // permission was granted, yay! Do the
                    // contacts-related task you need to do.
                } else {
                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                }
                return
            }

            // Add other 'when' lines to check for other
            // permissions this app might request.
            else -> {
                // Ignore all other requests.
            }
        }
    }
    

系统显示的对话框说明了您的应用需要访问的权限组,它不会列出具体权限。例如,如果请求 READ_CONTACTS 权限,系统对话框只会说明您的应用需要访问设备的联系人。用户只需要针对每个权限组授予一次权限。如果您的应用请求该组中的其他任何权限(已在您的应用清单中列出),系统会自动授予这些权限。当您请求权限时,系统会调用您的 onRequestPermissionsResult() 回调方法并传递 PERMISSION_GRANTED,就像用户通过系统对话框明确同意了您的请求时的处理方式一样。

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

例如,假设您在应用清单中同时列出了 READ_CONTACTSWRITE_CONTACTS。如果您请求 READ_CONTACTS 且用户授予了该权限,那么当您随后又请求 WRITE_CONTACTS 时,系统会立即授予您该权限,而无需与用户交互。

如果用户拒绝了权限请求,您的应用应采取适当的措施。例如,您的应用可能会显示一个对话框,解释它为什么无法执行用户请求的需要该权限的操作。

当系统要求用户授予某项权限时,用户可以选择告知系统不要再次请求该权限。在这种情况下,只要应用使用 requestPermissions() 再次请求该权限,系统就会立即拒绝请求。系统会调用您的 onRequestPermissionsResult() 回调方法并传递 PERMISSION_DENIED,就像用户再次明确拒绝了您的请求时的处理方式一样。如果设备政策禁止应用具有该权限,该方法也会返回 false。这意味着,当您调用 requestPermissions() 时,您不能假定已经发生与用户的任何直接交互。

要在请求应用权限时提供最佳用户体验,另请参阅应用权限最佳做法

按 API 级别声明权限

要仅在支持运行时权限的设备(即运行 Android 6.0(API 级别 23)或更高版本的设备)上声明某项权限,请添加 uses- permission-sdk-23 标记,而不是 uses-permission 标记。

使用这些标记中的任意一个时,您均可设置 maxSdkVersion 属性,以指定在运行更高版本的设备上不需要特定权限。

其他资源

如需详细了解权限,请阅读以下文章:

如需详细了解如何请求权限,请下载以下示例应用:

应用权限最佳做法

目录使用 Android 权限的原则Android 6.0+ 中的权限需要成为默认处理程序的权限避免请求不必要的权限

权限请求可以保护设备上的敏感信息,仅在需要访问信息以使应用正常工作时才应使用。利用本文档提供的技巧,您可能无需请求访问此类信息即可实现相同(或更好)的功能;但本文不会详细讨论权限在 Android 操作系统中的工作方式。

要比较笼统地了解 Android 权限,请参阅权限概述。要详细了解如何在代码中使用权限,请参阅请求应用权限

使用 Android 权限的原则

使用 Android 权限时,我们建议遵循以下原则:

#1:仅使用应用正常工作所需的权限。根据您使用权限的方式,您可以通过其他方式执行所需的操作(系统 intent、标识符、电话后台处理),而无需依赖于访问敏感信息。

#2: 注意库所需的权限。 包含某个库时,您也会继承它的权限要求。您应了解正要包含的库、它们需要的权限以及这些权限的用途。

#3:公开透明。 请求权限时,请清晰说明您要访问的内容以及访问原因,以便用户可以做出明智的决策。在请求权限时(包括安装、运行时或更新权限对话框)列出这些信息。

#4:让系统以显式方式访问。 在访问敏感功能(如摄像头或麦克风)时提供连续指示,让用户知道您在收集数据,避免让他们认为您在暗自收集数据。

本指南其余部分将以开发 Android 应用为背景详细介绍这些规则。

Android 6.0+ 中的权限

Android 6.0 Marshmallow 引入了一个新的权限模式,让应用可以在运行时而不是安装之前向用户请求权限。支持这种新模式的应用会在应用确实需要相关服务或这些服务保护的数据时才请求权限。尽管这不会(不一定会)改变整体应用行为,但会给敏感用户数据的处理方式带来一些变化:

增加了情境上下文:系统会在运行时在应用的上下文中提示用户提供访问相关权限组涵盖的功能所需的权限。用户对请求权限的上下文更加敏感,如果您请求的权限与应用的用途不匹配,则一定要向用户详细解释您为什么请求此权限;您应尽可能在请求时以及后续对话框中(如果用户拒绝请求)解释您的请求。

在授予权限时更加灵活:用户可以在收到请求时以及在设置中拒绝访问各个权限,但是当功能因此而中断时,他们可能仍会感到惊讶。最好监控有多少用户拒绝权限请求(例如,使用 Google Analytics(分析)),以便重构应用以避免依赖该权限,或更好地解释应用需要此权限才能正常工作的原因。您还应确保应用可以处理当用户拒绝权限请求或在设置中关闭权限时产生的异常。

增加了事务负担:系统将要求用户单独授予权限组的访问权限,而不是以集合的形式授予。这样,最大程度降低请求的权限数量就变得非常重要,因为数量多会增加用户授予权限的负担,并且会增大至少有一个请求被拒绝的概率。

需要成为默认处理程序的权限

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

如需有关默认处理程序的更多信息,包括有关向用户显示默认处理程序提示的指南,请参阅有关仅在默认处理程序中使用的权限的指南

避免请求不必要的权限

每次您请求某个权限时,都是在强迫用户做出决定。您应尽量减少提出这些请求的次数。如果用户运行的是 Android 6.0(API 级别 23)或更高版本,则每次用户尝试一些请求权限的新应用功能时,应用都必须中断用户的操作而发起权限请求。如果用户运行的是较低版本的 Android,则在安装应用时必须授予应用每一种权限;如果列表过长或看起来不合适,用户可能会决定根本不安装应用。因此,应尽量减少应用需要的权限数量。

本部分提供了常见用例的替代方法,有助于限制您提出权限请求的次数。由于向用户请求的权限数量和类型会影响下载量(与其他请求较少权限的类似应用相比),因此最好避免为不必要的功能请求权限。

改用 Intent

在许多情况下,要让应用执行某项任务,有两种方法供您选择。应用可以要求提供权限来自行执行该任务,也可以使用 intent 让其他应用执行该任务。

例如,假设应用需要使用设备摄像头才能够拍摄照片。应用可以请求 CAMERA 权限,以便允许应用直接访问摄像头。然后,应用将使用摄像头 API 控制摄像头并拍摄照片。此方法使应用能够完全控制拍摄过程,并且您可以将摄像头界面整合到应用中。

不过,如果您很少需要访问用户数据,换句话说,每次您需要访问数据时都向用户显示运行时对话框,这种中断操作并非不可接受,那么您可以使用基于 intent 的请求。Android 提供了一些系统 intent,借助这些 intent,应用无需请求权限,因为在发出基于 intent 的请求时用户会选择与应用共享的内容(如果有)。

例如,您可以使用 intent 操作类型 MediaStore.ACTION_IMAGE_CAPTUREMediaStore.ACTION_VIDEO_CAPTURE 来拍摄图像或视频,而无需直接使用 Camera 对象(或请求权限)。在这种情况下,每次拍摄图像时,系统 intent 都会代表您请求用户提供权限。

同样,如果您需要拨打电话、访问用户的联系人或执行其他操作,您可以通过创建适当的 intent 来完成,也可以直接请求权限并访问相应的对象。每种方法各有优缺点。

如果使用权限:

  • 当您执行操作时,您的应用可以完全控制用户体验。不过,如此广泛的控制会增加代码的复杂性,因为您需要设计适当的界面。
  • 系统会在运行时或安装时(具体取决于用户的 Android 版本)提示用户授予权限一次。之后,应用即可执行操作,不再需要用户进行其他互动。不过,如果用户未授予权限(或之后撤消权限),则应用将根本无法执行操作。

如果使用 intent:

  • 您不必为操作设计界面。处理 intent 的应用将提供界面。
  • 用户可以使用他们首选的应用执行任务。例如,用户可以选择用他们喜爱的照片应用拍照。
  • 如果用户没有适用于操作的默认应用,则系统会提示用户选择一款应用。如果用户未指定默认处理程序,则他们每次执行此操作时都可能必须处理一个额外的对话框。

不要让用户感到无所适从

如果用户运行的是 Android 6.0(API 级别 23)或更高版本,则用户必须在运行应用时为其授予权限。如果您让用户一次面对大量权限请求,可能会使用户感到无所适从,导致他们退出您的应用。您应根据需要请求权限。

在某些情况下,一项或几项权限可能对您的应用来说必不可少。在这种情况下,合理的做法是,在应用启动之后立即请求提供所有这些权限。例如,如果您创建的是摄影应用,则该应用将需要访问设备的摄像头。当用户首次启动该应用时,系统会要求他们提供摄像头使用权限,这不会令他们感到惊讶。但是,如果同一应用还具备与用户的联系人分享照片的功能,那么您或许不应在应用首次启动时请求用户提供 READ_CONTACTS 权限,而应等到用户尝试使用“分享”功能之后再请求该权限。

如果应用提供教程,则合理的做法是,在教程结束时请求提供应用的必要权限。

失去音频焦点后暂停媒体

在这种情况下,用户接到电话时您的应用需要转入后台,只有在通话停止后才会重新获得焦点。

出现此类情况(例如,媒体播放器在通话期间静音或暂停)时,通常采用的方法是使用 PhoneStateListener 或监听 android.intent.action.PHONE_STATE 的广播,以监听通话状态有无变化。这种解决方法的问题是它需要 READ_PHONE_STATE 权限,这将强制用户授予对广泛的敏感数据(如用户的设备和 SIM 硬件 ID 以及来电的电话号码)的访问权限。此外,当应用在 Android 10(API 级别 29)或更高版本上运行,LISTEN_CELL_LOCATIONLISTEN_CELL_INFO 事件需要位置权限;尤其而言,当应用以 Android 10 或更高版本为目标时将需要 ACCESS_FINE_LOCATION

您可以通过为应用请求 AudioFocus,在没有 READ_PHONE_STATEMODIFY_PHONE_STATE 权限的情况下检测用户是否在通话中,这么做不需要显式权限,因为它不访问敏感信息。只需将对音频放入后台所需的代码放入 onAudioFocusChange() 事件处理程序,当操作系统转换其音频焦点时,它将自动运行。要详细了解如何执行此操作,请参阅此文档

确定正在运行实例的设备

在这种情况下,您需要一个唯一标识符来确定您的应用实例正在哪个设备上运行。

应用可能具有设备特定的偏好设置或消息(例如,在云端为用户保存设备特定的播放列表,以便他们在车上和家里可以有不同的播放列表)。常见的解决方案是利用设备标识符(如 Device IMEI),但这需要 Device ID and call information 权限组(M+ 中为 PHONE)。它还使用一个无法重置且在所有应用之间共享的标识符。

下面两种方法可以替代这些类型的标识符:

  1. 使用 com.google.android.gms.iid InstanceID API。getInstance(Context context).getID() 将为您的应用实例返回一个唯一设备标识符。结果得到一个应用实例作用域标识符,在存储与应用有关的信息时可以将该标识符用作密钥,在用户重新安装应用时此标识符将重置。
  2. 使用 randomUUID() 之类的基本系统函数创建您自己的标识符,其作用域限定为应用的存储空间。

为广告或用户分析创建唯一标识符

在这种情况下,您需要一个唯一标识符来为没有登录您的应用的用户构建个人资料(例如,用于广告定位或衡量转化率)。

为广告和用户分析构建个人资料有时需要一个在其他应用之间共享的标识符。此问题的常见解决方案需要利用设备标识符(如 Device IMEI),这需要 Device ID and call information 权限组(API 级别 23+ 中为 PHONE),并且无法由用户重置。无论是上述哪种情况,除了使用不可重置的标识符并请求用户可能认为不寻常的权限外,还会违反 Play 开发者计划政策

遗憾的是,在这些情况下,使用 com.google.android.gms.iid InstanceID API 或系统函数创建应用作用域 ID 并不是适当的解决方案,因为可能需要在应用之间共享该 ID。一种替代解决方案是通过 getId() 方法使用 AdvertisingIdClient.Info 类提供的 Advertising Identifier。您可以使用 getAdvertisingIdInfo(Context) 方法创建一个 AdvertisingIdClient.Info 对象,并调用 getId() 方法来使用该标识符。请注意,此方法会造成堵塞,因此,您不应从主线程调用它;有关此方法的详细说明请查看此处

了解您正在使用的库

有时,您在应用中使用的库需要一些权限。例如,广告和分析库可能需要访问 LOCATION 权限组以实现必需的功能。但从用户的角度来看,权限请求来自于您的应用,而不是库。

就像用户会选择使用较少权限即可实现相同功能的应用一样,开发者也应检查他们的库,并选择不会使用非必要权限的第三方 SDK。例如,如果您使用的库提供了定位功能,请确保您不会请求 FINE_LOCATION 权限,除非您要使用基于位置的定位功能。

解释为何需要权限

系统在您调用 requestPermissions() 时显示的权限对话框将说明应用需要哪些权限,但不会解释为何需要这些权限。在某些情况下,用户可能会感到困惑。最好在调用 requestPermissions() 之前向用户解释应用需要相应权限的原因。

研究表明,如果用户知道应用需要相应权限的原因,他们会更容易接受权限请求。用户研究表明:

用户是否愿意为某个移动应用授予给定权限,在很大程度上受此类权限关联用途的影响。例如,用户是否愿意授予访问其位置的权限取决于该权限请求是否为支持应用的核心功能所必需,或者应用是否会与广告网络或分析公司分享此信息。1

卡内基梅隆大学 (CMU) 的 Jason Hong 教授根据他所带领的小组的研究成果得出一个一般结论:

与只是告诉用户应用正在使用其位置相比,如果用户知道应用为什么使用像他们的位置这样敏感的信息(例如,用于定向广告),那么用户会更容易接受。1

因此,如果您仅使用归入权限组的一小部分 API 调用,明确列出您使用哪些权限以及使用原因会非常有用。例如:

  • 如果您仅使用粗略位置,请在应用说明或应用帮助文档中告知用户。

  • 如果您需要访问短信以接收身份验证码,从而防止用户被欺诈,请在应用说明中和/或首次访问数据时告知用户。

    注意:如果应用面向 Android 8.0(API 级别 26)或更高版本,请不要在验证用户凭据过程中请求 READ_SMS 权限,而应使用 createAppSpecificSmsToken() 生成应用特定的令牌,然后将此令牌传递给可以发送验证短信的其他应用或服务。

在特定条件下,让用户实时了解应用在访问敏感数据也是非常有益的。例如,如果应用要访问摄像头或麦克风,通常最好在应用中的某个位置或在通知托盘中(如果应用正在后台运行)使用通知图标告知用户,这样不会让您看起来像是在暗自收集数据。

最后,如果您需要请求权限以便在应用中运行某项功能,但用户不清楚原因,则需要找到一种方法让用户知道您为什么需要最敏感的权限。

测试两种权限模式

自 Android 6.0(API 级别 23)起,用户在运行时(而不是在安装应用时)授予和撤消应用权限。因此,您必须在多种不同条件下测试应用。在低于 Android 6.0 的版本中,您可以合理地认为,如果应用能运行,它就已经获得在应用清单中声明的全部权限。自 Android 6.0 起,用户可以开启或关闭任何应用的权限,即使面向 API 级别 22 或更低级别的应用也是如此。您应测试以确保您的应用能正常运行,无论它是否具有任何权限。

以下提示可帮助您在搭载 API 级别 23 或更高级别的设备上找出与权限有关的代码问题:

  • 确定应用的当前权限和相关的代码路径。

  • 在各种受权限保护的服务和数据中测试用户流。

  • 使用授予或撤消权限的各种组合进行测试。例如,相机应用可能会在其清单中列出 CAMERAREAD_CONTACTSACCESS_FINE_LOCATION。您应在测试该应用时逐一开启和关闭这些权限,确保应用可以妥善处理所有权限配置。

  • 使用

    abd

    工具从命令行管理权限:

    • 按组列出权限和状态:

      $ adb shell pm list permissions -d -g
      
    • 授予或撤消一项或多项权限:

      $ adb shell pm [grant|revoke] <permission-name> ...
      
  • 针对使用权限的服务对应用进行分析。

其他资源

仅在默认处理程序中使用的权限

目录查看和更改默认处理程序集遵循针对默认处理程序的要求征求用户同意

注意:本指南主要面向准备在 Google Play 商店发布应用的 Android 应用开发者。不过,无论您在哪里发布 Android 应用,为了保护用户隐私,最好都完成本页面中所述的任务。

多种核心设备功能(例如,读取通话记录和发送短信)都需要访问敏感用户信息。为了保护用户隐私并让用户更好地控制他们为设备上的应用提供的信息,Google Play 会限制应用对与通话和短信相关的权限组的访问权。

如果您在 Google Play 商店分发应用,并想要访问与通话记录和短信相关的敏感用户信息,则您的应用需要注册为与该权限相关的核心设备功能的用户默认处理程序,除非应用满足 Play 管理中心帮助中心内显示的任意一种例外情况。例如,要访问与通话相关的权限,您的应用需要注册为用户的默认电话或 Google 助理处理程序,除非应用满足某种例外情况。

本指南简要概述了用户如何访问搭载 Android 的设备上的默认处理程序;然后介绍了应用必须满足哪些要求才有资格成为默认处理程序;最后详细展示了应用如何征求用户同意以成为默认处理程序。

要详细了解默认处理程序以及如何处理 Play 商店提供的应用内的权限,请参阅“权限”政策指南

查看和更改默认处理程序集

Android 为多种核心使用场景(例如,拨打电话、发送短信和提供辅助技术功能)提供了默认处理程序。

Android 上的“设置”应用的一个屏幕向用户显示了哪些应用目前正在充当设备核心功能的默认处理程序,如图 1 所示。用户可以在此屏幕更改指定功能的默认处理程序,如图 2 所示。

默认应用设置的屏幕截图图 1. 显示设备上的默认处理程序列表的系统设置屏幕

默认短信应用设置的屏幕截图图 2. 显示如何更改默认短信处理程序的系统设置屏幕

遵循针对默认处理程序的要求

鉴于应用在充当默认处理程序时会访问敏感的用户信息,因此,只有满足以下 Play 商品详情和核心功能要求的应用才可成为默认处理程序:

  • 应用必须能够执行其作为默认处理程序所负责的功能。例如,默认短信处理程序应该能够发送短信。
  • 应用必须提供隐私权政策。
  • 应用必须在 Play 商店说明中清晰阐述其核心功能。例如,默认电话处理程序应在说明中描述其与电话相关的功能。
  • 应用必须声明与其使用场景相符的权限。如需详细了解指定处理程序可声明哪些权限,请参阅 Play 管理中心帮助内有关如何使用短信或通话记录权限组的指南
  • 应用必须先请求成为默认处理程序,然后才能请求与成为该处理程序相关的权限。例如,应用必须先请求成为默认的短信处理程序,然后才能请求 READ_SMS 权限。

征求用户同意

在确保应用满足成为默认处理程序所需的各项要求之后,您可以添加逻辑,以显示如图 3 所示的对话框。此对话框要求用户针对特定使用场景将您的应用设置为默认处理程序。

注意:应用必须先请求成为默认处理程序,然后才能请求与成为该处理程序相关的权限。例如,应用必须先请求成为默认的短信处理程序,然后才能请求 READ_SMS 权限。

以下示例代码展示了显示询问用户是否同意更改设备默认短信处理程序的提示所需的逻辑:

KOTLINJAVA

    val setSmsAppIntent = Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT)
    setSmsAppIntent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, packageName)
    startActivityForResult(setSmsAppIntent, your-result-code)
    

显示面向用户的对话框的屏幕截图图 3. 询问用户是否要更改设备的默认短信处理程序的提示

定义自定义应用权限

目录背景应用签名用户 ID 和文件访问权限定义并强制执行权限创建权限组自定义权限建议

本文档介绍了应用开发者如何使用 Android 提供的安全功能来定义自己的权限。通过定义自定义权限,应用可以与其他应用共享其资源和功能。如需详细了解权限,请参阅权限概览

背景

Android 是一种权限分离的操作系统,其中每个应用都以不同的系统身份(Linux 用户 ID 和组 ID)运行。系统的各个部分也会被分隔为不同的身份。因此,Linux 可以将应用同其他应用和系统隔离开来。

应用可以定义其他应用可以请求的权限,从而将自己的功能提供给后者。它们还可以定义能够自动提供给已使用同一证书进行签名的任何其他应用的权限。

应用签名

所有 APK 都必须使用私钥由其开发者持有的证书进行签名。此证书可标识应用创作者。证书无需由证书授权机构进行签名;Android 应用完全可以使用自签名证书,这种做法也十分普遍。Android 中的证书旨在区分应用创作者。这样,系统可以授予或拒绝应用对签名级权限的访问权限,以及授予或拒绝应用获取与另一应用相同的 Linux 身份的请求

用户 ID 和文件访问权限

安装时,Android 会为每个软件包提供不同的 Linux 用户 ID。该身份在相应软件包在该设备上存续期间将保持不变。同一软件包在其他设备上可能具有不同的 UID;重要的是每个软件包在指定设备上的 UID 都不同。

由于系统会在进程级别强制执行安全措施,因此任何两个软件包的代码通常都无法在同一进程中运行,因为它们需要以不同的 Linux 用户身份运行。您可以在每个软件包的 AndroidManifest.xml清单标记中使用 sharedUserId 属性,以便为它们分配相同的用户 ID。这样做以后,出于安全考虑,系统随后会将这两个软件包视为具有相同用户 ID 和文件权限的同一应用。请注意,为了确保安全性,只有具有相同签名(以及请求了相同 sharedUserId)的两个应用才能够获得相同的用户 ID。

系统会为应用存储的所有数据分配该应用的用户 ID,而其他软件包通常无法访问这些数据。

如需详细了解 Android 的安全模型,请参阅 Android 安全性概览

定义并强制执行权限

要强制执行自己的权限,您首先必须使用一个或多个 `` 元素在您的 AndroidManifest.xml 中声明它们。

例如,某个应用若要控制谁可以启动它的 Activity,则可以针对此操作声明一个权限,如下所示:

    <manifest
      xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.example.myapp" >
        
        <permission
          android:name="com.example.myapp.permission.DEADLY_ACTIVITY"
          android:label="@string/permlab_deadlyActivity"
          android:description="@string/permdesc_deadlyActivity"
          android:permissionGroup="android.permission-group.COST_MONEY"
          android:protectionLevel="dangerous" />
        ...
    </manifest>
    

注意:系统不允许多个软件包声明同名权限,除非所有软件包均使用同一证书进行签名。如果软件包声明了某个权限,则系统不会允许用户安装其他具有相同权限名称的软件包,除非这些软件包使用与第一个软件包相同的证书进行签名。在为自定义权限命名时,为了避免命名冲突,我们建议采用反向域名方式,例如 com.example.myapp.ENGAGE_HYPERSPACE

protectionLevel 属性为必需项,用于告知系统如何让用户知道哪些应用正在请求权限或者哪些应用可以获得该权限,如链接的文档中所述。

android:permissionGroup 属性为可选项,仅用于帮助系统向用户显示权限。在大多数情况下,尽管您可以自行定义组,但您应将其设置为标准系统组(在 android.Manifest.permission_group 中列出)。最好使用现有的组,因为这可以简化用户看到的权限界面。

您需要为权限提供标签和说明。这些是用户在查看权限列表 (android:label) 或有关单个权限的详细信息 (android:description) 时能够看到的字符串资源。标签应当简短,用简短的文字描述该权限所保护的关键功能。该说明应该由一些句子组成,用于描述此权限允许权限获得者执行哪些操作。我们通常会使用包含两个句子的说明:第一句描述该权限;第二句提醒用户在向某个应用授予该权限后可能会出现哪类错误。

以下示例展示了 CALL_PHONE 权限的标签和说明:

    <string name="permlab_callPhone">directly call phone numbers</string>
    <string name="permdesc_callPhone">Allows the app to call
        phone numbers without your intervention. Malicious apps may
        cause unexpected calls on your phone bill. Note that this does not
        allow the app to call emergency numbers.</string>
    

创建权限组

如上一部分中所示,您可以使用 android:permissionGroup 属性帮助系统向用户描述权限。在大多数情况下,不妨将此属性设置为标准系统组(在 android.Manifest.permission_group 中列出),但您也可以使用 <permission-group> 定义自己的组。

<permission-group> 元素为一组权限(包括使用 <permission> 元素在清单中声明的权限和在其他位置声明的权限)定义了一个标签。这只会影响这些权限在向用户显示时的分组方式。<permission-group> 元素不会指定属于该组的权限,但它会为该组提供一个名称。

将组名称分配给 <permission> 元素的 permissionGroup 属性可在该组中放置权限。

<permission-tree> 元素为代码中定义的一组权限声明了命名空间。

自定义权限建议

应用可以定义自己的自定义权限,还可以通过定义 `` 元素从其他应用请求自定义权限。不过,您应该仔细评估应用是否有必要这样做。

  • 如果您正在设计一系列能够互相公开功能的应用,请尝试将这些应用设计为每个权限仅定义一次。如果这些应用并非全都使用同一证书进行签名,则您必须执行该操作。即使应用均使用同一证书进行签名,每个权限仅定义一次也是最佳做法。
  • 如果该功能仅适用于与提供该功能的应用具有相同签名的应用,则您可以使用签名检查功能,以避免定义自定义权限。如果您的某个应用向您的另一个应用发出请求,后者会先验证两者是否使用相同的证书进行签名,只有证书相同时才会遵照该请求行事。

继续阅读以下内容:

posted @ 2020-06-15 15:36  nupt想象之中  阅读(1149)  评论(0编辑  收藏  举报