定义自定义应用权限

本文档介绍了应用开发者如何使用 Android 提供的安全功能来定义自己的权限。通过定义自定义权限,应用可以与其它的应用共享其资源和功能。有关权限的更多信息,请参阅权限概述

背景

Android 是一个特权分离的操作系统,其中每个应用都以不同的系统身份(Linux 用户 ID 和组 ID)运行。系统的一部分也分离成不同的身份。因此,Linux 将应用彼此隔离,并与系统隔离。

应用可以通过定义其他应用可以请求的权限来将其功能公开给其他应用。它们还可以定义自动提供给使用相同证书签名的任何其他应用的权限。

应用签名

所有 APK 必须使用证书签名,其私钥由其开发者持有。该证书*不需要*由证书颁发机构签名。Android 应用可以使用自签名证书是允许的,也是典型的做法。Android 中证书的目的是区分应用作者。这使系统能够授予或拒绝应用访问签名级权限,并授予或拒绝应用请求与另一个应用具有相同的 Linux 身份

在设备制造时间之后授予签名权限

从 Android 12(API 级别 31)开始,knownCerts 属性用于签名级权限,允许您在声明时引用已知签名证书的摘要。

您可以声明 knownCerts 属性并在应用的 protectionLevel 属性中为特定签名级权限使用 knownSigner 标志。然后,如果请求应用的签名链(包括当前签名者)中的任何签名者与 knownCerts 属性中权限声明的摘要之一匹配,系统就会将该权限授予请求应用。

knownSigner 标志允许设备和应用授予其他应用签名权限,而无需在设备制造和发货时对应用进行签名。

用户 ID 和文件访问

在安装时,Android 为每个软件包分配一个唯一的 Linux 用户 ID。该标识在软件包在该设备上的整个生命周期中保持不变。在不同的设备上,同一个软件包可能具有不同的 UID——重要的是,每个软件包在给定设备上都有一个唯一的 UID。

由于安全执行发生在进程级别,因此任何两个软件包的代码通常都不能在同一进程中运行,因为它们需要以不同的 Linux 用户身份运行。

应用存储的任何数据都将分配该应用的用户 ID,并且通常无法被其他软件包访问。

有关 Android 安全模型的更多信息,请参阅 Android 安全概述

定义和执行权限

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

命名约定

除非所有软件包都使用相同的证书签名,否则系统不允许多个软件包声明具有相同名称的权限。如果软件包声明了权限,则系统也不允许用户安装具有相同权限名称的其他软件包,除非这些软件包与第一个软件包使用相同的证书签名。

我们建议使用反向域名样式的命名法,以应用的软件包名称作为权限的前缀,后跟 .permission.,然后是权限表示的功能的描述,使用大写 SNAKE_CASE 格式。例如,com.example.myapp.permission.ENGAGE_HYPERSPACE

遵循此建议可避免命名冲突,并有助于清楚地识别自定义权限的所有者和意图。

示例

例如,一个需要控制哪些其他应用可以启动其活动之一的应用可以为该操作声明一个权限,如下所示

<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>

protectionLevel 属性是必需的,它告诉系统如何告知需要权限的应用的用户或哪些应用可以持有权限,如链接文档中所述。

android:permissionGroup 属性是可选的,仅用于帮助系统向用户显示权限。在大多数情况下,您将其设置为标准系统组(在 android.Manifest.permission_group 中列出),尽管您可以自己定义一个组,如下一节所述。我们建议使用现有组,因为这简化了向用户显示的权限 UI。

您需要为权限提供标签和描述。这些是用户在查看权限列表 (android:label) 或单个权限的详细信息 (android:description) 时可以看到的字符串资源。标签很短:几个词描述了权限保护的关键功能。描述是几句话描述了权限允许持有者做什么。我们的约定是两句描述,第一句描述权限,第二句警告用户如果授予应用权限可能会发生什么问题。

以下是一个 CALL_PHONE 权限的标签和描述示例

<string name="permlab_callPhone">directly call phone numbers</string>
<string name="permdesc_callPhone">Allows the app to call non-emergency
phone numbers without your intervention. Malicious apps may cause unexpected
calls on your phone bill.</string>

创建权限组

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

<permission-group> 元素为一组权限定义一个标签——包括在清单中使用 <permission> 元素声明的权限和在其他地方声明的权限。这仅影响权限在呈现给用户时如何分组。 <permission-group> 元素不会指定属于该组的权限,但它为该组提供了一个名称。

您可以通过将组名称分配给 <permission> 元素的 permissionGroup 属性来将权限放入该组。

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

自定义权限建议

您可以为您的应用定义自定义权限,并通过定义 <uses-permission> 元素来请求其他应用的自定义权限。但是,请仔细评估是否需要这样做。

  • 如果您正在设计一套相互公开功能的应用,请尝试设计这些应用,以便每个权限只定义一次。如果这些应用并非都使用相同的证书签名,则必须这样做。即使所有应用都使用相同的证书签名,最好也只定义每个权限一次。
  • 如果功能仅对与提供应用使用相同签名的应用可用,则您可以通过使用签名检查来避免定义自定义权限。当您的一个应用向您的另一个应用发出请求时,第二个应用可以在遵守请求之前验证这两个应用是否都使用相同的证书签名。

如果需要自定义权限,请考虑是否只有与执行权限检查的应用使用相同开发者的应用需要访问它——例如,在实现来自同一开发者的两个应用之间的安全进程间通信时。如果是这样,我们建议使用 签名权限。签名权限对用户是透明的,并且避免了用户确认的权限,这可能会让用户感到困惑。

继续阅读有关

<uses-permission>
声明应用所需的系统权限的清单标签的 API 参考。

您可能也对以下内容感兴趣

Android 安全概述
关于 Android 平台安全模型的详细讨论。