3.6 应用配置文件解析

在Android基于组件的应用设计架构中,配置文件是一个很重要的元素。它扮演了应用翻译官的角色—将应用所包含的组件、各组件的能力和配置以及应用环境介绍给Android框架层的各个服务,让Android知道如何去调度应用中的各个组件。

每个应用都有一个配置文件,即应用根目录下的AndroidManifest.xml文件。这是一个XML格式的文件,概述了整个应用的状况,包含了应用的权限信息、所需环境信息、应用和组件信息等内容(如图3-17所示)。本节将依次介绍这些信息。

3.6 应用配置文件解析 - 图1

图 3-17 Android配置文件构成

3.6.1 权限配置

Android的权限系统,是Android整体安全体系的一部分。在配置文件中,应用可以定义第三方访问其中的组件和资源所需要的权限,并设置在特定的组件或方法上。同时,应用也可以通过配置文件声明其所需权限,以访问其他应用或系统中的受限资源。这些权限声明会在应用安装时告知用户,用户可以根据这些信息决定是否要安装该应用。

图3-17所示的权限配置,包含权限的定义和权限使用声明两部分内容。应用需要使用的权限,需要通过配置项<user-permission>来声明。比如,如果应用需要读取联系人记录,就需要在配置文件中添加权限使用声明:


<user-permission android:name="android. permission.READ_CONTACTS"/>


Android的权限验证是基于整个应用的。也就是说,只要应用在配置文件中声明使用该权限,整个应用中任意组件的任何一行代码就都可以访问该权限保护下的资源。应用对权限的使用声明会在应用安装时展示给用户。[1]因此,开发者不应在应用中声明不需要的权限,否则,用户可能会因为这条权限声明而放弃安装应用,那就得不偿失了。

如果开发者需要定义权限来限制第三方应用的访问,则可以通过<permission>配置项来进行定义,示例如下:


<permission

android:name="com.duguhome.test.permission.SAMPLE"

android:label="权限的名字"

android:description="权限的具体描述"

android:permissionGroup=

"android.permission-group.COST_MONEY"

android:protectionLevel="normal"/>


权限定义中的标签(android:label)和描述(android:description)会展示给用户看,因此需要使用用户可理解的语句(而不是开发者的术语)进行描述。而权限的分组,则是为了以更好的结构向用户展示权限,使用户能够更清楚地理解。比如,该示例权限的分组是android.permission-group.COST_MONEY,则该分组下的权限都是提示用户该应用可能会消耗通信费用。如果系统中没有预定义符合该权限的分组,也可以通过配置项<permission-group>自行定义。

定义了的权限还需要部署到对应的组件上才能生效(如图3-18所示)。组件管理服务在构造一个组件对象时,会校验请求组件的权限声明是否与该组件的权限配置相匹配(如果请求组件和实现组件位于同一应用,无需进行检查),如果匹配失败,会抛出异常阻止这次调用。Android的权限体系没有传递性,进行权限比较时仅有调用组件和实现组件参与。比如A组件使用了权限1,并定义了权限2,B组件需要使用A组件时,仅需要声明使用权限2即可,和权限1再无关联。

3.6 应用配置文件解析 - 图2

图 3-18 Android权限的定义和使用声明

基于组件部署权限约束粒度过大,并不能适用于所有的应用场景,尤其是与服务组件绑定并进行通信的时候。一个服务组件提供的接口中,可能有些涉及关键资源的使用,而另一些并非如此,用组件级别的权限进行约束显得过于简单和粗暴,会增加服务组件被使用的难度。因此在Android中,权限也可以不事先部署在组件上,而是在运行时调用Context.checkPermission函数动态校验。比如,很多服务都会对读操作开放而对写操作约束权限。这就需要在所有写操作相关的绑定接口处,调用Context.checkPermission进行校验。

Android的权限体系,需要组件使用者事先知晓被使用组件的权限状况。这是一种单向的权责关系,不适合处理双向组件交互的场景。比如,邮件应用可能会封装一个数据源组件,用来向第三方应用提供邮件内容信息。由于邮件信息比较私密,被读取时应该让用户知晓,邮件应用就需要定义权限限制对数据源组件的调用。但如果邮件应用主动调用第三方组件来展示邮件中的相关内容(比如附件中的图片),就会出现权限死锁的状况—被调用的组件需要从数据源中读取邮件信息,但它并不能提前知晓并声明对数据源的使用权限,从而构成了一个无法解开的死锁。

为了打破这样的死锁,Android为数据源组件特别部署了权限赦免机制。当邮件应用调用第三方组件展示邮件内容时,可以在Intent对象的标志位(flag)中,添加Intent.FLAG_GRANT_READ_URI_PERMISSION等权限赦免相关的标志位,临时允许这个被调用的组件跳过权限约束,直接访问特定URI路径下的资源信息,从而解开这个权限死锁。

对于大部分应用而言,定义并部署权限必须谨慎。因为Android是一个开放的系统,应用各组件之间彼此独立。而自定义的权限为了增加组件间的耦合性,则需要使用者明确知晓所有被调用组件的权限信息,限制了组件调用的灵活性。

小贴士 权限系统仅属于Android安全体系的一部分,除此之外,Android为了保障用户不受恶意应用的侵扰,还部署了其他相关的系统。

1)应用数据的私有性。Android会为每个应用开设一个独立的Linux用户账号,它们有自己独立的Home目录,不能相互读写对方的文件信息(除非获取了root权限)。利用Linux本身的权限机制,Android实现了应用数据的私有化,保障了数据的安全性。

2)应用的签名机制。Android的应用,是用其定义的Java包名来区分的,虽然Java包名有其命名规范,具有很好的辨识度,但并不能保证不同应用间没有重名。而当系统安装了两个同包名的应用时,会允许后来者覆盖前者,这给了恶意应用可乘之机。因此,Android引入了签名机制,开发者可以为它的应用进行签名,签了名的应用就相当于设置了超高保密度的密码,仅当应用包名和签名同时一致时,才允许被覆盖安装。

[1]如果应用请求的权限是危险等级以上的权限,其展现方式会更为丰富。比如,危险等级的权限会在运行时提示用户,而系统级别的权限,则要求应用应有符合需求的签名。