解读权限标签 <permission> 和 <uses-permission>

最近在项目中遇到一个问题,不同的包在安装时有冲突,后一个安装的应用会被提示已经有相同的权限被声明了,因此无法安装。看了下两个包的 AndroidManifest.xml 文件,确实有相同名称的 <permission><uses-permission> 标签。那么这个 <permission> 标签和 <uses-permission> 标签有什么区别呢?

权限

首先,什么是权限?我有一个应用A,一个应用B,现在应用A想使用应用B的一些功能,那么Android系统为了安全起见,肯定不能让我们随便就调用了。应用B必须先声明一些权限,对应于开放给外界调用的功能。应用A要用,必须要先注册这些权限,相当于获取了一块通行证。

我们在 AndroidManifest.xml 文件中所见到的权限标签有两种, <permission><uses-permission> 。他们的区别,在官网文档有很详细的解释:

<permission>

<uses-permission>

大概总结一下,就是 <permission> 标签用来声明、定义一个权限,而 <uses-permission> 标签用来申请一个权限。除此之外,还有一些权限相关的标签,比如 <permission-group><permission-tree> ,这两个都是和 <permission> 相关的。其中 <permission-group> 用来声明一个权限组,在定义单个具体权限时可以将其分配到指定的权限组中,这样一个权限组就包含了多个具体权限。当申请权限组中一个单个权限成功时,同一个组内的其他权限也会自动获取到,可谓“一荣俱荣”。另外一个标签 <permission-tree> 则是为一组权限声明一个基础的命名空间,相当于一组权限的前缀,是可以用代码动态添加的,而且其名字必须要至少两个 . 符号。

permission

1
2
3
4
5
6
7
<permission android:description="string resource" <!--描述信息(展示给用户)-->
android:icon="drawable resource"
android:label="string resource" <!--权限的名称(展示给用户)-->
android:name="string" <!--权限的名称(唯一标示)-->
android:permissionGroup="string" <!--所属权限组-->
android:protectionLevel=["normal" | "dangerous" | <!--权限等级-->
"signature" | ...] />

这里要注意一点, name 属性必须是唯一的!官方文档是这么说的:

Note: The system does not allow multiple packages to declare a permission with the same name, unless all the packages are signed with the same certificate. If a package declares a permission, the system does not permit the user to install other packages with the same permission name, unless those packages are signed with the same certificate as the first package. To avoid naming collisions, we recommend using reverse-domain-style naming for custom permissions, for example com.example.myapp.ENGAGE_HYPERSPACE.

也就是说,这一名称在系统中是唯一的,他是什么在系统中注册的就是什么,和你的包名什么的没有关系。因此Google建议采用和包名类似的命名方式。

此外, protectionLevel 这一属性表示这一权限的等级,不同等级的权限有不同的待遇:

  • normal: 任何应用都可以申请这一权限,而且在安装时默认授予,不会直接给用户提示
  • dangerous:任何应用都可以申请这一权限,但是会给用户提示,在5.0 6.0以后的系统,甚至需要应用动态获取,而无法再安装时一次获取
  • signature:只有与声明这一权限的应用使用了相同的私钥签名的应用才可以申请。
  • signatureAndSystem:除了满足signature的应用可以申请外,存放在系统目录 /system/app 目录下也可以申请。

难怪我们安装时会提示权限冲突,原来是因为有两个 <permission> 标签使用了相同的 name 值。

uses-permission

<uses-permission> 权限的相关属性就简单的多:

1
2
<uses-permission android:name="string" <!--权限名称-->
android:maxSdkVersion="integer" /> <!--权限需要的最大Sdk版本-->

这里的 name 属性所指的便是由 <permission> 标签所声明时使用的 name 属性了。

所以权限冲突和这一标签是没关系的。亲测之后发现,只要不存在相同的 <permission> ,即使存在两个一样的 <uses-permission> 也是可以的。

举个栗子

在源码目录的 frameworks/base/core/ 目录下的 AndroidManifest.xml 文件中,声明了一些系统常见的权限。这儿截取一部分来看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
<eat-comment />

<!-- ====================================================================== -->
<!-- Permissions for accessing user's contacts including personal profile -->
<!-- ====================================================================== -->
<eat-comment />

<!-- Used for runtime permissions related to user's contacts and profile. -->
<permission-group android:name="android.permission-group.CONTACTS"
android:icon="@drawable/perm_group_contacts"
android:label="@string/permgrouplab_contacts"
android:description="@string/permgroupdesc_contacts"
android:priority="100" />

<!-- Allows an application to read the user's contacts data.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_CONTACTS"
android:permissionGroup="android.permission-group.CONTACTS"
android:label="@string/permlab_readContacts"
android:description="@string/permdesc_readContacts"
android:protectionLevel="dangerous" />

<!-- Allows an application to write the user's contacts data.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_CONTACTS"
android:permissionGroup="android.permission-group.CONTACTS"
android:label="@string/permlab_writeContacts"
android:description="@string/permdesc_writeContacts"
android:protectionLevel="dangerous" />

拿通讯录权限举例。首先这里声明了一个权限组 android.permission-group.CONTACTS 。之后,分别定义了一个通讯录的读权限、一个通讯录的写权限,且他们的 permissionGroup 属性都指向了刚刚声明的权限组名称。因此,这两个权限便同属于一个权限组。此外,看看 labeldescription 属性的值是什么。在同级的 res 目录下可以看到各种语言版本的 strings.xml 文件,看看中文版怎么说吧:

<string name="permlab_readContacts" msgid="8348481131899886131">"读取您的通讯录"</string>

<string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"允许该应用读取您平板电脑上存储的联系人的相关数据,包括您通过打电话、发送电子邮件或以其他方式与特定个人通信的频率。此权限可让应用保存您的联系人数据,而恶意应用可能会在您不知情的情况下分享联系人数据。"</string>

<string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"允许应用读取您的电视上存储的联系人相关数据,包括您与特定联系人通话、发送电子邮件或通过其他方式进行通信的频率。此权限可让应用保存您的联系人数据,而且恶意应用可能会在您不知情的情况下分享联系人数据。"</string>

<string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"允许该应用读取您手机上存储的联系人的相关数据,包括您通过打电话、发送电子邮件或以其他方式与特定个人通信的频率。此权限可让应用保存您的联系人数据,而恶意应用可能会在您不知情的情况下分享联系人数据。"</string>

<string name="permlab_writeContacts" msgid="5107492086416793544">"修改您的通讯录"</string>

<string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"允许该应用修改您平板电脑上存储的联系人的相关数据,包括您通过打电话、发送电子邮件或以其他方式与特定联系人通信的频率。此权限可让应用删除联系人数据。"</string>

<string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"允许应用修改您的电视上存储的联系人相关数据,包括您与特定联系人通话、发送电子邮件或通过其他方式进行通信的频率。此权限可让应用删除联系人数据。"</string>

<string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"允许该应用修改您手机上存储的联系人的相关数据,包括您通过打电话、发送电子邮件或以其他方式与特定联系人通信的频率。此权限可让应用删除联系人数据。"</string>

就很清楚了,在应用主动请求获取这一权限时,会弹出一个dialog,询问用户是否给予这一权限。而dialog的内容,便是由这儿的 labeldescription 组成的。