Android 9.0 源碼_核心篇 -- 深入研究 PMS 系列(8)之 PackageParser

開篇

核心源碼

關鍵類 路徑
PackageParser.java frameworks/base/core/java/android/content/pm/PackageParser.java
PackageManagerService.java frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

PackageParser

創建解析器

    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
        ... ...

        PackageParser pp = new PackageParser();    // 創建 PackageParser
        
        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);    // 解析APK
            DexMetadataHelper.validatePackageDexMetadata(pkg);
        } catch (PackageParserException e) {
            res.setError("Failed parse during installPackageLI", e);
            return;
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
        ... ...
    }

解析APK

在分析 PackageParser 解析 APK 之前,我們先要了解一下 Split APK 機制!

Split APK機制

Split APK 是 Google 爲解決 65536 上限,以及 APK 安裝包越來越大等問題,在 Android L 中引入的機制。
Split APK 可以將一個龐大的 APK,按屏幕密度,ABI 等形式拆分成多個獨立的 APK,在應用程序更新時,不必下載整個 APK,只需單獨下載某個模塊即可安裝更新。
Split APK 將原來一個 APK 中多個模塊共享同一份資源的模型分離成多個 APK 使用各自的資源,並且可以繼承 Base APK 中的資源,多個 APK 有相同的 data,cache 目錄。

在引入了 Split APK 機制後,APK 有兩種分類

Single APK:安裝文件爲一個完整的 APK,即 Base APK。Android 稱其爲 Monolithic。
Mutiple APK:安裝文件在一個文件目錄中,其內部有多個被拆分的 APK,這些 APK 由一個 Base APK 和一個或多個Split APK組成。Android 稱其爲 Cluster。

parsePackage

    /**
     * Equivalent to {@link #parsePackage(File, int, boolean)} with {@code useCaches == false}.
     */
    public Package parsePackage(File packageFile, int flags) throws PackageParserException {
        return parsePackage(packageFile, flags, false /* useCaches */);
    }

新增一個 useCaches 參數:

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

        long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
        // 如果解析的 packageFile 是一個目錄,則調用 parseClusterPackage
        if (packageFile.isDirectory()) {
            parsed = parseClusterPackage(packageFile, flags);
        } else {
            // 如果是單個 APK 文件,則調用 parseMonolithicPackage
            parsed = parseMonolithicPackage(packageFile, flags);
        }

        long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
        cacheResult(packageFile, flags, parsed);
        if (LOG_PARSE_TIMINGS) {
            parseTime = cacheTime - parseTime;
            cacheTime = SystemClock.uptimeMillis() - cacheTime;
            if (parseTime + cacheTime > LOG_PARSE_TIMINGS_THRESHOLD_MS) {
                Slog.i(TAG, "Parse times for '" + packageFile + "': parse=" + parseTime
                        + "ms, update_cache=" + cacheTime + " ms");
            }
        }
        return parsed;
    }

我們這邊選取 parseClusterPackage 作爲分析的分支,當你搞懂這個方法的具體邏輯,單個 APK 的分析自然更簡單了。

parseClusterPackage

一起來瞅瞅源碼:

    private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
        /*
         * 調用 parseClusterPackageLite 方法用於輕量級解析目錄文件,
         * 之所以要輕量級解析是因爲解析APK是一個複雜耗時的操作,這裏的邏輯並不需要APK所有的信息。
         */
        final PackageLite lite = parseClusterPackageLite(packageDir, 0);
        
        /*
         * mOnlyCoreApps 用來指示 PackageParser 是否只解析“核心”應用,
         * “核心”應用指的是 AndroidManifest 中屬性 coreApp 值爲 true,只解析“核心”應用是爲了創建一個極簡的啓動環境,
         * 可以通過 PackageParser 的 setOnlyCoreApps 方法來設置 mOnlyCoreApps 的值。
         *
         * lite.coreApp 表示當前包是否包含“核心”應用,如果不滿足條件就會拋出異常。
         */
        if (mOnlyCoreApps && !lite.coreApp) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                    "Not a coreApp: " + packageDir);
        }

        // Build the split dependency tree.
        SparseArray<int[]> splitDependencies = null;
        final SplitAssetLoader assetLoader;
        if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
            try {
                splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
                assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
            } catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
            }
        } else {
            assetLoader = new DefaultSplitAssetLoader(lite, flags);
        }

        try {
            final AssetManager assets = assetLoader.getBaseAssetManager();
            final File baseApk = new File(lite.baseCodePath);
            // 解析 base APK
            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)) {
                // 獲取 split APK 的數量
                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);
                    // 解析每個 split APK
                    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);
        }
    }

parseClusterPackageLite

    static PackageLite parseClusterPackageLite(File packageDir, int flags)
            throws PackageParserException {
        ... ...

        for (File file : files) {
            if (isApkFile(file)) {
                // 通過 parseApkLite 方法解析每個 Mutiple APK,得到每個 Mutiple APK 對應的 ApkLite(輕量級 APK 信息)
                final ApkLite lite = parseApkLite(file, flags);
                ... ...
            }
        }
        ... ...

        final String codePath = packageDir.getAbsolutePath();
        // 將這些 ApkLite 封裝爲一個 PackageLite(輕量級包信息)並返回
        return new PackageLite(codePath, baseApk, splitNames, isFeatureSplits, usesSplitNames,
                configForSplits, splitCodePaths, splitRevisionCodes);
    }

parseBaseApk

    private static final String MNT_EXPAND = "/mnt/expand/";

    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());
            // 如果 APK 的路徑以 /mnt/expand/ 開頭,就截取該路徑獲取 volumeUuid
            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];
            // 再次調用 parseBaseApk 方法
            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);             // 用於以後標識這個解析後的 Package
            pkg.setApplicationVolumeUuid(volumeUuid);  // 用於標識該 App 所在的存儲卷 UUID
            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 的重載方法,可以看出當前的 parseBaseApk 方法主要是爲了獲取和設置 volumeUuid。

parseBaseApk重載

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

        // 創建 Package 對象
        final Package pkg = new Package(pkgName);
       
        //從資源中提取自定義屬性集 com.android.internal.R.styleable.AndroidManifest 得到 TypedArray 
        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();
        }

        // 讀取 APK 的 AndroidManifest 中的 coreApp 的值
        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);
    }

parseBaseApkCommon

最終調用了 parseBaseApkCommon 方法,這個方法主要用來解析APK的AndroidManifest中的各個標籤,比如 application、permission、uses-sdk、feature-group 等等。

    private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
            XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
            IOException {
        ... ...

        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
            ... ...

            // private static final String TAG_APPLICATION = "application";
            if (tagName.equals(TAG_APPLICATION)) {
                if (foundApp) {
                    if (RIGID_PARSER) {
                        outError[0] = "<manifest> has more than one <application>";
                        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                        return null;
                    } else {
                        Slog.w(TAG, "<manifest> has more than one <application>");
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    }
                }

                foundApp = true;
                // 其中四大組件的標籤在 application 標籤下,解析 application 標籤的方法爲 parseBaseApplication。
                if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
                    return null;
                }
            } else if (tagName.equals(TAG_OVERLAY)) {
            ... ...
        }
        ... ...
    }

parseBaseApplication

    // parseBaseApplication 方法很長,我們這裏只截取了解析四大組件相關的代碼。
    private boolean parseBaseApplication(Package owner, Resources res,
            XmlResourceParser parser, int flags, String[] outError)
        throws XmlPullParserException, IOException {
        ... ...

        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")) {
                // 調用 parseActivity 方法解析 activity 標籤並得到一個 Activity 對象(PackageParser 的靜態內部類)
                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);
                // 將解析得到的 Activity 對象保存在 Package 的列表 activities 中
                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);
            }
            ... ...
        }
        ... ...
    }

PackageParser 解析 APK 的代碼邏輯非常龐大,基本瞭解本文所講的就足夠了,如果有興趣可以自行看源碼。

parseBaseApk方法主要的解析結構可以理解爲以下簡圖:
1417629-1a9039e0ad81bf21.png

參考

 01. http://liuwangshu.cn/framewor...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章