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所示。
解析完manifest标签之后,便进入while循环解析其子标签。
(2)解析manifest的子标签
步骤1 解析application标签。
application标签是AndroidManifest中定义的最重要标签,Android应用程序中用到的四大组件都在这里配置。
解析application标签是在parseApplication方法中完成的。具体解析流程与manifest的解析类似,也是使用TypedArray存储标签属性值。解析完成后application标签与Package对象的对应关系如表9-2所示。
步骤2 解析permission相关标签。
解析permission标签指的是解析与permission相关的四个标签,分别是permission-group、permission-tree、permission和uses-permission。解析过程比较简单,解析后其与Package对象的对应关系如表9-3所示。
步骤3 解析supports-screens标签。
解析过程比较简单,解析后与Package对应关系如表9-4所示。
步骤4 解析其他标签。
这里把其他一些解析方式简单的标签归为一类。解析过程比较简单,解析后与Package对应关系如表9-5所示。
提示 uses-gl-texture、compatible-screens和eat-comment在parsePackage(Resources,……)阶段不解析,直接跳过。这部分标签用于以后扩展。
parsePackage解析AndroidManifest.xml成功后,将解析结果存入PackageParser.Package类型的pkg对象中,并将该对象返回给调用方scanPackageLI(File,……)。至此,scanPackage-LI(File,……)的第二阶段执行完毕。