本文档介绍了应用开发者如何使用 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
中),尽管您也可以像下一节所述那样自行定义组。我们建议使用现有组,因为这可以简化向用户显示的权限界面。
您需要为权限提供标签和描述。这些是用户在查看权限列表 (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 平台安全模型的详细讨论。