Google 致力于为黑人社区推动种族平等。查看具体举措

定义自定义应用权限

本文档介绍了应用开发者如何使用 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 平台的安全模型。