定义自定义应用权限

本文档描述了应用开发者如何使用 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 平台安全模型的详细讨论。