Android 11 开发者预览版现已推出;快来测试并分享您的反馈吧

定义自定义应用权限

本文档介绍了应用开发者如何使用 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 安全性概览

定义并强制执行权限

要强制执行自己的权限,您首先必须使用一个或多个 <permission> 元素在您的 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> 元素为代码中定义的一组权限声明了命名空间。

自定义权限建议

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

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

继续阅读以下内容:

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

您可能还对以下内容感兴趣:

Android 安全性概览
详细讨论了 Android 平台安全模型。