Android PMS

Android PMS ,第1张

简介

本篇介绍PackageParser,它用来在apk的安装过程中解析apk

1.引入PackageParser
Android世界中有很多包,比如应用程序的APK,Android运行环境的JAR包(比如framework.jar)和组成Android系统的各种动态库so等等,由于包的种类和数量繁多,就需要进行包管理,但是包管理需要在内存中进行,而这些包都是以静态文件的形式存在的,就需要一个工具类将这些包转换为内存中的数据结构,这个工具就是包解析器PackageParser。我们知道安装APK时需要调用PMS的installPackageLI方法:

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
    ...
    PackageParser pp = new PackageParser();//1
    pp.setSeparateProcesses(mSeparateProcesses);
    pp.setDisplayMetrics(mMetrics);
    pp.setCallback(mPackageParserCallback);
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
    final PackageParser.Package pkg;
    try {
        pkg = pp.parsePackage(tmpPackageFile, parseFlags);//2
    }
    ...
 }   

可以看到安装APK时,需要先在注释1处创建PackageParser,然后在注释2处调用PackageParser的parsePackage方法来解析APK。

2. PackageParser解析APK

Android5.0引入了Split APK机制,这是为了解决65536上限以及APK安装包越来越大等问题。Split APK机制可以将一个APK,拆分成多个独立APK。在引入了Split APK机制后,APK有两种分类:

Single APK:安装文件为一个完整的APK,即base APK。Android称其为Monolithic。
Mutiple APK:安装文件在一个文件目录中,其内部有多个被拆分的APK,这些APK由一个 base APK和一个或多个split APK组成。Android称其为Cluster。
了解了APK,我们接着学习PackageParser解析APK,查看PackageParser的parsePackage方法:

/**
 * Parse only lightweight details about the package at the given location.
 * Automatically detects if the package is a monolithic style (single APK
 * file) or cluster style (directory of APKs).
 * 

* This performs sanity checking on cluster style packages, such as * requiring identical package name and version codes, a single base APK, * and unique split names. * * @see PackageParser#parsePackage(File, int) */ public static PackageLite parsePackageLite(File packageFile, int flags) throws PackageParserException { if (packageFile.isDirectory()) { return parseClusterPackageLite(packageFile, flags); } else { return parseMonolithicPackageLite(packageFile, flags); } }

如果要解析的packageFile是一个目录,说明是Mutiple APK,就需要调用parseClusterPackageLite方法来解析,如果是Single APK则调用parseMonolithicPackageLite方法来解析。这里以复杂的parseClusterPackageLite方法为例,了解了这个方法,parseMonolithicPackageLite方法自然也看的懂。

/**
 * Parse all APKs contained in the given directory, treating them as a
 * single package. This also performs sanity checking, such as requiring
 * identical package name and version codes, a single base APK, and unique
 * split names.
 * 

* Note that this does not perform signature verification; that * must be done separately in {@link #collectCertificates(Package, int)}. */ private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException { //注释1 final PackageLite lite = parseClusterPackageLite(packageDir, 0); //注释2 if (mOnlyCoreApps && !lite.coreApp) { throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, "Not a coreApp: " + packageDir); } ... try { final AssetManager assets = assetLoader.getBaseAssetManager(); final File baseApk = new File(lite.baseCodePath); //注释3 final Package pkg = parseBaseApk(baseApk, assets, flags); if (pkg == null) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse base APK: " + baseApk); } if (!ArrayUtils.isEmpty(lite.splitNames)) { //注释4 final int num = lite.splitNames.length; pkg.splitNames = lite.splitNames; pkg.splitCodePaths = lite.splitCodePaths; pkg.splitRevisionCodes = lite.splitRevisionCodes; pkg.splitFlags = new int[num]; pkg.splitPrivateFlags = new int[num]; pkg.applicationInfo.splitNames = pkg.splitNames; pkg.applicationInfo.splitDependencies = splitDependencies; pkg.applicationInfo.splitClassLoaderNames = new String[num]; for (int i = 0; i < num; i++) { final AssetManager splitAssets = assetLoader.getSplitAssetManager(i); //注释5 parseSplitApk(pkg, i, splitAssets, flags); } } pkg.setCodePath(packageDir.getCanonicalPath()); pkg.setUse32bitAbi(lite.use32bitAbi); return pkg; } catch (IOException e) { throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, "Failed to get path: " + lite.baseCodePath, e); } finally { IoUtils.closeQuietly(assetLoader); } }

注释1处调用parseClusterPackageLite方法用于轻量级解析目录文件,之所以要轻量级解析是因为解析APK是一个复杂耗时的 *** 作,这里的逻辑并不需要APK所有的信息。parseClusterPackageLite方法内部会通过parseApkLite方法解析每个Mutiple APK,得到每个Mutiple APK对应的ApkLite(轻量级APK信息),然后再将这些ApkLite封装为一个PackageLite(轻量级包信息)并返回。

注释2处,mOnlyCoreApps用来指示PackageParser是否只解析“核心”应用,“核心”应用指的是AndroidManifest中属性coreApp值为true,只解析“核心”应用是为了创建一个极简的启动环境。mOnlyCoreApps在创建PMS时就一路传递过来,如果我们加密了设备,mOnlyCoreApps值就为true。另外可以通过PackageParser的setOnlyCoreApps方法来设置mOnlyCoreApps的值。
lite.coreApp表示当前包是否包含“核心”应用,如果不满足注释2的条件就会抛出异常。

注释3处的parseBaseApk方法用于解析base APK,注释4处获取split APK的数量,根据这个数量在

注释5处遍历调用parseSplitApk来解析每个split APK。这里主要查看parseBaseApk方法,如下所示。

private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
        throws PackageParserException {
    final String apkPath = apkFile.getAbsolutePath();

    String volumeUuid = null;
    if (apkPath.startsWith(MNT_EXPAND)) {
        final int end = apkPath.indexOf('/', MNT_EXPAND.length());
        //注释1
        volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
    }

    mParseError = PackageManager.INSTALL_SUCCEEDED;
    mArchiveSourcePath = apkFile.getAbsolutePath();

    if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);

    XmlResourceParser parser = null;
    try {
        final int cookie = assets.findCookieForPath(apkPath);
        if (cookie == 0) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                    "Failed adding asset path: " + apkPath);
        }
        parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
        final Resources res = new Resources(assets, mMetrics, null);

        final String[] outError = new String[1];
        //注释2
        final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
        if (pkg == null) {
            throw new PackageParserException(mParseError,
                    apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
        }
        //注释3
        pkg.setVolumeUuid(volumeUuid);
        //注释4
        pkg.setApplicationVolumeUuid(volumeUuid);
        pkg.setBaseCodePath(apkPath);
        pkg.setSigningDetails(SigningDetails.UNKNOWN);

        return pkg;

    } catch (PackageParserException e) {
        throw e;
    } catch (Exception e) {
        throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                "Failed to read manifest from " + apkPath, e);
    } finally {
        IoUtils.closeQuietly(parser);
    }
}

注释1处,如果APK的路径以/mnt/expand/开头,就截取该路径获取volumeUuid

注释3处用于以后标识这个解析后的Package

注释4处的用于标识该App所在的存储卷UUID。

注释2处又调用了parseBaseApk的重载方法,可以看出当前的parseBaseApk方法主要是为了获取和设置volumeUuid。parseBaseApk的重载方法如下所示。

/**
* Parse the manifest of a base APK. When adding new features you
* need to consider whether they should be supported by split APKs and child
* packages.
*
* @param apkPath The package apk file path
* @param res The resources from which to resolve values
* @param parser The manifest parser
* @param flags Flags how to parse
* @param outError Human readable error message
* @return Parsed package or null on error.
*
* @throws XmlPullParserException
* @throws IOException
*/
private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
        String[] outError) throws XmlPullParserException, IOException {
    ...
    //注释1
    final Package pkg = new Package(pkgName);
    //从资源中提取自定义属性集com.android.internal.R.styleable.AndroidManifest得到TypedArray
    //注释2
    TypedArray sa = res.obtainAttributes(parser,
            com.android.internal.R.styleable.AndroidManifest);
    //使用typedarray获取AndroidManifest中的versionCode赋值给Package的对应属性 
    pkg.mVersionCode = sa.getInteger(
            com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
    pkg.mVersionCodeMajor = sa.getInteger(
            com.android.internal.R.styleable.AndroidManifest_versionCodeMajor, 0);
    pkg.applicationInfo.setVersionCode(pkg.getLongVersionCode());
    pkg.baseRevisionCode = sa.getInteger(
            com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
    pkg.mVersionName = sa.getNonConfigurationString(
            com.android.internal.R.styleable.AndroidManifest_versionName, 0);
    if (pkg.mVersionName != null) {
        pkg.mVersionName = pkg.mVersionName.intern();
    }
    //注释3
    pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);

    pkg.mCompileSdkVersion = sa.getInteger(
            com.android.internal.R.styleable.AndroidManifest_compileSdkVersion, 0);
    pkg.applicationInfo.compileSdkVersion = pkg.mCompileSdkVersion;
    pkg.mCompileSdkVersionCodename = sa.getNonConfigurationString(
            com.android.internal.R.styleable.AndroidManifest_compileSdkVersionCodename, 0);
    if (pkg.mCompileSdkVersionCodename != null) {
        pkg.mCompileSdkVersionCodename = pkg.mCompileSdkVersionCodename.intern();
    }
    pkg.applicationInfo.compileSdkVersionCodename = pkg.mCompileSdkVersionCodename;
    //获取资源后要回收
    sa.recycle();

    return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
}

注释1处创建了Package对象

注释2处从资源中提取自定义属性集 com.android.internal.R.styleable.AndroidManifest得到TypedArray ,这个属性集所在的源码位置为frameworks/base/core/res/res/values/attrs_manifest.xml。接着用TypedArray读取APK的AndroidManifest中的versionCode、revisionCode和versionName的值赋值给Package的对应的属性。

注释3处读取APK的AndroidManifest中的coreApp的值。
最后会调用parseBaseApkCommon方法,这个方法非常长,主要用来解析APK的AndroidManifest中的各个标签,比如application、permission、uses-sdk、feature-group等等,其中四大组件的标签在application标签下,解析application标签的方法为parseBaseApplication。

/**
* This is the common parsing routing for handling parent and child
* packages in a base APK. The difference between parent and child
* parsing is that some tags are not supported by child packages as
* well as some manifest attributes are ignored. The implementation
* assumes the calling code has already handled the manifest tag if needed
* (this applies to the parent only).
*
* @param pkg The package which to populate
* @param acceptedTags Which tags to handle, null to handle all
* @param res Resources against which to resolve values
* @param parser Parser of the manifest
* @param flags Flags about how to parse
* @param outError Human readable error if parsing fails
* @return The package if parsing succeeded or null.
*
* @throws XmlPullParserException
* @throws IOException
*/
private Package parseBaseApkCommon(Package pkg, Set acceptedTags, Resources res,
        XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
        IOException {
    ...
    int outerDepth = parser.getDepth();
    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
            && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
        if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
            continue;
        }

        String tagName = parser.getName();

        if (acceptedTags != null && !acceptedTags.contains(tagName)) {
            Slog.w(TAG, "Skipping unsupported element under : "
                    + tagName + " at " + mArchiveSourcePath + " "
                    + parser.getPositionDescription());
            XmlUtils.skipCurrentTag(parser);
            continue;
        }
        //解析application
        if (tagName.equals(TAG_APPLICATION)) {
            if (foundApp) {
                if (RIGID_PARSER) {
                    outError[0] = " has more than one ";
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return null;
                } else {
                    Slog.w(TAG, " has more than one ");
                    XmlUtils.skipCurrentTag(parser);
                    continue;
                }
            }

            foundApp = true;
            if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
                return null;
            }
        }
        ...
        } else if (tagName.equals(TAG_PERMISSION_GROUP)) {
            if (!parsePermissionGroup(pkg, flags, res, parser, outError)) {
                return null;
            }
        } else if (tagName.equals(TAG_PERMISSION)) {
            //解析permission
            if (!parsePermission(pkg, res, parser, outError)) {
                return null;
            }
        }
        ...
    }
    ...
    return pkg;
}
/**
* Parse the {@code application} XML tree at the current parse location in a
* base APK manifest.
* 

* When adding new features, carefully consider if they should also be * supported by split APKs. */ private boolean parseBaseApplication(Package owner, Resources res, XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException { final ApplicationInfo ai = owner.applicationInfo; final String pkgName = owner.applicationInfo.packageName; ... while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } String tagName = parser.getName(); //注释1 if (tagName.equals("activity")) { Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false, owner.baseHardwareAccelerated); if (a == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } hasActivityOrder |= (a.order != 0); owner.activities.add(a); //注释2 } else if (tagName.equals("receiver")) { Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, true, false); if (a == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } hasReceiverOrder |= (a.order != 0); owner.receivers.add(a); //注释3 } else if (tagName.equals("service")) { Service s = parseService(owner, res, parser, flags, outError, cachedArgs); if (s == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } hasServiceOrder |= (s.order != 0); owner.services.add(s); //注释4 } else if (tagName.equals("provider")) { Provider p = parseProvider(owner, res, parser, flags, outError, cachedArgs); if (p == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } owner.providers.add(p); } ... return true; }

parseBaseApplication方法有近500行代码,这里只截取了解析四大组件相关的代码。注释1处如果标签名为activity,就调用注释2处的parseActivity方法解析activity标签并得到一个Activity对象(PackageParser的静态内部类),这个方法有300多行代码,解析一个activity标签就如此繁琐,activity标签只是Application中众多标签的一个,而Application只是AndroidManifest众多标签的一个,这让我们更加理解了为什么此前解析APK时要使用轻量级解析了。注释3处将解析得到的Activity对象保存在Package的列表activities中。其他的四大组件也是类似的逻辑。
PackageParser解析APK的代码逻辑非常庞大,基本了解本文所讲的就足够了,如果有兴趣可以自行看源码。
parseBaseApk方法主要的解析结构可以理解为以下简图。

 3. Package 数据结构

包被解析后,最终在内存是Package,Package是PackageParser的内部类,它的部分成员变量如下所示。

public final static class Package implements Parcelable {
    public String packageName;
    public String manifestPackageName;
    public String[] splitNames;
    public String volumeUuid;
    public String codePath;
    public String baseCodePath;
    ...
    public ApplicationInfo applicationInfo = new ApplicationInfo();
    public final ArrayList permissions = new ArrayList(0);
    public final ArrayList permissionGroups = new ArrayList(0);
    public final ArrayList activities = new ArrayList(0);//1
    public final ArrayList receivers = new ArrayList(0);
    public final ArrayList providers = new ArrayList(0);
    public final ArrayList services = new ArrayList(0);
    public final ArrayList instrumentation = new ArrayList(0);
...
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/906346.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-15
下一篇 2022-05-15

发表评论

登录后才能评论

评论列表(0条)

保存