PackageManagerService(2)扫描目录并解析APK

PackageManagerService(1)扫描前的准备 文章中,分析了 PKMS ( PackageManagerService ) 在扫描之前的准备工作,这篇文章来分析扫描目录并解析APK的过程。

扫描目录并解析APK这个过程,伴随着大量升级相关的代码,但是我要提醒一句,我们现在分析的目标是系统首次开机,并不会把系统升级的代码混淆在一起分析。

扫描目录,解析APK

    public PackageManagerService(Context context, Installer installer,
                                 boolean factoryTest, boolean onlyCore) {
        synchronized (mInstallLock) {
            // writer
            synchronized (mPackages) {
                // 判断是否首次开机
                mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));

                // 承接上文继续分析...

                // 创建缓存目录,用于缓存扫描结果
                mCacheDir = preparePackageParserCache(); 

                int scanFlags = SCAN_BOOTING | SCAN_INITIAL;

                if (mIsUpgrade || mFirstBoot) {
                    // 注意,首次开机还要加上这个扫描flag
                    scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
                }                

                // 扫描目录并解析APK
                //先扫描Overlay APK
                scanDirTracedLI(new File(VENDOR_OVERLAY_DIR),
                        mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR,
                        scanFlags | SCAN_AS_SYSTEM | SCAN_AS_VENDOR,
                        0);

                // 还会扫描其它分区的overlay目录,例如/product/overlay, /product_services/overlay 等等。
                // ...

                // 保存静态overlay包的信息,这些信息会在后面解析APK时用到
                mParallelPackageParserCallback.findStaticOverlayPackages();

                // /system/framework目录,这个目录下只有一个apk,那就是framework-res.apk
                scanDirTracedLI(frameworkDir,
                        mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR,
                        scanFlags | SCAN_NO_DEX | SCAN_AS_SYSTEM | SCAN_AS_PRIVILEGED,
                        0);

                // 扫描其它分区的目录, 方法与上面的一致....

            }
        }
    }

在扫描之前,会通过preparePackageParserCache()创建一个缓存目录,用来缓存扫描APK结果。如此一来,下次开机,如果缓存有效,就可以直接从缓存中获取apk信息,而不用再扫描APK了,这样加快了不是首次开机的速度。

开始扫描APK时,首先扫描的是Overlay APK,目录有/vendor/overlay, /product/overlay, /product_services/overlay, /odm/overlay, /oem/overlay。因此如果你想生成Overlay APK,那么必须编译到这些目录下。

什么是Overlay APK?Overlay APK是用来覆盖目标APK的资源的,然而仅限于res/values目录下的资源,例如替换应用的名字,主题等等。因此也称为运行时资源覆盖(Runtime Resource Overlay)。

然而,为了保证RRO生效,需要在使用静态的RRO,例如

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.qualcomm.qti.optinoverlay">
    <overlay
        android:isStatic="true"
        android:priority="1"
        android:targetPackage="com.qualcomm.location.XT" />
</manifest>

其中isStatic属性为true表示这个RRO是静态的,它会覆盖目标应用(targetPackage属性指定的应用)的资源。

mParallelPackageParserCallback.findStaticOverlayPackages()方法保存的就是静态RRO。这个将在后面解析APK时用到。

本文的目标是PackageManagerService扫描并解析APK,至于Overlay APK的代码,还请大家自行分析。

那么现在我们来看看scanDirTracedLI()是如何扫描目录并解析APK的

    private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
        final File[] files = scanDir.listFiles();
        if (ArrayUtils.isEmpty(files)) {
            Log.d(TAG, "No files in app dir " + scanDir);
            return;
        }
        /**
         * mCacheDir用于缓存apk信息
         * mParallelPackageParserCallback用于在解析时提供overlay资源
         */
        try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
                mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
                mParallelPackageParserCallback)) {
            int fileCount = 0;
            for (File file : files) {
                // 必须是.apk或者目录
                final boolean isPackage = (isApkFile(file) || file.isDirectory())
                        && !PackageInstallerService.isStageName(file.getName());
                if (!isPackage) {
                    continue;
                }
                // 1. 提交APK文件,并行解析
                parallelPackageParser.submit(file, parseFlags);
                fileCount++;
            }

            // 2. 处理扫描结果
            for (; fileCount > 0; fileCount--) {
                // APK数据保存在ParseResult.pkg中,类型为PackageParser.Package
                ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
                Throwable throwable = parseResult.throwable;
                int errorCode = PackageManager.INSTALL_SUCCEEDED;

                if (throwable == null) {
                    // 使用<static-library>生成的静态库,需要重新设置包名,形式为package_libVersion
                    if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {
                        renameStaticSharedLibraryPackage(parseResult.pkg);
                    }
                    try {
                        // 扫描,并返回一个新的结果
                        // 注意最后一个参数user,值为null
                        scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
                                currentTime, null);
                    } 
                    // ...
                } 
				// ...
            }
        }
    }

ParallelPackageParser 是一个并行的APK解析器,通过ParallelPackageParser#submit() 方法,向线程池提交待解析的APK,然后通过ParallelPackageParser#take() 来不断地获取结果,用于扫描处理。

提交并解析APK

现在来看下 ParallelPackageParser#submit() 如何提交并解析APK

    public void submit(File scanFile, int parseFlags) {
        mService.submit(() -> {
            // ParseResult只有三个变量
            // pkg变量保存扫描的结果
            // scanFile保存扫描的文件
            // throwable保存扫描出现的异常
            ParseResult pr = new ParseResult();
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
            try {
                PackageParser packageParser = new PackageParser();
                packageParser.setSeparateProcesses(mSeparateProcesses);
                packageParser.setOnlyCoreApps(mOnlyCore);
                packageParser.setDisplayMetrics(mMetrics);
                packageParser.setCacheDir(mCacheDir);
                packageParser.setCallback(mPackageParserCallback);
                pr.scanFile = scanFile;
                // 通过PackageParser解析APK,并返回解析结果
                // 解析结果的类型为 PackageParser.Package
                pr.pkg = parsePackage(packageParser, scanFile, parseFlags);
            } catch (Throwable e) {
                pr.throwable = e;
            } finally {
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
            try {
                // 把解析的结果添加到队列中
                mQueue.put(pr);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                mInterruptedInThread = Thread.currentThread().getName();
            }
        });
    }

这里向线程池中提交了一个请求,在这个请求中,会通过parsePackage()解析APK,然后把解析结果添加到一个类型为BlockingQueuemQueue中。后面在处理解析结果的时候,那个解析结果就是从mQueue中获取的。

现在来看下 parsePackage() 是如何解析APK的

    protected PackageParser.Package parsePackage(PackageParser packageParser, File scanFile,
            int parseFlags) throws PackageParser.PackageParserException {
        // 第三个参数表示是否使用缓存,这样会提升开机效率
        return packageParser.parsePackage(scanFile, parseFlags, true /* useCaches */);
    }

原来就是通过PackageParser#parsePackage()来解析的,注意第三个参数的值为 true ,它代表对解析结果使用缓存。

	// frameworks/base/core/java/android/content/pm/PackageParser.java
	
    public Package parsePackage(File packageFile, int flags, boolean useCaches)
            throws PackageParserException {
        // 1. 获取有效的缓存结果
        Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;
        if (parsed != null) {
            return parsed;
        }

        long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
        if (packageFile.isDirectory()) {
            parsed = parseClusterPackage(packageFile, flags);
        } else {
            // 2. 解析APK
            parsed = parseMonolithicPackage(packageFile, flags);
        }

        long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
        // 3. 把解析的结果进行缓存
        // 原理是把解析的数据分解为字节数组,然后写入到缓存文件中
        cacheResult(packageFile, flags, parsed);
        return parsed;
    }

从这里可以看出,整个解析过程使用了缓存,但是本文不打算分析这个缓存的机制,有兴趣的同学可以研究下,并不难。

而解析APK使用的是parseMonolithicPackage()

    public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
        // 这里只是简单解析了AndroidManifest.xml的某些属性,这里主要是为了Split APK功能,本文不讨论
        final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);

        // ...

        final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
        try {
            final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);
            // PackageParser.Package.codePath存储了apk的全路径
            pkg.setCodePath(apkFile.getCanonicalPath());
            pkg.setUse32bitAbi(lite.use32bitAbi);
            return pkg;
        }

        // ...
    }

这一步解析加入了Split APK解析功能,如果大家想了解Split APK功能是如何实现的,看完本文可自行分析。

那么接下来是使用parseBaseApk()来继续解析APK

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

        // ...

        mParseError = PackageManager.INSTALL_SUCCEEDED;
        mArchiveSourcePath = 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);
            }

            // 1. 打开AndroidManifest.xml的解析器
            parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);

            // 2. 获取APK的资源
            final Resources res = new Resources(assets, mMetrics, null);

            final String[] outError = new String[1];

            // 3. 解析AndroidManifest.xml
            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;

        }

        // ...
    }

第一步,打开了AndroidManifest.xml的解析器,第二步,获取了APK的资源 ,第三步正式解析AndroidManifest.xml

    private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
                                 String[] outError) throws XmlPullParserException, IOException {
        // 省略Split APK功能的代码...

        if (mCallback != null) {
            // 这里获取的就是刚才解析的静态RRO路径
            String[] overlayPaths = mCallback.getOverlayPaths(pkgName, apkPath);
            if (overlayPaths != null && overlayPaths.length > 0) {
                for (String overlayPath : overlayPaths) {
                    // 向Resource添加Overlay资源,如此一来,RRO资源是生效了
                    res.getAssets().addOverlayPath(overlayPath);
                }
            }
        }

        // 这个Package就是保存解析APK结果的地方
        final Package pkg = new Package(pkgName);

        // 以下解析并保存了<manifest>标签的属性
        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();

        // 继续解析
        return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
    }

在这一步中,我们可以看到,它把静态的RRO资源加入到了APK资源中,如此一来,RRO资源就会覆盖APK的资源 。

之后创建了一个PackageParser.Package对象,这个对象就是用来保存解析结果的,在这一步中,解析了<manifest>标签的属性。然后通过parseBaseApkCommon()继续解析

我们在看代码的时候 ,一定是思路明确,搞清楚代码到底在做什么,这样就会形成一个完整的逻辑。

    private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
                                       XmlResourceParser parser, int flags, String[] outError) throws ...{
        TypedArray sa = res.obtainAttributes(parser,
                com.android.internal.R.styleable.AndroidManifest);

        // 解析sharedUserId属性
        String str = sa.getNonConfigurationString(
                com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
        if (str != null && str.length() > 0) {
            String nameError = validateName(str, true, true);
            if (nameError != null && !"android".equals(pkg.packageName)) {
                outError[0] = "<manifest> specifies bad sharedUserId name \""
                        + str + "\": " + nameError;
                mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
                return null;
            }
            pkg.mSharedUserId = str.intern();
            pkg.mSharedUserLabel = sa.getResourceId(
                    com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
        }

        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
            if (tagName.equals(TAG_APPLICATION)) {
                // ...

                foundApp = true;
                // 解析<application>各种属性以及各种子标签
                if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
                    return null;
                }
            } else if (tagName.equals(TAG_OVERLAY)) {

            } else if (tagName.equals(TAG_PERMISSION)) {

            } else if (tagName.equals(TAG_ORIGINAL_PACKAGE)) {

            }

            // ...
        }

        return pkg;
    }

从整体看,这一步主要就是为解析<manifest>下的各种子标签,例如我们最熟悉的<application>标签。

如果你想知道Overlay APK的事情,在这里可以分析下TAG_OVERLAY 分支的代码。

当然,我不可能把这里所有标签都分析完,这肯定不现实。但是后面的分析可能会用到,那怎么办呢?我们可以大致看一下有个映象,如果后面分析到相关的地方,再回头来查询一下即可。用一句话总结就是,按需分析。这样可以大提升了我们分析源码的效率。

现在,对于提交APK解析这一步,解析APK过程已经分析完毕,让我们回到这一步的出发点的代码

    public void submit(File scanFile, int parseFlags) {
        mService.submit(() -> {
            // ParseResult只有三个变量
            // pkg变量保存扫描的结果
            // scanFile保存扫描的文件
            // throwable保存扫描出现的异常
            ParseResult pr = new ParseResult();
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
            try {
                PackageParser pp = new PackageParser();
                pp.setSeparateProcesses(mSeparateProcesses);
                pp.setOnlyCoreApps(mOnlyCore);
                pp.setDisplayMetrics(mMetrics);
                pp.setCacheDir(mCacheDir);
                pp.setCallback(mPackageParserCallback);
                pr.scanFile = scanFile;
                // 1. 解析APK,并将结果保存到ParseResult.pkg中
                pr.pkg = parsePackage(pp, scanFile, parseFlags);
            } catch (Throwable e) {
                pr.throwable = e;
            } finally {
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
            try {
                // 2. 把解析的结果添加到队列中
                mQueue.put(pr);
            } catch (InterruptedException e) {
                // ...
            }
        });
    }

解析APK完成后,就会把结果添加到mQueue中。那么在下一步中,会不断从mQueue中取出结果来处理。

framework的分析是一个异常艰辛的过程,每次分析完一大步后,需要给自己一点点掌声鼓励自己继续坚持下去。

处理APK解析结果

让我们再回到扫描开始的地方

    private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
        final File[] files = scanDir.listFiles();
        if (ArrayUtils.isEmpty(files)) {
            Log.d(TAG, "No files in app dir " + scanDir);
            return;
        }
        /**
         * mCacheDir用于缓存apk信息
         * mParallelPackageParserCallback用于在解析时提供overlay资源
         */
        try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
                mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
                mParallelPackageParserCallback)) {
            int fileCount = 0;
            for (File file : files) {
                // 必须是.apk或者目录
                final boolean isPackage = (isApkFile(file) || file.isDirectory())
                        && !PackageInstallerService.isStageName(file.getName());
                if (!isPackage) {
                    continue;
                }
                // 1. 提交APK文件,并行解析
                parallelPackageParser.submit(file, parseFlags);
                fileCount++;
            }

            // 2. 处理扫描结果
            for (; fileCount > 0; fileCount--) {
                // APK数据保存在ParseResult.pkg中,类型为PackageParser.Package
                ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
                Throwable throwable = parseResult.throwable;
                int errorCode = PackageManager.INSTALL_SUCCEEDED;

                if (throwable == null) {
                    // 使用<static-library>生成的静态库,需要重新设置包名,形式为package_libVersion
                    if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {
                        renameStaticSharedLibraryPackage(parseResult.pkg);
                    }
                    try {
                        // 扫描,并返回一个新的结果
                        // 注意最后一个参数user,值为null
                        scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
                                currentTime, null);
                    } 
                    // ...
                } 
				// ...
            }
        }
    }

现在我们已经分析完了APK解析的过程,现在来分析处理处理结果的过程,这个处理过程包含了大量的系统升级的代码,这个过程也是异常的复杂。但是本文的目标是系统首次开机,因此我会过滤掉系统升级的代码。现在我们来看看scanPackageChildLI()如何处理扫描的结果

    private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg,
                                                     final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
                                                     @Nullable UserHandle user)
            throws PackageManagerException {

        if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
            // <application>下的<package>标签只对debug版本有用
            // 可以发现SCAN_CHECK_ONLY与<package>相关,也就是与debug版本有关
            if (pkg.childPackages != null && pkg.childPackages.size() > 0) {
                scanFlags |= SCAN_CHECK_ONLY;
            }
        } else {
            scanFlags &= ~SCAN_CHECK_ONLY;
        }

        // Scan the parent
        // 继续处理扫描的结果
        PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags,
                scanFlags, currentTime, user);

        // Scan the children
        // <application>下的<package>标签只对debug版本有用
        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
        for (int i = 0; i < childCount; i++) {
            PackageParser.Package childPackage = pkg.childPackages.get(i);
            addForInitLI(childPackage, parseFlags, scanFlags,
                    currentTime, user);
        }

        if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
            return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
        }

        // 然而这个返回结果好像并没有用到
        return scannedPkg;
    }

这一步正如方法名 scanPackageChildLI 所示,处理的是<manifest>下的<package>标签,并且标志位SCAN_CHECK_ONLY也就为此面设置的。但是这个功能只与DEBUG版本有关,大家可以自行查询<pacakge>标签的解析过程,所以以后关于SCAN_CHECK_ONLY这方面的代码一概忽略。

在这一步中,使用了addForInitLI()来继续处理扫描的结果。

    /**
     *  注意,第一个参数pkg指的是解析APK的结果
     */
    private PackageParser.Package addForInitLI(PackageParser.Package pkg,
                                               @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
                                               @Nullable UserHandle user)
        // 省略了大量的关于系统升级的代码 ...

        // 继续处理
        final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags
                | SCAN_UPDATE_SIGNATURE, currentTime, user);

        if (scanResult.success) {
            synchronized (mPackages) {
                boolean appIdCreated = false;
                try {
                    final String pkgName = scanResult.pkgSetting.name;
                    // 对重新扫描的结果做一些调整
                    final Map<String, ReconciledPackage> reconcileResult = reconcilePackagesLocked(
                            new ReconcileRequest(
                                    Collections.singletonMap(pkgName, scanResult),
                                    mSharedLibraries, // 保存了系统配置文件中<library>信息
                                    mPackages, // 系统首次启动时,还是空的
                                    Collections.singletonMap(
                                            pkgName, getSettingsVersionForPackage(pkg)),
                                    Collections.singletonMap(pkgName,
                                            getSharedLibLatestVersionSetting(scanResult))),
                            mSettings.mKeySetManagerService);
                    // 赋予uid
                    appIdCreated = optimisticallyRegisterAppId(scanResult);
                    // 提交信息进行保存
                    commitReconciledScanResultLocked(reconcileResult.get(pkgName));
                } catch (PackageManagerException e) {
                    if (appIdCreated) {
                        cleanUpAppIdCreation(scanResult);
                    }
                    throw e;
                }
            }
        }

        // ...

        return scanResult.pkgSetting.pkg;
    }

这里省略了大量的关于系统升级的代码,剩下的这些就是关于首次开机过程中,处理扫描结果的代码,可以看到分为了三步

  1. scanPackageNewLI()用于重新扫描,记住,第一个参数pkg的类型为PackageParser.Package,它代表首次扫描的结果。
  2. reconcilePackagesLocked()对重新扫描的结果进行一些小的调整。
  3. commitReconciledScanResultLocked()提交结果进行保存,例如把结果写到packages.xml中。

下面我们再分三步来解析这个过程。

重新扫描

重新扫描调用如下代码

        final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags
                | SCAN_UPDATE_SIGNATURE, currentTime, user);

可以看到扫描参数scanFlags添加了一个标志位SCAN_UPDATE_SIGNATURE,这表示在这次重新扫描的过程中,需要更新签名信息。

    private ScanResult scanPackageNewLI(@NonNull PackageParser.Package pkg,
                                        final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
                                        @Nullable UserHandle user) throws PackageManagerException {

        // ...

        // 调整标志位
        scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, pkg);


        synchronized (mPackages) {
            // 向PackageParser.Package.applicationInfo中设置一些标志信息,例如
            applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage);
            assertPackageIsValid(pkg, parseFlags, scanFlags);

            SharedUserSetting sharedUserSetting = null;
            // 获取共享进程的SharedUserSetting对象
            if (pkg.mSharedUserId != null) {
                sharedUserSetting = mSettings.getSharedUserLPw(
                        pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
            }

            // 重新封闭一个请求,第一个参数表示上一次扫描的结果
            final ScanRequest request = new ScanRequest(pkg, sharedUserSetting,
                    pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
                    originalPkgSetting, realPkgName, parseFlags, scanFlags,
                    (pkg == mPlatformPackage), user);
            // 发送扫描请求
            return scanPackageOnlyLI(request, mFactoryTest, currentTime);
        }
    }

在开始再次扫描前,通过adjustScanFlags()调整了一下标志位,但是对于首次开机,只对一种特殊情况进行调整。这种情况就是,当扫描vendor/app目录,也就是扫描非特权应用,而这个非特权应用通过sharedUserId属性共享了一个有特权的应用,并且这个应用拥有平台签名,那么扫描的标志位会添加SCAN_AS_PRIVILEGED

然后通过applyPolicy()向上一次的扫描结果中添加一些标志位,例如

  1. 扫描的是系统APK,会为PackageParser.Package.applicationInfo.flags添加ApplicationInfo.FLAG_SYSTEM标志位。
  2. 扫描的是特权目录下的APK,会为PackageParser.Package.applicationInfo.privateFlags添加ApplicationInfo.PRIVATE_FLAG_PRIVILEGED标志位。
  3. 如果APK拥有平台签名,那么会为PackageParser.Package.applicationInfo.privateFlags添加ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY标志位。

最后,封装一个请求ScanRequest,通过scanPackageOnlyLI()再次扫描。

    private static @NonNull
    ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
                                 boolean isUnderFactoryTest, long currentTime)
            throws PackageManagerException {
        // 这个表示上一次扫描APK的结果
        final PackageParser.Package pkg = request.pkg;
        // 首次开机,这个PackageSetting为空
        PackageSetting pkgSetting = request.pkgSetting;
        // 首次开机,值为null
        final PackageSetting disabledPkgSetting = request.disabledPkgSetting;
        // 首次开机,值为null
        final PackageSetting originalPkgSetting = request.originalPkgSetting;
        final @ParseFlags int parseFlags = request.parseFlags;
        final @ScanFlags int scanFlags = request.scanFlags;
        // 这是设置了<original-pacakge>标签的功能,为系统升级功能
        final String realPkgName = request.realPkgName;
        final SharedUserSetting sharedUserSetting = request.sharedUserSetting;
        final UserHandle user = request.user;
        // 包名为android的apk,也就是framework-res.apk
        final boolean isPlatformPackage = request.isPlatformPackage;


        // Initialize package source and resource directories
        final File scanFile = new File(pkg.codePath);
        final File destCodeFile = new File(pkg.applicationInfo.getCodePath());
        final File destResourceFile = new File(pkg.applicationInfo.getResourcePath());


        // <uses-static-library>标签的内容
        String[] usesStaticLibraries = null;
        if (pkg.usesStaticLibraries != null) {
            usesStaticLibraries = new String[pkg.usesStaticLibraries.size()];
            pkg.usesStaticLibraries.toArray(usesStaticLibraries);
        }

        // 首次启动,需要创建PackageSetting
        final boolean createNewPackage = (pkgSetting == null);
        if (createNewPackage) {
            // 这个parentPackage不知哪里赋值的
            final String parentPackageName = (pkg.parentPackage != null)
                    ? pkg.parentPackage.packageName : null;
            final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
            final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
            // 创建PackageSetting
            pkgSetting = Settings.createNewSetting(pkg.packageName, originalPkgSetting,
                    disabledPkgSetting, realPkgName, sharedUserSetting, destCodeFile,
                    destResourceFile, pkg.applicationInfo.nativeLibraryRootDir,
                    pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi,
                    pkg.mVersionCode, pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,
                    user, true /*allowInstall*/, instantApp, virtualPreload,
                    parentPackageName, pkg.getChildPackageNames(),
                    UserManagerService.getInstance(), usesStaticLibraries,
                    pkg.usesStaticLibrariesVersions);
        } else {
            // ...
        }
        
        // ...

        final int userId = (user == null ? UserHandle.USER_SYSTEM : user.getIdentifier());

        // ...

        final int targetSdkVersion =
                ((sharedUserSetting != null) && (sharedUserSetting.packages.size() != 0)) ?
                        sharedUserSetting.seInfoTargetSdkVersion : pkg.applicationInfo.targetSdkVersion;
        final boolean isPrivileged = (sharedUserSetting != null) ?
                sharedUserSetting.isPrivileged() | pkg.isPrivileged() : pkg.isPrivileged();

        pkg.applicationInfo.seInfo = SELinuxMMAC.getSeInfo(pkg, isPrivileged,
                pkg.applicationInfo.targetSandboxVersion, targetSdkVersion);
        pkg.applicationInfo.seInfoUser = SELinuxUtil.assignSeinfoUser(pkgSetting.readUserState(
                userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId));

        // PacakgeParser.Package.mExtras保存了PackageSetting
        pkg.mExtras = pkgSetting;

        // 如果没有声明process属性,那么进程名子就是包名,否则为process定义的名子
        pkg.applicationInfo.processName = fixProcessName(
                pkg.applicationInfo.packageName,
                pkg.applicationInfo.processName);

        if (!isPlatformPackage) {
            // 创建数据保存的目录
            pkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM);
        }

        // ...

        // 系统应用,设置标记PacakgeSetting.isOrphaned标志
        if (isSystemApp(pkg)) {
            pkgSetting.isOrphaned = true;
        }

        // 更新时间
        final long scanFileTime = getLastModifiedTime(pkg);
        if (currentTime != 0) {
            if (pkgSetting.firstInstallTime == 0) {
                pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
            } else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
                pkgSetting.lastUpdateTime = currentTime;
            }
        } else if (pkgSetting.firstInstallTime == 0) {
            // We need *something*.  Take time time stamp of the file.
            pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
        } else if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
            if (scanFileTime != pkgSetting.timeStamp) {
                // A package on the system image has changed; consider this
                // to be an update.
                pkgSetting.lastUpdateTime = scanFileTime;
            }
        }
        pkgSetting.setTimeStamp(scanFileTime);

        // PackageSetting.pkg保存了上一次扫描的结果
        pkgSetting.pkg = pkg;
        // PackageSetting.pkgFlags保存了标志位
        pkgSetting.pkgFlags = pkg.applicationInfo.flags;
        if (pkg.getLongVersionCode() != pkgSetting.versionCode) {
            pkgSetting.versionCode = pkg.getLongVersionCode();
        }

        // ...

        // <static-library> -> Android静态库
        SharedLibraryInfo staticSharedLibraryInfo = null;
        if (!TextUtils.isEmpty(pkg.staticSharedLibName)) {
            staticSharedLibraryInfo = SharedLibraryInfo.createForStatic(pkg);
        }

        // <library> -> Android动态库
        List<SharedLibraryInfo> dynamicSharedLibraryInfos = null;
        if (!ArrayUtils.isEmpty(pkg.libraryNames)) {
            dynamicSharedLibraryInfos = new ArrayList<>(pkg.libraryNames.size());
            for (String name : pkg.libraryNames) {
                dynamicSharedLibraryInfos.add(SharedLibraryInfo.createForDynamic(pkg, name));
            }
        }

        return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
                !createNewPackage /* existingSettingCopied */, staticSharedLibraryInfo,
                dynamicSharedLibraryInfos);
    }

这一步,再次向APK的解析结果PackageParser.Package中添加数据,然后创建了一个 PackageSetting,并且PackageSettingPackageParser.Package相互保存,最后返回一个数据包装类ScanResult表示扫描的结果。

提交扫描结果

我们已经兜兜转转几个来回了,可以已经忘记最初的出发点了。不过没有关系,我们现在处在再次扫描的过程中,只不过再次扫描的动作已经完成,现在要提交再次扫描的结果了。

    private PackageParser.Package addForInitLI(PackageParser.Package pkg,
                                               @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
                                               @Nullable UserHandle user)
        // ...

        // 重新扫描
        final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags
                | SCAN_UPDATE_SIGNATURE, currentTime, user);

        if (scanResult.success) {
            synchronized (mPackages) {
                boolean appIdCreated = false;
                try {
                    final String pkgName = scanResult.pkgSetting.name;
                    // 对重新扫描的结果做一些调整
                    final Map<String, ReconciledPackage> reconcileResult = reconcilePackagesLocked(
                            new ReconcileRequest(
                                    Collections.singletonMap(pkgName, scanResult),
                                    mSharedLibraries, // 保存了系统配置文件中<library>信息
                                    mPackages, // 系统首次启动时,还是空的
                                    Collections.singletonMap(
                                            pkgName, getSettingsVersionForPackage(pkg)),
                                    Collections.singletonMap(pkgName,
                                            getSharedLibLatestVersionSetting(scanResult))),
                            mSettings.mKeySetManagerService);
                    // 赋予uid
                    appIdCreated = optimisticallyRegisterAppId(scanResult);
                    // 提交信息进行保存
                    commitReconciledScanResultLocked(reconcileResult.get(pkgName));
                } catch (PackageManagerException e) {
                    if (appIdCreated) {
                        cleanUpAppIdCreation(scanResult);
                    }
                    throw e;
                }
            }
        }

        // ...

        return scanResult.pkgSetting.pkg;
    }

前面我们已经对scanPackageNewLI()的再次扫描过程进行了分析,现在我们继续往下看,reconcilePackagesLocked()对扫描的结果再次进行了调整和包装

    private static Map<String, ReconciledPackage> reconcilePackagesLocked(
            final ReconcileRequest request, KeySetManagerService ksms)
            throws ReconcileFailure {
        // 这个Map中的ScanResult就是重新扫描的结果,String为包名
        final Map<String, ScanResult> scannedPackages = request.scannedPackages;

        // 这就是返回值
        final Map<String, ReconciledPackage> result = new ArrayMap<>(scannedPackages.size());

        // make a copy of the existing set of packages so we can combine them with incoming packages
        // request.allPackages就是 mPackages
        // 这个combinedPackages表示所有的已经解析的过的APK的集合
        final ArrayMap<String, PackageParser.Package> combinedPackages =
                new ArrayMap<>(request.allPackages.size() + scannedPackages.size());
        combinedPackages.putAll(request.allPackages);

        // 这个incomingSharedLibraries保存了<static-library>和<library>信息
        final Map<String, LongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
                new ArrayMap<>();

        for (String installPackageName : scannedPackages.keySet()) {
            // 这个就是重新扫描的结果
            final ScanResult scanResult = scannedPackages.get(installPackageName);

            // 把解析过的APK数据加入到combinedPackages
            combinedPackages.put(scanResult.pkgSetting.name, scanResult.request.pkg);

            // request.sharedLibrarySource就是mSharedLibraries, 保存了系统配置文件中<library>信息
            // 这里优先返回静态库的信息,也是<static-library>,其次是返回<library>信息
            final List<SharedLibraryInfo> allowedSharedLibInfos =
                    getAllowedSharedLibInfos(scanResult, request.sharedLibrarySource);
            final SharedLibraryInfo staticLib = scanResult.staticSharedLibraryInfo;
            if (allowedSharedLibInfos != null) {
                for (SharedLibraryInfo info : allowedSharedLibInfos) {
                    // 把库的信息添加到 incomingSharedLibraries 中
                    if (!addSharedLibraryToPackageVersionMap(incomingSharedLibraries, info)) {
                        throw new ReconcileFailure("Static Shared Library " + staticLib.getName()
                                + " is being installed twice in this set!");
                    }
                }
            }

           
            if (isInstall && prepareResult.replace && !prepareResult.system) {
                // 省略APK的安装流程
            } else {
                deletePackageAction = null;
            }

            // 省略更新签名信息的流程...

            result.put(installPackageName,
                    new ReconciledPackage(request, installArgs, scanResult.pkgSetting,
                            res, request.preparedPackages.get(installPackageName), scanResult,
                            deletePackageAction, allowedSharedLibInfos, signingDetails,
                            sharedUserSignaturesChanged, removeAppKeySetData));
        }

        // 如果安装的APK是一个库,这里会收集库的信息,代码省略....

        return result;
    }

这一步,其实提炼了两个信息,一个是签名信息,一个是APK本身为库的信息。APK库的信息提炼到了allowedSharedLibInfos中,签名信息提炼到了signingDetailssharedUserSignaturesChanged中。

然后通过optimisticallyRegisterAppId()为进程赋予了uid,这个过程比较简单,不分析。

最后通过commitReconciledScanResultLocked()提交了最终的结果,注意这个结果的类型为ReconciledPackage,但是最终会调用如下代码

// pkg表示第一次扫描的结果
// oldPkg在首次开机扫描时,为null
// pkgSetting表示再次扫描的结果
// scanFlags表示扫描的参数
// chatty在首次开机扫描时,值为false
// reconciledPkg为再次扫描调整后的结果
commitPackageSettings(pkg, oldPkg, pkgSetting, scanFlags,
    (parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);

参数的注释已经给出,下面我们来看看这个这个提交过程

    private void commitPackageSettings(PackageParser.Package pkg,
                                       @Nullable PackageParser.Package oldPkg, PackageSetting pkgSetting,
                                       final @ScanFlags int scanFlags, boolean chatty, ReconciledPackage reconciledPkg) {
        final String pkgName = pkg.packageName;
        // mCustomResolverComponentName 默认为空
        if (mCustomResolverComponentName != null &&
                mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) {
            setUpCustomResolverActivity(pkg);
        }

        // 针对framework-res.apk
        if (pkg.packageName.equals("android")) {
            synchronized (mPackages) {
                if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
                    // Set up information for our fall-back user intent resolution activity.
                    mPlatformPackage = pkg;
                    pkg.mVersionCode = mSdkVersion;
                    pkg.mVersionCodeMajor = 0;
                    mAndroidApplication = pkg.applicationInfo;
                    if (!mResolverReplaced) {
                        mResolveActivity.applicationInfo = mAndroidApplication;
                        // ResolverActivity 是当启动一个Intent时,有多个Activity匹配时出现的界面
                        mResolveActivity.name = ResolverActivity.class.getName();
                        mResolveActivity.packageName = mAndroidApplication.packageName;
                        mResolveActivity.processName = "system:ui";
                        mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
                        mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
                        mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
                        mResolveActivity.theme = R.style.Theme_Material_Dialog_Alert;
                        mResolveActivity.exported = true;
                        mResolveActivity.enabled = true;
                        mResolveActivity.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
                        mResolveActivity.configChanges = ActivityInfo.CONFIG_SCREEN_SIZE
                                | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE
                                | ActivityInfo.CONFIG_SCREEN_LAYOUT
                                | ActivityInfo.CONFIG_ORIENTATION
                                | ActivityInfo.CONFIG_KEYBOARD
                                | ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
                        mResolveInfo.activityInfo = mResolveActivity;
                        mResolveInfo.priority = 0;
                        mResolveInfo.preferredOrder = 0;
                        mResolveInfo.match = 0;
                        mResolveComponentName = new ComponentName(
                                mAndroidApplication.packageName, mResolveActivity.name);
                    }
                }
            }
        }

        ArrayList<PackageParser.Package> clientLibPkgs = null;
        // writer
        synchronized (mPackages) {
            if (!ArrayUtils.isEmpty(reconciledPkg.allowedSharedLibraryInfos)) {
                // 用 mSharedLibraries 和 mStaticLibsByDeclaringPackage 保存<library>和<static-library>信息
                for (SharedLibraryInfo info : reconciledPkg.allowedSharedLibraryInfos) {
                    commitSharedLibraryInfoLocked(info);
                }
                final Map<String, PackageParser.Package> combinedPackages =
                        reconciledPkg.getCombinedPackages();
                try {
                    // Shared libraries for the package need to be updated.
                    // 用pkg.usesLibraryFiles和pkg.usesLibraryInfos保存<uses-library>和<uses-static-library>信息
                    updateSharedLibrariesLocked(pkg, null, combinedPackages);
                } catch (PackageManagerException e) {
                    Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
                }
                // Update all applications that use this library. Skip when booting
                // since this will be done after all packages are scaned.
                if ((scanFlags & SCAN_BOOTING) == 0) {
                    clientLibPkgs = updateAllSharedLibrariesLocked(pkg, combinedPackages);
                }
            }
        }

        // ...

        // writer
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");

        synchronized (mPackages) {

            // 用Settings中的mPackage保存
            mSettings.insertPackageSettingLPw(pkgSetting, pkg);

            // 保存到PKMS的mPackages数据结构中
            mPackages.put(pkg.applicationInfo.packageName, pkg);

            KeySetManagerService ksms = mSettings.mKeySetManagerService;
            // 这个好像是保存public key
            ksms.addScannedPackageLPw(pkg);

            // ComponentResolver保存了四在组件的信息,以用于提供匹配
            mComponentResolver.addAllComponents(pkg, chatty);

            if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
                // ...
            } else {
                // PermissionManagerService中用PermissionSetting保存<permission-group>信息
                mPermissionManager.addAllPermissionGroups(pkg, chatty);
            }

            if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
                // ...
            } else {
                // PermissionManagerService用PermissionSetting保存<permission>和<permission-tree>信息
                mPermissionManager.addAllPermissions(pkg, chatty);
            }

            // <instrumentation>信息
            int collectionSize = pkg.instrumentation.size();
            StringBuilder r = null;
            int i;
            for (i = 0; i < collectionSize; i++) {
                PackageParser.Instrumentation a = pkg.instrumentation.get(i);
                a.info.packageName = pkg.applicationInfo.packageName;
                // ...
                a.info.secondaryNativeLibraryDir = pkg.applicationInfo.secondaryNativeLibraryDir;
                // mInstrumentation保存了<instrumentation>
                mInstrumentation.put(a.getComponentName(), a);
            }

            if (pkg.protectedBroadcasts != null) {
                collectionSize = pkg.protectedBroadcasts.size();
                synchronized (mProtectedBroadcasts) {
                    for (i = 0; i < collectionSize; i++) {
                        // 保护广播,全部保存到PKMS的mProtectedBroadcasts中
                        mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
                    }
                }
            }

            // ...
        }
    }

现在总结下,数据的保存地方

  1. APK再次扫描的结果PackageSetting,保存到了Settings.mPackages中,也保存到PackageManagerService的mPackages中。
  2. 如果APK是一个库,它被保存到了PackageManagerService.mSharedLibraries和PackageManagerService.mStaticLibsByDeclaringPackage中。
  3. 四大组件的信息,保存到了ComponentResolver.mActivities, ComponentResolver.mProviders, ComponentResolver.mReceivers, ComponentResolver.mServices中。
  4. APK的权限(<permission>, <permission-tree>, <permission-group>)保存到了PermissionManagerService.mSettings中,具体为PermissionSetting.permissions、PermissionSetting.permissionGroups、PermissionSetting.permissionTrees。
  5. APK声明的<instrumentation>保存到了PackageManagerService.mInsturmentations中。
  6. 保护广播保存到了PackageManagerService.mProtectedBroadcasts中。
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值