基于Android P版本PKMS详解二

由以上代码可知,`PKMS` 扫描了很多目录,下面列举几个重点说明:

 ✨ /system/framework :该目录中的文件都是系统库,例如:framework.jar、services.jar、framework-res.apk 等。不过 scanDirTracedLI 只扫描 APK 文件,所以 framework-res.apk 是该目录中唯一被扫描的文件。

 ✨ /system/app :该目录下全是默认的系统应用。例如:Browser.apk、SettingsProvider.apk 等。

 ✨ /vendor/app :该目录中的文件由厂商提供,即全是厂商特定的 APK 文件。

### 4.2.1 scanDirTracedLI

`PKMS` 扫描目录统一调用 `scanDirTracedLI` 方法:

```java {.line-numbers}
    public void scanDirTracedLI(File scanDir,
           final int parseFlags, int scanFlags, long currentTime) {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
        try {
            scanDirLI(scanDir, parseFlags, scanFlags, currentTime);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }
```

重点转移到 `scanDirLI` 方法:

```java {.line-numbers}
    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;
        }
        ...
        // ParallelPackageParser 是 Android O 新增的一个类,可以理解它其实就是一个队列,
        // 收集系统 apk 文件,然后从这个队列里面一个个取出 apk ,调用 PackageParser 解析!
        try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
                mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
                mParallelPackageParserCallback)) {
            // Submit files for parsing in parallel
            int fileCount = 0;
            for (File file : files) {
                final boolean isPackage = (isApkFile(file) || file.isDirectory())
                        && !PackageInstallerService.isStageName(file.getName());
                // 过滤掉非 apk 文件,如果不是则跳过继续扫描
                if (!isPackage) {
                    // Ignore entries which are not packages
                    continue;
                }
                parallelPackageParser.submit(file, parseFlags);
                fileCount++;
            }

            // Process results one by one
            for (; fileCount > 0; fileCount--) {
                ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
                Throwable throwable = parseResult.throwable;
                int errorCode = PackageManager.INSTALL_SUCCEEDED;

                if (throwable == null) {
                    // TODO(toddke): move lower in the scan chain
                    // Static shared libraries have synthetic package names
                    if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {
                        renameStaticSharedLibraryPackage(parseResult.pkg);
                    }
                    try {
                        if (errorCode == PackageManager.INSTALL_SUCCEEDED) {
                            /*
                             * Android P 调用 scanPackageChildLI,
                             * Android O 调用 scanPackageLI,
                             *
                             * 调用 scanPackageChildLI 方法扫描一个特定的 apk 文件,
                             * 返回值是 PackageParser 的内部类 Package,
                             * 该类的实例代表一个 APK 文件,所以它就是和 apk 文件对应的数据结构。
                             */
                            scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
                                    currentTime, null);
                        }
                    } catch (PackageManagerException e) {
                        errorCode = e.error;
                        Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
                    }
                } else if (throwable instanceof PackageParser.PackageParserException) {
                    PackageParser.PackageParserException e = (PackageParser.PackageParserException)
                            throwable;
                    errorCode = e.error;
                    Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage());
                } else {
                    throw new IllegalStateException("Unexpected exception occurred while parsing "
                            + parseResult.scanFile, throwable);
                }

                // Delete invalid userdata apps
                // 如果是非系统 apk 并且解析失败
                if ((scanFlags & SCAN_AS_SYSTEM) == 0 &&
                        errorCode != PackageManager.INSTALL_SUCCEEDED) {
                    logCriticalInfo(Log.WARN,
                            "Deleting invalid package at " + parseResult.scanFile);
                    // 非系统 Package 扫描失败,删除文件
                    removeCodePathLI(parseResult.scanFile);
                }
            }
        }
    }
```

看下 `ParallelPackageParser` 这个类:

> frameworks/base/services/core/java/com/android/server/pm/ParallelPackageParser.java

```java {.line-numbers}
/**
 * Helper class for parallel parsing of packages using {@link PackageParser}.
 * <p>Parsing requests are processed by a thread-pool of {@link #MAX_THREADS}.
 * At any time, at most {@link #QUEUE_CAPACITY} results are kept in RAM</p>
 */
class ParallelPackageParser implements AutoCloseable {

    private static final int QUEUE_CAPACITY = 10;
    private static final int MAX_THREADS = 4;

    private final String[] mSeparateProcesses;
    private final boolean mOnlyCore;
    private final DisplayMetrics mMetrics;
    private final File mCacheDir;
    private final PackageParser.Callback mPackageParserCallback;
    private volatile String mInterruptedInThread;

    // BlockingQueue 是一个 FIFO(先进先出)的 Queue(队列),是设计用来实现生产者-消费者队列的。
    // 它提供了可阻塞的插入和移除方法,当队列容器已满,生产者线程会被阻塞,直到队列未满;
    // 当队列容器为空时,消费者线程会被阻塞,直至队列非空时为止。
    // ArrayBlockingQueue 是由数组实现的有界阻塞队列,创建的时候需要指定 capacity(容量,可以存储的最大的元素个数,因为它不会自动扩容)以及是否为公平锁。
    private final BlockingQueue<ParseResult> mQueue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);

    // 线程池
    private final ExecutorService mService = ConcurrentUtils.newFixedThreadPool(MAX_THREADS,
            "package-parsing-thread", Process.THREAD_PRIORITY_FOREGROUND);

    ParallelPackageParser(String[] separateProcesses, boolean onlyCoreApps,
            DisplayMetrics metrics, File cacheDir, PackageParser.Callback callback) {
        mSeparateProcesses = separateProcesses;
        mOnlyCore = onlyCoreApps;
        mMetrics = metrics;
        mCacheDir = cacheDir;
        mPackageParserCallback = callback;
    }

    static class ParseResult {

        PackageParser.Package pkg; // Parsed package
        File scanFile; // File that was parsed
        Throwable throwable; // Set if an error occurs during parsing

        @Override
        public String toString() {
            return "ParseResult{" +
                    "pkg=" + pkg +
                    ", scanFile=" + scanFile +
                    ", throwable=" + throwable +
                    '}';
        }
    }

    /**
     * Take the parsed package from the parsing queue, waiting if necessary until the element
     * appears in the queue.
     * @return parsed package
     */
    public ParseResult take() {
        try {
            if (mInterruptedInThread != null) {
                throw new InterruptedException("Interrupted in " + mInterruptedInThread);
            }
            return mQueue.take();
        } catch (InterruptedException e) {
            // We cannot recover from interrupt here
            Thread.currentThread().interrupt();
            throw new IllegalStateException(e);
        }
    }

    /**
     * Submits the file for parsing
     * @param scanFile file to scan
     * @param parseFlags parse falgs
     */
    public void submit(File scanFile, int parseFlags) {
        mService.submit(() -> {
            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;
                pr.pkg = parsePackage(pp, 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();
                // Propagate result to callers of take().
                // This is helpful to prevent main thread from getting stuck waiting on
                // ParallelPackageParser to finish in case of interruption
                mInterruptedInThread = Thread.currentThread().getName();
            }
        });
    }

    @VisibleForTesting
    protected PackageParser.Package parsePackage(PackageParser packageParser, File scanFile,
            int parseFlags) throws PackageParser.PackageParserException {
        return packageParser.parsePackage(scanFile, parseFlags, true /* useCaches */);
    }

    ...

}
```

`ParallelPackageParser` 是一个并行的 `PackageParser.Package` 解析器,内部使用线程池来完成并行的工作。具体解析操作后面再另行分析,这里先按照解析成功继续分析:

```java {.line-numbers}
    /**
     *  Scans a package and returns the newly parsed package.
     *  @throws PackageManagerException on a parse error.
     */
    private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg,
            final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
            @Nullable UserHandle user)
                    throws PackageManagerException {
        // If the package has children and this is the first dive in the function
        // we scan the package with the SCAN_CHECK_ONLY flag set to see whether all
        // packages (parent and children) would be successfully scanned before the
        // actual scan since scanning mutates internal state and we want to atomically
        // install the package and its children.
        if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
            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
        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;
    }
```

### 4.2.2 addForInitLI

```java {.line-numbers}
    /**
     * Adds a new package to the internal data structures during platform initialization.
     * <p>After adding, the package is known to the system and available for querying.
     * <p>For packages located on the device ROM [eg. packages located in /system, /vendor,
     * etc...], additional checks are performed. Basic verification [such as ensuring
     * matching signatures, checking version codes, etc...] occurs if the package is
     * identical to a previously known package. If the package fails a signature check,
     * the version installed on /data will be removed. If the version of the new package
     * is less than or equal than the version on /data, it will be ignored.
     * <p>Regardless of the package location, the results are applied to the internal
     * structures and the package is made available to the rest of the system.
     * <p>NOTE: The return value should be removed. It's the passed in package object.
     */
    private PackageParser.Package addForInitLI(PackageParser.Package pkg,
            @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
            @Nullable UserHandle user)
                    throws PackageManagerException {
        final boolean scanSystemPartition = ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0)
                // M:operator app also is removable and not system flag
                || sPmsExt.isRemovableSysApp(pkg.packageName);
        final String renamedPkgName;
        final PackageSetting disabledPkgSetting;
        final boolean isSystemPkgUpdated;
        final boolean pkgAlreadyExists;
        PackageSetting pkgSetting;

        ...

        // 判断系统应用是否需要更新
        synchronized (mPackages) {
            renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
            final String realPkgName = getRealPackageName(pkg, renamedPkgName);
            if (realPkgName != null) {
                ensurePackageRenamed(pkg, renamedPkgName);
            }
            final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName);
            final PackageSetting installedPkgSetting = mSettings.getPackageLPr(pkg.packageName);
            pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
            pkgAlreadyExists = pkgSetting != null;
            final String disabledPkgName = pkgAlreadyExists ? pkgSetting.name : pkg.packageName;
            disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName);
            isSystemPkgUpdated = disabledPkgSetting != null;

            ...

            final SharedUserSetting sharedUserSetting = (pkg.mSharedUserId != null)
                    ? mSettings.getSharedUserLPw(pkg.mSharedUserId,
                            0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
                    : null;
            ...

            if (scanSystemPartition) {
                // Potentially prune child packages. If the application on the /system
                // partition has been updated via OTA, but, is still disabled by a
                // version on /data, cycle through all of its children packages and
                // remove children that are no longer defined.
                // 更新子应用
                if (isSystemPkgUpdated) {
                    ...
                }
                ...
            }
        }

        final boolean newPkgChangedPaths =
                pkgAlreadyExists && !pkgSetting.codePathString.equals(pkg.codePath);
        final boolean newPkgVersionGreater =
                //M: change ">" to ">=" can restore the same version apk, test step:
                //1) xunfei app is in operator path and version is 6
                //2) then adb install the upgrade verison 8
                //3) uninstall it(version 8)
                //4) install it version 6 again
                //5) adb reboot
                //6)result: can found xunfei apk verision 6
                pkgAlreadyExists && pkg.getLongVersionCode() >= pkgSetting.versionCode;
        final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
                && newPkgChangedPaths && newPkgVersionGreater;
        if (isSystemPkgBetter) {
            // The version of the application on /system is greater than the version on
            // /data. Switch back to the application on /system.
            // It's safe to assume the application on /system will correctly scan. If not,
            // there won't be a working copy of the application.
            // 更新安装包到 system 分区中
            synchronized (mPackages) {
                // just remove the loaded entries from package lists
                mPackages.remove(pkgSetting.name);
            }

            ...

            // 创建安装参数 InstallArgs
            final InstallArgs args = createInstallArgsForExisting(
                    packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
                    pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
            args.cleanUpResourcesLI();
            synchronized (mPackages) {
                mSettings.enableSystemPackageLPw(pkgSetting.name);
            }
        }

        ...

        // Verify certificates against what was last scanned. If it is an updated priv app, we will
        // force re-collecting certificate.
        final boolean forceCollect = PackageManagerServiceUtils.isApkVerificationForced(
                disabledPkgSetting);
        // Full APK verification can be skipped during certificate collection, only if the file is
        // in verified partition, or can be verified on access (when apk verity is enabled). In both
        // cases, only data in Signing Block is verified instead of the whole file.
        final boolean skipVerify = ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) ||
                (forceCollect && canSkipFullPackageVerification(pkg));
        // 安装包校验
        collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify);

        // Reset profile if the application version is changed
        maybeClearProfilesForUpgradesLI(pkgSetting, pkg);

        /*
         * A new system app appeared, but we already had a non-system one of the
         * same name installed earlier.
         *
         */
        boolean shouldHideSystemApp = false;
        // A new application appeared on /system, but, we already have a copy of
        // the application installed on /data.
        if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
                && !pkgSetting.isSystem()) {

            if (!pkg.mSigningDetails.checkCapability(pkgSetting.signatures.mSigningDetails,
                    PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
                            && !pkgSetting.signatures.mSigningDetails.checkCapability(
                                    pkg.mSigningDetails,
                                    PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
                ...
                try (PackageFreezer freezer = freezePackage(pkg.packageName,
                        "scanPackageInternalLI")) {
                    // 如果两个 apk 签名不匹配,则调用 deletePackageLIF 方法清除 apk 文件及其数据
                    deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null);
                }
                pkgSetting = null;
            } else if (newPkgVersionGreater) {
                // The application on /system is newer than the application on /data.
                // Simply remove the application on /data [keeping application data]
                // and replace it with the version on /system.
                ...
                // 更新系统 apk 程序
                InstallArgs args = createInstallArgsForExisting(
                        packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
                        pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
                synchronized (mInstallLock) {
                    args.cleanUpResourcesLI();
                }
            } else {
                // The application on /system is older than the application on /data. Hide
                // the application on /system and the version on /data will be scanned later
                // and re-added like an update.
                shouldHideSystemApp = true;
                ...
            }
        }

        final PackageParser.Package scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags
                | SCAN_UPDATE_SIGNATURE, currentTime, user);

        // 如果新安装的系统APP 会被旧的APP数据覆盖,所以需要隐藏隐藏系统应用程序,并重新扫描 /data/app 目录
        if (shouldHideSystemApp) {
            synchronized (mPackages) {
                mSettings.disableSystemPackageLPw(pkg.packageName, true);
            }
        }
        return scannedPkg;
    }
```

### 4.2.3 scanPackageNewLI

开始解析 `PackageParser.Package:

> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

```java {.line-numbers}
    ...
    PackageParser.Package mPlatformPackage;
    ...
    // TODO: scanPackageNewLI() and scanPackageOnly() should be merged. But, first, commiting
    // the results / removing app data needs to be moved up a level to the callers of this
    // method. Also, we need to solve the problem of potentially creating a new shared user
    // setting. That can probably be done later and patch things up after the fact.
    @GuardedBy("mInstallLock")
    private PackageParser.Package scanPackageNewLI(@NonNull PackageParser.Package pkg,
            final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
            @Nullable UserHandle user) throws PackageManagerException {

        final String renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
        final String realPkgName = getRealPackageName(pkg, renamedPkgName);
        if (realPkgName != null) {
            ensurePackageRenamed(pkg, renamedPkgName);
        }
        final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName);
        final PackageSetting pkgSetting = mSettings.getPackageLPr(pkg.packageName);
        final PackageSetting disabledPkgSetting =
                mSettings.getDisabledSystemPkgLPr(pkg.packageName);
        ...
        // 根据上述各种 PackageSetting 调整扫描 flag
        scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, pkg);
        synchronized (mPackages) {
            // 应用扫描策略
            applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage);
            // 断言 package 有效
            assertPackageIsValid(pkg, parseFlags, scanFlags);

            SharedUserSetting sharedUserSetting = null;
            if (pkg.mSharedUserId != null) {
                // SIDE EFFECTS; may potentially allocate a new shared user
                sharedUserSetting = mSettings.getSharedUserLPw(
                        pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
                ...
            }

            boolean scanSucceeded = false;
            try {
                final ScanRequest request = new ScanRequest(pkg, sharedUserSetting,
                        pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
                        originalPkgSetting, realPkgName, parseFlags, scanFlags,
                        (pkg == mPlatformPackage), user);
                final ScanResult result = scanPackageOnlyLI(request, mFactoryTest, currentTime);
                if (result.success) {
                    // 将 pkg 中的数据保存到对应的 PMS 变量中,用于以后的管理查询调用等
                    commitScanResultsLocked(request, result);
                }
                scanSucceeded = true;
            ...
            }
        }
        return pkg;
    }
```

### 4.2.4 commitScanResultsLocked

> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

```java {.line-numbers}
     /**
     * Commits the package scan and modifies system state.
     * <p><em>WARNING:</em> The method may throw an excpetion in the middle
     * of committing the package, leaving the system in an inconsistent state.
     * This needs to be fixed so, once we get to this point, no errors are
     * possible and the system is not left in an inconsistent state.
     */
    @GuardedBy("mPackages")
    private void commitScanResultsLocked(@NonNull ScanRequest request, @NonNull ScanResult result)
            throws PackageManagerException {
        final PackageParser.Package pkg = request.pkg;
        final PackageParser.Package oldPkg = request.oldPkg;
        final @ParseFlags int parseFlags = request.parseFlags;
        final @ScanFlags int scanFlags = request.scanFlags;
        final PackageSetting oldPkgSetting = request.oldPkgSetting;
        ...
        final UserHandle user = request.user;
        ...
        final PackageSetting pkgSetting = result.pkgSetting;
        ...
        if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
            if (oldPkgSetting != null) {
                synchronized (mPackages) {
                    mSettings.mPackages.put(oldPkgSetting.name, oldPkgSetting);
                }
            }
        } else {
            final int userId = user == null ? 0 : user.getIdentifier();
            // Modify state for the given package setting
            commitPackageSettings(pkg, oldPkg, pkgSetting, user, scanFlags,
                    (parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/);
            ...
        }
    }
```

### 4.2.5 commitPackageSettings

> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

```java {.line-numbers}
    ...
        // All available activities, for your resolving pleasure.
    final ActivityIntentResolver mActivities =
            new ActivityIntentResolver();

    // All available receivers, for your resolving pleasure.
    final ActivityIntentResolver mReceivers =
            new ActivityIntentResolver();

    // All available services, for your resolving pleasure.
    final ServiceIntentResolver mServices = new ServiceIntentResolver();

    // All available providers, for your resolving pleasure.
    final ProviderIntentResolver mProviders = new ProviderIntentResolver();
    ...
    // Mapping from instrumentation class names to info about them.
    final ArrayMap<ComponentName, PackageParser.Instrumentation> mInstrumentation =
            new ArrayMap<ComponentName, PackageParser.Instrumentation>();
    ...
    // Broadcast actions that are only available to the system.
    @GuardedBy("mProtectedBroadcasts")
    final ArraySet<String> mProtectedBroadcasts = new ArraySet<>();
    ...
    ApplicationInfo mAndroidApplication;
    final ActivityInfo mResolveActivity = new ActivityInfo();
    ...
    ComponentName mResolveComponentName;
    ...
    /**
     * Adds a scanned package to the system. When this method is finished, the package will
     * be available for query, resolution, etc...
     */
    private void commitPackageSettings(PackageParser.Package pkg,
            @Nullable PackageParser.Package oldPkg, PackageSetting pkgSetting, UserHandle user,
            final @ScanFlags int scanFlags, boolean chatty) {
        final String pkgName = pkg.packageName;
        // 可配置的 ResloverActivity
        if (mCustomResolverComponentName != null &&
                mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) {
            setUpCustomResolverActivity(pkg);
        }

        // 针对包名为 "android" 的 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;
                    // 配置系统默认的 ResloverActivity
                    if (!mResolverReplaced) {
                        mResolveActivity.applicationInfo = mAndroidApplication;
                        mResolveActivity.name = ResolverActivity.class.getName();
                        mResolveActivity.packageName = mAndroidApplication.packageName;
                        mResolveActivity.processName = "system:ui";
                        ...
                        mResolveComponentName = new ComponentName(
                                mAndroidApplication.packageName, mResolveActivity.name);
                    }
                }
            }
        }

        ...

        synchronized (mPackages) {
            ...
            // 在此之前,四大组件的信息都是 Package 对象的私有变量,通过下面的代码,
            // 将他们注册到 PackageManagerService 里面,
            // 这样 PackageManagerService 就有了所有的组件信息
            int N = pkg.providers.size();
            StringBuilder r = null;
            int i;
            // 注册 pkg 里面的 provider 到 PackageManagerService 上的 mProvider 上
            for (i=0; i<N; i++) {
                PackageParser.Provider p = pkg.providers.get(i);
                p.info.processName = fixProcessName(pkg.applicationInfo.processName,
                        p.info.processName);
                mProviders.addProvider(p);
                ...
            }
            ...
            N = pkg.services.size();
            r = null;
            // 注册 pkg 中的 service 到 PackageManagerService 的 mServices 上
            for (i=0; i<N; i++) {
                PackageParser.Service s = pkg.services.get(i);
                s.info.processName = fixProcessName(pkg.applicationInfo.processName,
                        s.info.processName);
                mServices.addService(s);
                ...
            }
            ...
            N = pkg.receivers.size();
            r = null;
            // 注册 pkg 里面的 receiver 到 PackageManagerService 上的 mReceivers 上
            for (i=0; i<N; i++) {
                PackageParser.Activity a = pkg.receivers.get(i);
                a.info.processName = fixProcessName(pkg.applicationInfo.processName,
                        a.info.processName);
                mReceivers.addActivity(a, "receiver");
                ...
            }
            ...
            N = pkg.activities.size();
            r = null;
            // 注册 pkg 里面的 activity 到 PackageManagerService 上的 mActivities 上
            for (i=0; i<N; i++) {
                PackageParser.Activity a = pkg.activities.get(i);
                a.info.processName = fixProcessName(pkg.applicationInfo.processName,
                        a.info.processName);
                mActivities.addActivity(a, "activity");
                ...
            }

            ...

            N = pkg.instrumentation.size();
            r = null;
            //  注册 pkg 里面的 instrumentation 到 PackageManagerService 的 mInstrumentation 中
            // Instrumentation 用来跟踪本应用内的 application 及 activity 的生命周期
            for (i=0; i<N; i++) {
                PackageParser.Instrumentation a = pkg.instrumentation.get(i);
                a.info.packageName = pkg.applicationInfo.packageName;
                ...
                mInstrumentation.put(a.getComponentName(), a);
                ...
            }
            ...
            // 如果有包内广播
            if (pkg.protectedBroadcasts != null) {
                N = pkg.protectedBroadcasts.size();
                synchronized (mProtectedBroadcasts) {
                    for (i = 0; i < N; i++) {
                        mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
                    }
                }
            }

            ...
        }
        ...
    }
```

## 4.3 PMS_DATA_SCAN_START

 > frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

```java {.line-numbers}
    ...
    /** Directory where installed applications are stored */
    private static final File sAppInstallDir =
            new File(Environment.getDataDirectory(), "app");
    ...
    /** Directory where code and non-resource assets of forward-locked applications are stored */
    private static final File sDrmAppPrivateInstallDir =
            new File(Environment.getDataDirectory(), "app-private");
    ...
    /**
     * Tracks high priority intent filters for protected actions. During boot, certain
     * filter actions are protected and should never be allowed to have a high priority
     * intent filter for them. However, there is one, and only one exception -- the
     * setup wizard. It must be able to define a high priority intent filter for these
     * actions to ensure there are no escapes from the wizard. We need to delay processing
     * of these during boot as we need to look at all of the system packages in order
     * to know which component is the setup wizard.
     */
    private final List<PackageParser.ActivityIntentInfo> mProtectedFilters = new ArrayList<>();
    /**
     * Whether or not processing protected filters should be deferred.
     */
    private boolean mDeferProtectedFilters = true;
    ...
    final @Nullable String mSetupWizardPackage;
    final @Nullable String mStorageManagerPackage;
    final @Nullable String mSystemTextClassifierPackage;
    ...
    private final PackageUsage mPackageUsage = new PackageUsage();
    private final CompilerStats mCompilerStats = new CompilerStats();
    ...
    public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        ...
        synchronized (mInstallLock) {
        // writer
        synchronized (mPackages) {
            ...
            if (!mOnlyCore) {
                // 第三阶段开始,扫描非系统 package
                EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                        SystemClock.uptimeMillis());
                // 扫描 /data/app 目录
                scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);

                // 扫描 /data/app-private 目录
                scanDirTracedLI(sDrmAppPrivateInstallDir, mDefParseFlags
                        | PackageParser.PARSE_FORWARD_LOCK,
                        scanFlags | SCAN_REQUIRE_KNOWN, 0);

                // Remove disable package settings for updated system apps that were
                // removed via an OTA. If the update is no longer present, remove the
                // app completely. Otherwise, revoke their system privileges.
                // 这里检查用户目录下升级文件是否还存在,然后进行处理
                for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
                    PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
                    // 从 mSettings.mDisabledSysPackages 变量中移除去此应用
                    mSettings.removeDisabledSystemPackageLPw(deletedAppName);
                    final String msg;
                    // 用户目录中也没有升级包,则肯定是残留的应用信息,则把它的数据目录删除掉
                    if (deletedPkg == null) {
                        // should have found an update, but, we didn't; remove everything
                        msg = "Updated system package " + deletedAppName
                                + " no longer exists; removing its data";
                        // Actual deletion of code and data will be handled by later
                        // reconciliation step
                    } else {
                        // found an update; revoke system privileges
                        // 如果在用户空间找到了文件,则说明系统目录下的文件可能被删除了,
                        // 因此,把应用的系统属性去掉,以普通应用的方式运行
                        msg = "Updated system package + " + deletedAppName
                                + " no longer exists; revoking system privileges";

                        // Don't do anything if a stub is removed from the system image. If
                        // we were to remove the uncompressed version from the /data partition,
                        // this is where it'd be done.

                        final PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
                        deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
                        deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
                    }
                    // 报告系统发生了不一致的情况
                    logCriticalInfo(Log.WARN, msg);
                }

                /*
                 * Make sure all system apps that we expected to appear on
                 * the userdata partition actually showed up. If they never
                 * appeared, crawl back and revive the system version.
                 */
                // 处理 mExpectingBetter 列表,这个列表的应用是带有升级包的系统的应用,
                // 前面把他们从 mPackages 列表中清除了并放到 mExpectingBetter 列表,
                // 最后也对他们进行扫描处理,但不会放到 mPackages 中。
                for (int i = 0; i < mExpectingBetter.size(); i++) {
                    final String packageName = mExpectingBetter.keyAt(i);
                    if (!mPackages.containsKey(packageName)) {
                        final File scanFile = mExpectingBetter.valueAt(i);

                        logCriticalInfo(Log.WARN, "Expected better " + packageName
                                + " but never showed up; reverting to system");

                        final @ParseFlags int reparseFlags;
                        final @ScanFlags int rescanFlags;
                        // 确保应用位于下面的系统应用目录中,如果不在,则不需要处理
                        if (FileUtils.contains(privilegedAppDir, scanFile)) {
                            reparseFlags =
                                    mDefParseFlags |
                                    PackageParser.PARSE_IS_SYSTEM_DIR;
                            rescanFlags =
                                    scanFlags
                                    | SCAN_AS_SYSTEM
                                    | SCAN_AS_PRIVILEGED;
                        } else if (FileUtils.contains(systemAppDir, scanFile)) {
                            reparseFlags =
                                    mDefParseFlags |
                                    PackageParser.PARSE_IS_SYSTEM_DIR;
                            rescanFlags =
                                    scanFlags
                                    | SCAN_AS_SYSTEM;
                        ...
                        } else {
                            Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
                            // 如果应用不在上面这些目录,继续循环,不要处理
                            continue;
                        }

                        mSettings.enableSystemPackageLPw(packageName);

                        try {
                            // 重新扫描一下这个文件,会添加一个 <update-package> 标签
                            scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null);
                        } catch (PackageManagerException e) {
                            Slog.e(TAG, "Failed to parse original system package: "
                                    + e.getMessage());
                        }
                    }
                }
                // Uncompress and install any stubbed system applications.
                // This must be done last to ensure all stubs are replaced or disabled.
                decompressSystemApplications(stubSystemApps, scanFlags);

                final int cachedNonSystemApps = PackageParser.sCachedPackageReadCount.get()
                                - cachedSystemApps;

                // 计算扫描时间及数量
                final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
                final int dataPackagesCount = mPackages.size() - systemPackagesCount;
                ...
            }
            mExpectingBetter.clear();

            // Resolve the storage manager.
            mStorageManagerPackage = getStorageManagerPackageName();

            // Resolve protected action filters. Only the setup wizard is allowed to
            // have a high priority filter for these actions.
            // 只有开机向导具有高优先级
            mSetupWizardPackage = getSetupWizardPackageName();
            if (mProtectedFilters.size() > 0) {
                ...
                for (ActivityIntentInfo filter : mProtectedFilters) {
                    if (filter.activity.info.packageName.equals(mSetupWizardPackage)) {
                        ...
                        // skip setup wizard; allow it to keep the high priority filter
                        continue;
                    }
                    ...
                    filter.setPriority(0);
                }
            }

            mSystemTextClassifierPackage = getSystemTextClassifierPackageName();

            mDeferProtectedFilters = false;
            mProtectedFilters.clear();

            // Now that we know all of the shared libraries, update all clients to have
            // the correct library paths.
            updateAllSharedLibrariesLPw(null);

            for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
                // NOTE: We ignore potential failures here during a system scan (like
                // the rest of the commands above) because there's precious little we
                // can do about it. A settings error is reported, though.
                final List<String> changedAbiCodePath =
                        adjustCpuAbisForSharedUserLPw(setting.packages, null /*scannedPackage*/);
                if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
                    for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
                        final String codePathString = changedAbiCodePath.get(i);
                        try {
                            mInstaller.rmdex(codePathString,
                                    getDexCodeInstructionSet(getPreferredInstructionSet()));
                        } catch (InstallerException ignored) {
                        }
                    }
                }
                // Adjust seInfo to ensure apps which share a sharedUserId are placed in the same
                // SELinux domain.
                setting.fixSeInfoLocked();
            }

            // Now that we know all the packages we are keeping,
            // read and update their last usage times.
            mPackageUsage.read(mPackages);
            mCompilerStats.read();

            // 至此,第三阶段结束
            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                    SystemClock.uptimeMillis());
            ...
        }
        }
        ...
    }
```

### 4.3.1 scanPackageTracedLI

 > frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

```java {.line-numbers}
    /**
     *  Traces a package scan.
     *  @see #scanPackageLI(File, int, int, long, UserHandle)
     */
    public PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags,
            int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
        try {
            return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }

    /**
     *  Scans a package and returns the newly parsed package.
     *  Returns {@code null} in case of errors and the error code is stored in mLastScanError
     */
    private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
            long currentTime, UserHandle user) throws PackageManagerException {
        if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
        PackageParser pp = new PackageParser();
        pp.setSeparateProcesses(mSeparateProcesses);
        pp.setOnlyCoreApps(mOnlyCore);
        pp.setDisplayMetrics(mMetrics);
        pp.setCallback(mPackageParserCallback);

        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
        final PackageParser.Package pkg;
        try {
            // 解析 Package
            pkg = pp.parsePackage(scanFile, parseFlags);
        } catch (PackageParserException e) {
            throw PackageManagerException.from(e);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }

        // Static shared libraries have synthetic package names
        if (pkg.applicationInfo.isStaticSharedLibrary()) {
            renameStaticSharedLibraryPackage(pkg);
        }

        return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
    }
```

本阶段主要工作:

- 扫描 /data/app 目录下的apk;

- 扫描 /data/app-private 目录下的apk;

## 4.4 PMS_SCAN_END

 > frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

```java {.line-numbers}
    ...
    public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        ...
        synchronized (mInstallLock) {
        // writer
        synchronized (mPackages) {
            ...
            // 第四阶段开始
            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                    SystemClock.uptimeMillis());
            ...

            // If the platform SDK has changed since the last time we booted,
            // we need to re-grant app permission to catch any new ones that
            // appear.  This is really a hack, and means that apps can in some
            // cases get permissions that the user didn't initially explicitly
            // allow...  it would be nice to have some better way to handle
            // this situation.
            // 当sdk版本不一致时,需要更新权限
            final boolean sdkUpdated = (ver.sdkVersion != mSdkVersion);
            ...
            mPermissionManager.updateAllPermissions(
                    StorageManager.UUID_PRIVATE_INTERNAL, sdkUpdated, mPackages.values(),
                    mPermissionCallback);
            ver.sdkVersion = mSdkVersion;

            ...

            // If this is first boot after an OTA, and a normal boot, then
            // we need to clear code cache directories.
            // Note that we do *not* clear the application profiles. These remain valid
            // across OTAs and are used to drive profile verification (post OTA) and
            // profile compilation (without waiting to collect a fresh set of profiles).
            // 当这是ota后的首次启动,或者正常启动则需要清除目录的缓存代码
            if (mIsUpgrade && !onlyCore) {
                Slog.i(TAG, "Build fingerprint changed; clearing code caches");
                for (int i = 0; i < mSettings.mPackages.size(); i++) {
                    final PackageSetting ps = mSettings.mPackages.valueAt(i);
                    if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
                        // No apps are running this early, so no need to freeze
                        clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
                                StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE
                                        | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
                    }
                }
                ver.fingerprint = Build.FINGERPRINT;
            }

            checkDefaultBrowser();

            // clear only after permissions and other defaults have been updated
            // 当权限和其他默认项都完成更新,则清理相关信息
            mExistingSystemPackages.clear();
            mPromoteSystemApps = false;

            // All the changes are done during package scanning.
            ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;

            // can downgrade to reader
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "write settings");
            // 信息写回 packages.xml 文件
            mSettings.writeLPr();
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            // 至此,第四阶段完成
            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
                    SystemClock.uptimeMillis());
            ...
        }
        }
        ...
    }
```

到此阶段,apk 已安装完成,本阶段主要工作:

- 将 SCAN 阶段的信息写回 /data/system/packages.xml;

## 4.5 PMS_READY

 > frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

```java {.line-numbers}
    ...
    final PackageInstallerService mInstallerService;
    ...
    // DexManager handles the usage of dex files (e.g. secondary files, whether or not a package
    // is used by other apps).
    private final DexManager mDexManager;
    ...
    public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        ...
        synchronized (mInstallLock) {
        // writer
        synchronized (mPackages) {
            ...
            // 第五阶段开始
            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
                    SystemClock.uptimeMillis());

            ...

            // 创建 PackageInstallerService 服务
            mInstallerService = new PackageInstallerService(context, this);

            ...

            // Read and update the usage of dex files.
            // Do this at the end of PM init so that all the packages have their
            // data directory reconciled.
            // At this point we know the code paths of the packages, so we can validate
            // the disk file and build the internal cache.
            // The usage file is expected to be small so loading and verifying it
            // should take a fairly small time compare to the other activities (e.g. package
            // scanning).
            final Map<Integer, List<PackageInfo>> userPackages = new HashMap<>();
            final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
            for (int userId : currentUserIds) {
                userPackages.put(userId, getInstalledPackages(/*flags*/ 0, userId).getList());
            }
            mDexManager.load(userPackages);
            ...
        } // synchronized (mPackages)
        } // synchronized (mInstallLock)

        // Now after opening every single application zip, make sure they
        // are all flushed.  Not really needed, but keeps things nice and
        // tidy.
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "GC");
        Runtime.getRuntime().gc();
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

        // The initial scanning above does many calls into installd while
        // holding the mPackages lock, but we're mostly interested in yelling
        // once we have a booted system.
        mInstaller.setWarnIfHeld(mPackages);

        ...
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
```

### 4.5.1 创建 PKIS 服务

> frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java

```java {.line-numbers}
public class PackageInstallerService extends IPackageInstaller.Stub {
    private static final String TAG = "PackageInstaller";
    ...
    private final Context mContext;
    private final PackageManagerService mPm;
    private final PermissionManagerInternal mPermissionManager;
    ...
    private final HandlerThread mInstallThread;
    private final Handler mInstallHandler;

    private final Callbacks mCallbacks;

    /**
     * File storing persisted {@link #mSessions} metadata.
     */
    private final AtomicFile mSessionsFile;

    /**
     * Directory storing persisted {@link #mSessions} metadata which is too
     * heavy to store directly in {@link #mSessionsFile}.
     */
    private final File mSessionsDir;
    ...
    public PackageInstallerService(Context context, PackageManagerService pm) {
        mContext = context;
        mPm = pm;
        mPermissionManager = LocalServices.getService(PermissionManagerInternal.class);

        mInstallThread = new HandlerThread(TAG);
        mInstallThread.start();

        mInstallHandler = new Handler(mInstallThread.getLooper());

        mCallbacks = new Callbacks(mInstallThread.getLooper());

        mSessionsFile = new AtomicFile(
                new File(Environment.getDataSystemDirectory(), "install_sessions.xml"),
                "package-session");
        mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions");
        mSessionsDir.mkdirs();
    }
}
```

本阶段主要工作:

- 创建 PackageInstallerService 服务;

# 五 Others

## 5.1 PKMS.updatePackagesIfNeeded

 > frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

```java {.line-numbers}
    @Override
    public void updatePackagesIfNeeded() {
        enforceSystemOrRoot("Only the system can request package update");

        // We need to re-extract after an OTA.
        boolean causeUpgrade = isUpgrade();

        // First boot or factory reset.
        // Note: we also handle devices that are upgrading to N right now as if it is their
        //       first boot, as they do not have profile data.
        boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade;

        // We need to re-extract after a pruned cache, as AoT-ed files will be out of date.
        boolean causePrunedCache = VMRuntime.didPruneDalvikCache();

        if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) {
            return;
        }

        List<PackageParser.Package> pkgs;
        synchronized (mPackages) {
            // 获取 dexopt 操作的 app 集合
            pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
        }

        final long startTime = System.nanoTime();
        final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
                    causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
                    false /* bootComplete */);

        final int elapsedTimeSeconds =
                (int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime);

        ..
    }
```

> frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java

```java {.line-numbers}
/**
 * Class containing helper methods for the PackageManagerService.
 *
 * {@hide}
 */
public class PackageManagerServiceUtils {
    private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;

    ...

    // Sort apps by importance for dexopt ordering. Important apps are given
    // more priority in case the device runs out of space.
    public static List<PackageParser.Package> getPackagesForDexopt(
            Collection<PackageParser.Package> packages,
            PackageManagerService packageManagerService) {
        ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages);
        LinkedList<PackageParser.Package> result = new LinkedList<>();
        ArrayList<PackageParser.Package> sortTemp = new ArrayList<>(remainingPkgs.size());

        // Give priority to core apps.
        // 将 pkgs 中的核心 app 添加到 sortTemp
        applyPackageFilter((pkg) -> pkg.coreApp, result, remainingPkgs, sortTemp,
                packageManagerService);

        // Give priority to system apps that listen for pre boot complete.
        // 获取监听 PRE_BOOT_COMPLETE 的系统 app 集合
        Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
        final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
        applyPackageFilter((pkg) -> pkgNames.contains(pkg.packageName), result, remainingPkgs,
                sortTemp, packageManagerService);

        // Give priority to apps used by other apps.
        // 将被依赖的 app 添加到 sortTemp
        DexManager dexManager = packageManagerService.getDexManager();
        applyPackageFilter((pkg) ->
                dexManager.getPackageUseInfoOrDefault(pkg.packageName)
                        .isAnyCodePathUsedByOtherApps(),
                result, remainingPkgs, sortTemp, packageManagerService);

        // Filter out packages that aren't recently used, add all remaining apps.
        // TODO: add a property to control this?
        Predicate<PackageParser.Package> remainingPredicate;
        if (!remainingPkgs.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) {
            if (DEBUG_DEXOPT) {
                Log.i(TAG, "Looking at historical package use");
            }
            // Get the package that was used last.
            PackageParser.Package lastUsed = Collections.max(remainingPkgs, (pkg1, pkg2) ->
                    Long.compare(pkg1.getLatestForegroundPackageUseTimeInMills(),
                            pkg2.getLatestForegroundPackageUseTimeInMills()));
            if (DEBUG_DEXOPT) {
                Log.i(TAG, "Taking package " + lastUsed.packageName + " as reference in time use");
            }
            long estimatedPreviousSystemUseTime =
                    lastUsed.getLatestForegroundPackageUseTimeInMills();
            // Be defensive if for some reason package usage has bogus data.
            if (estimatedPreviousSystemUseTime != 0) {
                final long cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS;
                remainingPredicate =
                        (pkg) -> pkg.getLatestForegroundPackageUseTimeInMills() >= cutoffTime;
            } else {
                // No meaningful historical info. Take all.
                remainingPredicate = (pkg) -> true;
            }
            sortPackagesByUsageDate(remainingPkgs, packageManagerService);
        } else {
            // No historical info. Take all.
            remainingPredicate = (pkg) -> true;
        }
        // 将最近(一周)使用的 app 添加到 sortTemp
        applyPackageFilter(remainingPredicate, result, remainingPkgs, sortTemp,
                packageManagerService);

        ...

        return result;
    }
    ...
}
```

> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

```java {.line-numbers}
    ...
    @GuardedBy("mPackages")
    private boolean mDexOptDialogShown;
    ...
    /*
     * Return the prebuilt profile path given a package base code path.
     */
    private static String getPrebuildProfilePath(PackageParser.Package pkg) {
        return pkg.baseCodePath + ".prof";
    }

    /**
     * Performs dexopt on the set of packages in {@code packages} and returns an int array
     * containing statistics about the invocation. The array consists of three elements,
     * which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped}
     * and {@code numberOfPackagesFailed}.
     */
    private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, boolean showDialog,
            final int compilationReason, boolean bootComplete) {

        int numberOfPackagesVisited = 0;
        int numberOfPackagesOptimized = 0;
        int numberOfPackagesSkipped = 0;
        int numberOfPackagesFailed = 0;
        final int numberOfPackagesToDexopt = pkgs.size();

        for (PackageParser.Package pkg : pkgs) {
            numberOfPackagesVisited++;

            boolean useProfileForDexopt = false;

            // 仅对出厂或 OTA 后首次开机时的系统 app
            if ((isFirstBoot() || isUpgrade()) && isSystemApp(pkg)) {
                // Copy over initial preopt profiles since we won't get any JIT samples for methods
                // that are already compiled.
                File profileFile = new File(getPrebuildProfilePath(pkg));
                // Copy profile if it exists.
                ...
            }

            if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
                ...
                numberOfPackagesSkipped++;
                continue;
            }
            ...

            if (showDialog) {
                try {
                    // 对话框显示"正在优化第 i 个应用(共 n 个)。“
                    ActivityManager.getService().showBootMessage(
                            mContext.getResources().getString(R.string.android_upgrading_apk,
                                    numberOfPackagesVisited, numberOfPackagesToDexopt), true);
                } catch (RemoteException e) {
                }
                synchronized (mPackages) {
                    mDexOptDialogShown = true;
                }
            }

            int pkgCompilationReason = compilationReason;
            if (useProfileForDexopt) {
                // Use background dexopt mode to try and use the profile. Note that this does not
                // guarantee usage of the profile.
                pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
            }

            // checkProfiles is false to avoid merging profiles during boot which
            // might interfere with background compilation (b/28612421).
            // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
            // behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a
            // trade-off worth doing to save boot time work.
            int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0;
            if (compilationReason == REASON_FIRST_BOOT) {
                // TODO: This doesn't cover the upgrade case, we should check for this too.
                dexoptFlags |= DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
            }
            // 执行 dexopt
            int primaryDexOptStaus = performDexOptTraced(new DexoptOptions(
                    pkg.packageName,
                    pkgCompilationReason,
                    dexoptFlags));

            switch (primaryDexOptStaus) {
                case PackageDexOptimizer.DEX_OPT_PERFORMED:
                    numberOfPackagesOptimized++;
                    break;
                case PackageDexOptimizer.DEX_OPT_SKIPPED:
                    numberOfPackagesSkipped++;
                    break;
                case PackageDexOptimizer.DEX_OPT_FAILED:
                    numberOfPackagesFailed++;
                    break;
                default:
                    Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStaus);
                    break;
            }
        }

        return new int[] { numberOfPackagesOptimized, numberOfPackagesSkipped,
                numberOfPackagesFailed };
    }
```

## 5.2 PKMS.performFstrimIfNeeded

> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

```java {.line-numbers}
    ...
    /**
     * Wall-clock timeout (in milliseconds) after which we *require* that an fstrim
     * be run on this device.  We use the value in the Settings.Global.MANDATORY_FSTRIM_INTERVAL
     * settings entry if available, otherwise we use the hardcoded default.  If it's been
     * more than this long since the last fstrim, we force one during the boot sequence.
     *
     * This backstops other fstrim scheduling:  if the device is alive at midnight+idle,
     * one gets run at the next available charging+idle time.  This final mandatory
     * no-fstrim check kicks in only of the other scheduling criteria is never met.
     */
    private static final long DEFAULT_MANDATORY_FSTRIM_INTERVAL = 3 * DateUtils.DAY_IN_MILLIS;
    ...
    @Override
    public void performFstrimIfNeeded() {
        // 确保只有 system 或者 root uid 才有权限执行该方法
        enforceSystemOrRoot("Only the system can request fstrim");

        // Before everything else, see whether we need to fstrim.
        try {
            // 获取 MountService
            IStorageManager sm = PackageHelper.getStorageManager();
            if (sm != null) {
                boolean doTrim = false;
                // 默认 3 天
                final long interval = android.provider.Settings.Global.getLong(
                        mContext.getContentResolver(),
                        android.provider.Settings.Global.FSTRIM_MANDATORY_INTERVAL,
                        DEFAULT_MANDATORY_FSTRIM_INTERVAL);
                if (interval > 0) {
                    final long timeSinceLast = System.currentTimeMillis() - sm.lastMaintenance();
                    // 距离上次 fstrim 时间超过 3 天,则执行 fstrim
                    if (timeSinceLast > interval) {
                        doTrim = true;
                        Slog.w(TAG, "No disk maintenance in " + timeSinceLast
                                + "; running immediately");
                    }
                }
                if (doTrim) {
                    final boolean dexOptDialogShown;
                    synchronized (mPackages) {
                        dexOptDialogShown = mDexOptDialogShown;
                    }
                    if (!isFirstBoot() && dexOptDialogShown) {
                        try {
                            // 对话框显示“正在优化存储空间”
                            ActivityManager.getService().showBootMessage(
                                    mContext.getResources().getString(
                                            R.string.android_upgrading_fstrim), true);
                        } catch (RemoteException e) {
                        }
                    }
                    // 执行 fstrim
                    sm.runMaintenance();
                }
            } else {
                Slog.e(TAG, "storageManager service unavailable!");
            }
        } catch (RemoteException e) {
            // Can't happen; StorageManagerService is local
        }
    }
```

## 5.3 PKMS.systemReady

> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

```java {.line-numbers}
    ...
    private static final int[] EMPTY_INT_ARRAY = new int[0];
    ...
    /**
     * Messages for {@link #mHandler} that need to wait for system ready before
     * being dispatched.
     */
    private ArrayList<Message> mPostSystemReadyMessages;
    ...
    volatile boolean mSystemReady;
    ...

    @Override
    public void systemReady() {
        enforceSystemOrRoot("Only the system can claim the system is ready");

        mSystemReady = true;
        ...

        int[] grantPermissionsUserIds = EMPTY_INT_ARRAY;

        synchronized (mPackages) {
            // Verify that all of the preferred activity components actually
            // exist.  It is possible for applications to be updated and at
            // that point remove a previously declared activity component that
            // had been set as a preferred activity.  We try to clean this up
            // the next time we encounter that preferred activity, but it is
            // possible for the user flow to never be able to return to that
            // situation so here we do a sanity check to make sure we haven't
            // left any junk around.
            ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>();
            for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
                PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i);
                removed.clear();
                for (PreferredActivity pa : pir.filterSet()) {
                    if (mActivities.mActivities.get(pa.mPref.mComponent) == null) {
                        removed.add(pa);
                    }
                }
                if (removed.size() > 0) {
                    for (int r=0; r<removed.size(); r++) {
                        PreferredActivity pa = removed.get(r);
                        Slog.w(TAG, "Removing dangling preferred activity: "
                                + pa.mPref.mComponent);
                        pir.removeFilter(pa);
                    }
                    mSettings.writePackageRestrictionsLPr(
                            mSettings.mPreferredActivities.keyAt(i));
                }
            }

            for (int userId : UserManagerService.getInstance().getUserIds()) {
                if (!mSettings.areDefaultRuntimePermissionsGrantedLPr(userId)) {
                    grantPermissionsUserIds = ArrayUtils.appendInt(
                            grantPermissionsUserIds, userId);
                }
            }
        }
        // 多用户管理服务
        sUserManager.systemReady();

        // If we upgraded grant all default permissions before kicking off.
        // 升级所有已获取的默认权限
        for (int userId : grantPermissionsUserIds) {
            mDefaultPermissionPolicy.grantDefaultPermissions(userId);
        }

        if (grantPermissionsUserIds == EMPTY_INT_ARRAY) {
            // If we did not grant default permissions, we preload from this the
            // default permission exceptions lazily to ensure we don't hit the
            // disk on a new user creation.
            mDefaultPermissionPolicy.scheduleReadDefaultPermissionExceptions();
        }

        // Now that we've scanned all packages, and granted any default
        // permissions, ensure permissions are updated. Beware of dragons if you
        // try optimizing this.
        synchronized (mPackages) {
            mPermissionManager.updateAllPermissions(
                    StorageManager.UUID_PRIVATE_INTERNAL, false, mPackages.values(),
                    mPermissionCallback);
        }

        // Kick off any messages waiting for system ready
        // 处理所有等待系统准备就绪的消息
        if (mPostSystemReadyMessages != null) {
            for (Message msg : mPostSystemReadyMessages) {
                msg.sendToTarget();
            }
            mPostSystemReadyMessages = null;
        }

        // Watch for external volumes that come and go over time
        // 观察存储服务
        final StorageManager storage = mContext.getSystemService(StorageManager.class);
        storage.registerListener(mStorageListener);

        mInstallerService.systemReady();
        mDexManager.systemReady();
        mPackageDexOptimizer.systemReady();

        StorageManagerInternal StorageManagerInternal = LocalServices.getService(
                StorageManagerInternal.class);
        StorageManagerInternal.addExternalStoragePolicy(
                new StorageManagerInternal.ExternalStorageMountPolicy() {
            @Override
            public int getMountMode(int uid, String packageName) {
                if (Process.isIsolated(uid)) {
                    return Zygote.MOUNT_EXTERNAL_NONE;
                }
                if (checkUidPermission(READ_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) {
                    return Zygote.MOUNT_EXTERNAL_DEFAULT;
                }
                if (checkUidPermission(WRITE_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) {
                    return Zygote.MOUNT_EXTERNAL_READ;
                }
                return Zygote.MOUNT_EXTERNAL_WRITE;
            }

            @Override
            public boolean hasExternalStorage(int uid, String packageName) {
                return true;
            }
        });

        // Now that we're mostly running, clean up stale users and apps
        sUserManager.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
        reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);

        mPermissionManager.systemReady();

        ...
    }
```

# 六 开机时间分析

使用命令:`adb shell cat /proc/bootprof` 查看开机时间:

```xml {.line-numbers}
$ adb shell cat /proc/bootprof                                                                                                                                                                            [16:12:19]
----------------------------------------
0        BOOT PROF (unit:msec)
----------------------------------------
      4354        : preloader
      2814        : lk (Start->Show logo: 1142)
----------------------------------------
       203.490846 : ON
       347.638385 :    1-swapper/0       : initcall: pwrap_hal_init    38.437462ms
       388.408308 :    1-swapper/0       : initcall: init_menu    20.046385ms
       446.307077 :    1-swapper/0       : initcall: customize_machine    53.406384ms

       ...

       2718.143083 :    1-swapper/0       : Kernel_init_done
       3966.768624 :  268-init            : INIT:early-init
       4408.037856 :  268-init            : INIT:late-init
       4423.971471 :  268-init            : INIT:Mount_START
       4863.027626 :  268-init            : INIT:Mount_END
       5150.339396 :  268-init            : INIT:post-fs
       5528.991474 :  268-init            : INIT:post-fs-data
       5680.929013 :  268-init            : post-fs-data: on modem start
       6122.993014 :  268-init            : INIT:boot
       7028.273170 :  268-init            : USB ready
       7043.689247 :  401-Binder:313_3    : vold:decrypt_master_key:START
       7100.255016 :  268-init            : probe: probe=platform_drv_probe drv=fm(bf14a018)    27.782308ms
       7100.401555 :  268-init            : initcall: init_module [fmradio_drv]    30.018693ms
       7411.948248 :  425-SurfaceFlinger: : BOOT_Animation:START
       7713.793095 :   55-kworker/3:1     : USB configured
       7726.765787 :  401-Binder:313_3    : vold:decrypt_master_key:END
       8462.948020 :  401-Binder:313_3    : vold:cryptfs_restart_internal:START
       8552.378635 :  268-init            : INIT:vold.decrypt=trigger_post_fs_data
       8787.473097 :  268-init            : INIT:post-fs-data
       8889.881405 :  268-init            : post-fs-data: on modem start
       9898.642023 :  401-Binder:313_3    : vold:cryptfs_restart_internal:END
       9927.095792 :  268-init            : INIT:vold.decrypt=trigger_restart_framework
      10047.714716 :  491-zygote          : boot_progress_start
      11504.507642 :  491-main            : Zygote:Preload Start
      13570.089801 :  491-main            : Zygote:Preload 6539 classes in 1916ms
      13908.996494 :  491-main            : Zygote:Preload 64 obtain resources in 79ms
      13999.568648 :  491-main            : Zygote:Preload 41 resources in 89ms
      14568.986573 :  491-main            : Zygote:Preload End
      14937.230420 :  918-system_server   : Android:SysServerInit_START
      15920.134730 :  918-system_server   : Android:PackageManagerService_Start
      16432.177039 :  918-system_server   : Android:PMS_scan_START
      16465.812654 :  918-system_server   : Android:PMS_scan_data:/vendor/overlay
      16497.700500 :  918-system_server   : Android:PMS_scan_data:/system/framework
      16593.062347 :  918-system_server   : Android:PMS_scan_data:/system/priv-app
      17297.726502 :  918-system_server   : Android:PMS_scan_data:/system/app
      17736.394119 :  918-system_server   : Android:PMS_scan_data:/vendor/app
      17762.593965 :  918-system_server   : Android:PMS_scan_data:/data/app
      18010.729119 :  918-system_server   : Android:PMS_scan_END
      18030.269042 :  918-system_server   : PMS:reconcileAppsDataLI
      18577.189197 :  918-system_server   : Android:PMS_READY
      20504.176202 :  918-system_server   : AMS:systemReady
      20513.526202 :  918-system_server   : AMS:AMS_READY
      20710.391203 :  937-ActivityManager : AP_Init:[service]:[com.android.inputmethod.latin]:[com.android.inputmethod.latin/.LatinIME]:pid:1045
      20745.502664 :  937-ActivityManager : AP_Init:[service]:[com.android.systemui]:[com.android.systemui/.SystemUIService]:pid:1063:(PersistAP)
      20800.240049 :  918-system_server   : SystemServer:NetworkStatsService systemReady
      20928.649280 :  937-ActivityManager : AP_Init:[]:[WebViewLoader-armeabi-v7a]:pid:1093
      20957.218434 :  918-system_server   : SystemServer:ConnectivityService systemReady
      20978.366665 :  918-system_server   : SystemServer:NetworkPolicyManagerServ systemReady
      21229.010665 :  918-system_server   : SystemServer:PhaseThirdPartyAppsCanStart
      21465.574974 :  918-system_server   : Android:SysServerInit_END
      21507.890204 :  937-ActivityManager : AP_Init:[added application]:[com.android.phone]:[com.android.phone]:pid:1179:(PersistAP)
      21587.235666 :  937-ActivityManager : AP_Init:[activity]:[com.android.settings]:[com.android.settings/.FallbackHome]:pid:1207
      21871.090821 :  937-ActivityManager : AP_Init:[service]:[android.ext.services]:[android.ext.services/.notification.Assistant]:pid:1258
      22058.393206 :  935-ActivityManager : AMS:ENABLE_SCREEN
      24001.307057 :  937-ActivityManager : AP_Init:[service]:[com.mediatek.ims]:[com.mediatek.ims/.MtkDynamicImsService]:pid:1440
      24355.295057 :  937-ActivityManager : AP_Init:[broadcast]:[com.mediatek.ppl]:[com.mediatek.ppl/.PplReceiver]:pid:1468
      26605.395217 :  380-Binder:354_1    : BOOT_Animation:END
      26656.498294 :  937-ActivityManager : AP_Init:[service]:[com.android.bluetooth]:[com.android.bluetooth/.btservice.AdapterService]:pid:1513
      26743.682909 :  937-ActivityManager : AP_Init:[broadcast]:[com.android.deskclock]:[com.android.deskclock/.AlarmInitReceiver]:pid:1522
      26774.370448 : OFF
----------------------------------------
```

可以从上面的信息看到 `PKMS` 在开机的时候做的动作和时间分布,一般 app 装的越多,开机时间越长。

# 七 参考资料

- [PackageManager启动篇](http://gityuan.com/2016/11/06/packagemanager/)

- [PackageManagerService启动流程](https://maoao530.github.io/2017/01/10/packagemanager/)

- [PackageManagerService 系列](https://superandroid.pro/2019/01/01/%E3%80%90%E6%A0%B8%E5%BF%83%E6%9C%8D%E5%8A%A1%E3%80%91Android%20%E6%A1%86%E6%9E%B6%E6%9C%8D%E5%8A%A1%E7%AF%87%20--%20%E6%B7%B1%E5%85%A5%E7%A0%94%E7%A9%B6%20PMS%20%E7%B3%BB%E5%88%97%EF%BC%881%EF%BC%89%E4%B9%8B%20%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值