9.1.2 解析AndroidManifest.xml文件

scanPackageLI第二阶段的主要工作是解析AndroidManifest.xml文件,由parsePackage方法完成。该方法定义于frameworks/base/core/java/android/content/pm/PackageParser.java中,有两个重载方法,声明如下:


public Package parsePackage(File sourceFile, String destCodePath,

DisplayMetrics metrics, int flags)

private Package parsePackage(Resources res, XmlResourceParser parser,

int flags, String[]outError)


注意 这里以parsePackage(File,……)的简写形式表示第一个方法,该方法负责APK文件的预处理和打开AndroidManifest.xml文件;以parsePackage(Resources,……)表示第二个方法,该方法负责解析AndroidManifest.xml中的标签并存入pkg变量中。

这里解析的内容包括APK的package名,AndroidManifest.xml中的versionCode、version Name、sharedUserId、sharedUserLabel、installLocation、application、permission-group、permission、permission-tree、uses-permission、uses-configuration、uses-feature、uses-sdk、supports-screens、protected-broadcast、instrumentation、original-package、adopt-permissions、uses-gl-texture、compatible-screens、eat-comment等标签信息。最终这些信息被存入PackageParser.Package类型的pkg对象中。

1.parsePackage(File,……)方法

首先分析parsePackage(File,……)方法,代码如下:


public Package parsePackage(File sourceFile, String destCodePath,

DisplayMetrics metrics, int flags){

……//省略APK文件的判断和过滤部分

mArchiveSourcePath=sourceFile.getPath();//APK文件的路径

XmlResourceParser parser=null;

AssetManager assmgr=null;

Resources res=null;

boolean assetError=true;

try{

//将APK添加到资源路径,AssetManager获取该路径下的任何资源

assmgr=new AssetManager();

int cookie=assmgr.addAssetPath(mArchiveSourcePath);

if(cookie!=0){

res=new Resources(assmgr, metrics, null);

assmgr.setConfiguration(0,0,null,0,0,0,0,0,0,0,

0,0,0,0,0,0,Build.VERSION.RESOURCES_SDK_INT);

parser=assmgr.openXmlResourceParser(cookie,

ANDROID_MANIFEST_FILENAME);

assetError=false;

}

}

……//省略错误和异常处理代码

String[]errorText=new String[1];

Package pkg=null;

Exception errorException=null;

try{

/调用parsePackage(Resources……)重载方法解析AndroidManifest.xml/

pkg=parsePackage(res, parser, flags, errorText);

}catch(Exception e){

errorException=e;

}

……//省略错误和异常处理代码

/将已知属性直接存入pkg/

pkg.mPath=destCodePath;

pkg.mScanPath=mArchiveSourcePath;

pkg.mSignatures=null;

return pkg;

}


可见parsePackage(File,……)方法仅仅负责对APK文件的预处理,过滤掉不符合条件的文件;然后通过AssetManager获取APK中的AndroidManifest.xml文件资源,并将该资源和一个XML资源解析器XmlResourceParser一同传入重载方法parsePackage(Resources,……),进行具体的AndroidManifest.xml解析工作。这步工作完成后,AndroidManifest.xml定义的package信息便被存入pkg变量中了。

2.AndroidManifest.xml的文件结构

在分析parsePackage(Resources,……)方法之前,有必要先熟悉AndroidManifest.xml的文件结构。除了在各个应用程序中必须有一个AndroidManifest.xml文件外,frameworks/base/core/res目录下也有一个该文件,它隶属于Android系统资源包framework-res.apk。以frameworks/base/core/res/AndroidManifest.xml为例,其文件的结构如下:


<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="android"coreApp="true"

android:sharedUserId="android.uid.system"

android:sharedUserLabel="@string/android_system_label">

<protected-broadcast android:name="android.intent.action.SCREEN_OFF"/>

……

<permission-group android:name="android.permission-group.COST_MONEY"

android:label="@string/permgrouplab_costMoney"

android:description="@string/permgroupdesc_costMoney"/>

……

<application android:process="system"

android:persistent="true"

android:hasCode="false"

android:label="@string/android_system_label"

android:allowClearUserData="false"

android:backupAgent="com.android.server.SystemBackupAgent"

android:killAfterRestore="false"

android:icon="@drawable/ic_launcher_android">

<activity android:name="com.android.internal.app.ChooserActivity"

android:theme="@style/Theme.Holo.Dialog.Alert"

android:finishOnCloseSystemDialogs="true"

android:excludeFromRecents="true"

android:multiprocess="true">

<intent-filter>

<action android:name="android.intent.action.CHOOSER"/>

<category android:name="android.intent.category.DEFAULT"/>

</intent-filter>

</activity>

……

</application>

……

</manifest>


AndroidManifest. xml中包含的标签和属性大多数由frameworks/base/core/res/res/value/attrs_manifest.xml定义和限定。对于manifest标签,其包含的属性定义如下:


<declare-styleable name="AndroidManifest">

<attr name="versionCode"/>

<attr name="versionName"/>

<attr name="sharedUserId"/>

<attr name="sharedUserLabel"/>

<attr name="installLocation"/>

</declare-styleable>


该定义的意思是:对于manifest标签,可以有versionCode、versionName、sharedUserId、sharedUserLabel和installLocation这几个内置属性。使用这些内置属性时,需要以"android:"为前缀引用。manifest有许多子标签,对于子标签,可以通过parent属性指定父标签。以permission-group子标签为例,其定义如下:


<declare-styleable name="AndroidManifestPermissionGroup"

parent="AndroidManifest">

<attr name="name"/>

<attr name="label"/>

<attr name="icon"/>

<attr name="logo"/>

<attr name="description"/>

</declare-styleable>


这段代码的意思是,permission-group的父标签(外层标签)是manifest,其可以包含以下属性:name、label、icon、logo、description。

注意 关于AndroidManifest.xml的详细信息,请读者参照Android开发者网站http://developer.android.com/index.html,这里不做全面分析。

熟悉了AndroidManifest.xml的基本结构,接下来继续分析parsePackage(Resources,……)。

3.parsePackage(Resources,……)方法

parsePackage(Resources,……)是parsePackage(File,……)的重载方法,该方法的作用就是解析AndroidManifest.xml的各个标签,并将解析结果存入pkg中。对于不同的标签,分别调用对应的解析函数解析。具体步骤如下。

(1)解析manifest标签

步骤1 解析manifest标签的package属性。

package属性值不能为null,否则程序将跳过后续解析,直接返回。对于framework-res.apk其package属性值为android。对于其他应用程序,该属性值一般为包名。解析成功后,以package属性值构造Package类型的pkg对象,后续解析步骤都是往这个对象里填充解析数据。

步骤2 解析manifest标签的coreApp属性。

coreApp指定系统核心应用,为系统提供最基本功能,默认包含以下应用程序:

framework-res. apk(包名为android)

DefaultContainerService(包名为com.android.defcontainer)

SettingsProvider(包名为com.android.providers.settings)

SystemUI(包名为com.android.systemui)

Phone(包名为com.android.phone)

Settings(包名为com.android.settings)

LatinIME(包名为com.android.inputmethod.latin)

TelephonyProvider(包名为com.android.providers.telephony)

scanPackageLI(File,……)方法中会调用setOnlyCoreApps方法设置如果指定PackageParser的成员变量mOnlyCoreApps,如果其赋值为true,则该解析过程将跳过未在manifest中指定coreApp属性的应用程序。

步骤3 解析attrs_manifest.xml中指定的manifest标签的内置属性。

attrs_manifest. xml中指定的manifest属性有versionCode、versionName、sharedUserId、sharedUserLabel和installLocation,代码如下:


//该pkg便是PackageParser.Package类型的变量

final Package pkg=new Package(pkgName);

TypedArray sa=res.obtainAttributes(attrs,

com.android.internal.R.styleable.AndroidManifest);

pkg.mVersionCode=sa.getInteger(

com.android.internal.R.styleable.AndroidManifest_versionCode,0);

pkg.mVersionName=sa.getNonConfigurationString(

com.android.internal.R.styleable.AndroidManifest_versionName,0);

String str=sa.getNonConfigurationString(

com.android.internal.R.styleable.AndroidManifest_sharedUserId,0);

if(str!=null&&str.length()>0){

……

pkg.mSharedUserId=str.intern();

pkg.mSharedUserLabel=sa.getResourceId(

com.android.internal.R.styleable.AndroidManifest_sharedUserLabel,0);

}

sa.recycle();

pkg.installLocation=sa.getInteger(

com.android.internal.R.styleable.AndroidManifest_installLocation,

PARSE_DEFAULT_INSTALL_LOCATION);

pkg.applicationInfo.installLocation=pkg.installLocation;

……


TypedArray是Android为存储属性提供的数据结构,其实现依赖于数组。通过obtainAttributes方法可以得到manifest标签的属性信息。传入obtainAttributes方法的第二个参数com.android.internal.R.styleable.AndroidManifest来自于系统资源文件out/target/common/R/android/R.java,该参数是数组类型,存储了标签属性的ID值。有了属性ID后,便可以通过TypedArray上的getXXX方法提取出具体的属性值。以属性versionCode为例,其在R文件中的ID为com.android.internal.R.styleable.AndroidManifest_versionCode,将该标记传入getInteger方法,便得到了versionCode属性的值。

注意 这里是以com.android.internal.R.styleable.AndroidManifestversionCode标记具体的属性,而不是以com.android.internal.R.styleable.AndroidManifest.versionCode的形式标记。在R文件中是以“”取代了“.”。

manifest标签的内置属性解析完毕后,manifest与Package对象的对应关系如表9-1所示。

9.1.2 解析AndroidManifest.xml文件 - 图1

9.1.2 解析AndroidManifest.xml文件 - 图2

解析完manifest标签之后,便进入while循环解析其子标签。

(2)解析manifest的子标签

步骤1 解析application标签。

application标签是AndroidManifest中定义的最重要标签,Android应用程序中用到的四大组件都在这里配置。

解析application标签是在parseApplication方法中完成的。具体解析流程与manifest的解析类似,也是使用TypedArray存储标签属性值。解析完成后application标签与Package对象的对应关系如表9-2所示。

9.1.2 解析AndroidManifest.xml文件 - 图3

9.1.2 解析AndroidManifest.xml文件 - 图4

步骤2 解析permission相关标签。

解析permission标签指的是解析与permission相关的四个标签,分别是permission-group、permission-tree、permission和uses-permission。解析过程比较简单,解析后其与Package对象的对应关系如表9-3所示。

9.1.2 解析AndroidManifest.xml文件 - 图5

步骤3 解析supports-screens标签。

解析过程比较简单,解析后与Package对应关系如表9-4所示。

9.1.2 解析AndroidManifest.xml文件 - 图6

步骤4 解析其他标签。

这里把其他一些解析方式简单的标签归为一类。解析过程比较简单,解析后与Package对应关系如表9-5所示。

9.1.2 解析AndroidManifest.xml文件 - 图7

提示 uses-gl-texture、compatible-screens和eat-comment在parsePackage(Resources,……)阶段不解析,直接跳过。这部分标签用于以后扩展。

parsePackage解析AndroidManifest.xml成功后,将解析结果存入PackageParser.Package类型的pkg对象中,并将该对象返回给调用方scanPackageLI(File,……)。至此,scanPackage-LI(File,……)的第二阶段执行完毕。