Apk安装之——PMS解析apk过程分析

在android的apk安装过程中,PMS会调用installPackageLI方法对apk进行解析,下面看看这个方法的具体实现:


http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
        ...
	PackageParser pp = new PackageParser();
	...
        final PackageParser.Package pkg;
        try {
	    //关键代码1
            pkg = pp.parsePackage(tmpPackageFile, parseFlags);
            DexMetadataHelper.validatePackageDexMetadata(pkg);
        } catch (PackageParserException e) {
            res.setError("Failed parse during installPackageLI", e);
            return;
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }

        ...

}

这个方法中,会先创建一个PackageParser对象,并调用PackageParser了的parsePackage方法对apk包进行解析。下面来看看PackageParser类的parsePackage方法的具体实现:


http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/content/pm/PackageParser.java

public Package parsePackage(File packageFile, int flags) throws PackageParserException {
    return parsePackage(packageFile, flags, false /* useCaches */);
}


public Package parsePackage(File packageFile, int flags, boolean useCaches)
        throws PackageParserException {
    Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;
    if (parsed != null) {
        return parsed;
    }

    ...
    //关键代码
    if (packageFile.isDirectory()) {
        parsed = parseClusterPackage(packageFile, flags);
    } else {
        parsed = parseMonolithicPackage(packageFile, flags);
    }

    ...
    return parsed;
}

parsePackage方法内部,调用了其重载方法,这个重载方法内部,会判断packageFile是否是文件夹,而采用不同的解析方法对apk进行解析,之所以采用两种方式对apk包进行解析,是因为从android 5.0开始,为了解决单个包方法是数超过65535的限制,就采用了multiple apk,将一个apk拆分成多个apk,这样就解决了单个apk的方法数的限制。对于一个完整的未拆分的apk,集base APK,Andriod称之为Monolithic,对于多个apk,由一个base APK和多个split APK组成,Android 称之为Cluster。并且对于多个apk,它们是存储在一个目录下的,所以,上面的代码中会通过packageFile是否是目录来区分。从parseClusterPackage中带有Cluster这个单词,就知道这个方法是解析多个apk的,另外一个parseMonolithicPackage方法,就是解析一个base.apk的。由于解析多个apk的方法中是包含了解析一个apk的原理的,所以,下面以parseClusterPackage为例来分析,是如何具体的解析apk的。

http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/content/pm/PackageParser.java

private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
    //关键代码1
    final PackageLite lite = parseClusterPackageLite(packageDir, 0);
    ...

    try {
        final AssetManager assets = assetLoader.getBaseAssetManager();
        final File baseApk = new File(lite.baseCodePath);
	//关键代码2
        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)) {
            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);
		//关键代码3
                parseSplitApk(pkg, i, splitAssets, flags);
            }
        }

        ...
        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方法,获取到PackageLite类型的对象,这个方法的名称中包含了Lite,表示这个方法只是轻量的解析了apk包,因为apk的解析过程是很复制的,并且和耗时,所以这里只是轻量的解析apk,获取apk包中的少量的信息,后面用这个获取到的apk的少量信息来作为后面解析apk的参数使用,在关键代码2处,调用parseBaseApk方法来解析baseApk,在关键代码3处,调用parseSplitApk方法对split apk进行解析。下面先来看看parseBaseApk方法的具体实现:


http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/content/pm/PackageParser.java

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

    ...

    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];
	//关键代码
        final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
        if (pkg == null) {
            throw new PackageParserException(mParseError,
                    apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
        }

        pkg.setVolumeUuid(volumeUuid);
        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);
    }
}

这个方法内部,在关键代码处,调用了parseBaseApk的重载方法,下面看看这个重载方法:


http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/content/pm/PackageParser.java

private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
        String[] outError) throws XmlPullParserException, IOException {
    final String splitName;
    final String pkgName;

    ...

    final Package pkg = new Package(pkgName);
    //关键代码1处
    TypedArray sa = res.obtainAttributes(parser,
            com.android.internal.R.styleable.AndroidManifest);

    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();
    }

    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();
    //关键代码2
    return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
}

在这个方法中,从资源中提取自定义属性集,接着用TypedArray读取APK的AndroidManifest中的versionCode、revisionCode和versionName的值赋值给Package的对应的属性。在关键代码2处,调用parseBaseApkCommon方法来将来解析manifest文件中的permission,user-sdk,application,feature-group等标签,并调用parseBaseApplication方法,对Application标签进行。下面看看这个方法的具体实现:


http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/content/pm/PackageParser.java

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;

    TypedArray sa = res.obtainAttributes(parser,
            com.android.internal.R.styleable.AndroidManifestApplication);

    ...

    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();
        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);

        } 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);

        } 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);

        } 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);

        } else if (parser.getName().equals("meta-data")) {
            if ((owner.mAppMetaData = parseMetaData(res, parser, owner.mAppMetaData,
                    outError)) == null) {
                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                return false;
            }
        } 
	    ...
    }

    ...

    return true;
}

这个方法内部代码太多,只贴出了四大组件相关的解析代码,在解析到activity这个tag时,就将其转换成Activity,这里的Activity并不是平时开发中的activity,这里的Activity是继承自Component类的,它是PackageParser类的一个静态内部类。PackageParser$Activity类中的ActivityInfo类才是正在的Activity的数据。

http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/content/pm/PackageParser.java

public final static class Activity extends Component<ActivityIntentInfo>{
   public final ActivityInfo info;
	...
}

将manifest中的activity标签中的各个属性以及子标签解析成PackageParser A c t v i t y , 然 后 将 其 存 储 到 o w n e r 这 个 对 象 中 , 这 个 o w n e r 是 P a c k a g e P a r s e r Actvity,然后将其存储到owner这个对象中,这个owner是PackageParser Actvity,ownerownerPackageParserPackage类型的对象,下面看看PackageParser$Package类

http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/content/pm/PackageParser.java

public final static class Package implements Parcelable {

    public String packageName;

    public String manifestPackageName;

    ...

    public boolean baseHardwareAccelerated;

    public ApplicationInfo applicationInfo = new ApplicationInfo();

    public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
    public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);
    public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
    public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
    public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
    public final ArrayList<Service> services = new ArrayList<Service>(0);
    public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);

    public final ArrayList<String> requestedPermissions = new ArrayList<String>();

   ...
}

这个类中,存储了很多的解析manifest文件中的信息,由于代码太多,这里只贴出了部分代码。可以看到,里面有activities,receivers,providers,services这四大组件的信息,都存储到这4个ArrayList集合中了。这样就解析完了base Apk的信息,下面接着看parseClusterPackage方法的关键处3:


http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/content/pm/PackageParser.java
private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
    //关键代码1
    final PackageLite lite = parseClusterPackageLite(packageDir, 0);
    ...

    try {
        final AssetManager assets = assetLoader.getBaseAssetManager();
        final File baseApk = new File(lite.baseCodePath);
	//关键代码2
        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)) {
            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);
		//关键代码3
                parseSplitApk(pkg, i, splitAssets, flags);
            }
        }

        ...
        return pkg;
    } catch (IOException e) {
        throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                "Failed to get path: " + lite.baseCodePath, e);
    } finally {
        IoUtils.closeQuietly(assetLoader);
    }
}

在关键代码3处,调用了parseSplitApk方法对split Apk进行解析,parseSplitApk方法内部又调用其重载方法,这个重载方法内部,又调用了parseSplitApplication方法,对application标签中的四大组件等标签进行解析,并将解析的信息存储到PackageParser$Package类中。由于这个过程和解析baseApk类似,所以这就不再贴出
parseSplitApk这个过程的具体代码。

以上便是PMS中对apk的具体解析过程分析。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值