Merge "Merge "AbsoluteVolume change custom call to official API" am: d51c3f3a2c am: 29da6bb3e4 am: 548b9eaf35 am: 229680564c am: af22a31d61"
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index 850cf1b..9696d3d 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -242,7 +242,8 @@
                     // #performDexOptUpgrade. When we do that we should have a
                     // more granular check here and only update the existing
                     // profiles.
-                    if (mPm.mIsUpgrade || mPm.mFirstBoot || (userId != UserHandle.USER_SYSTEM)) {
+                    if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()
+                            || (userId != UserHandle.USER_SYSTEM)) {
                         mArtManagerService.prepareAppProfiles(pkg, userId,
                                 /* updateReferenceProfileContent= */ false);
                     }
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 44e8e66..af9c401 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -251,13 +251,15 @@
                 resetStatesForNewDexOptRunLocked(Thread.currentThread());
             }
             PackageManagerService pm = mInjector.getPackageManagerService();
+            DexOptHelper dexOptHelper = new DexOptHelper(pm);
             ArraySet<String> packagesToOptimize;
             if (packageNames == null) {
-                packagesToOptimize = pm.getOptimizablePackages();
+                packagesToOptimize = dexOptHelper.getOptimizablePackages();
             } else {
                 packagesToOptimize = new ArraySet<>(packageNames);
             }
-            return runIdleOptimization(pm, packagesToOptimize, /* isPostBootUpdate= */ false);
+            return runIdleOptimization(pm, dexOptHelper, packagesToOptimize,
+                    /* isPostBootUpdate= */ false);
         } finally {
             Binder.restoreCallingIdentity(identity);
             markDexOptCompleted();
@@ -318,7 +320,8 @@
             return false;
         }
 
-        ArraySet<String> pkgs = pm.getOptimizablePackages();
+        DexOptHelper dexOptHelper = new DexOptHelper(pm);
+        ArraySet<String> pkgs = dexOptHelper.getOptimizablePackages();
         if (pkgs.isEmpty()) {
             Slog.i(TAG, "No packages to optimize");
             markPostBootUpdateCompleted(params);
@@ -344,7 +347,7 @@
                         tr.traceBegin("jobExecution");
                         boolean completed = false;
                         try {
-                            completed = runIdleOptimization(pm, pkgs,
+                            completed = runIdleOptimization(pm, dexOptHelper, pkgs,
                                     params.getJobId() == JOB_POST_BOOT_UPDATE);
                         } finally { // Those cleanup should be done always.
                             tr.traceEnd();
@@ -457,7 +460,8 @@
 
     @GuardedBy("mLock")
     private void controlDexOptBlockingLocked(boolean block) {
-        mInjector.getPackageManagerService().controlDexOptBlocking(block);
+        PackageManagerService pm = mInjector.getPackageManagerService();
+        new DexOptHelper(pm).controlDexOptBlocking(block);
     }
 
     private void scheduleAJob(int jobId) {
@@ -507,10 +511,11 @@
     }
 
     /** Returns true if completed */
-    private boolean runIdleOptimization(PackageManagerService pm, ArraySet<String> pkgs,
-            boolean isPostBootUpdate) {
+    private boolean runIdleOptimization(PackageManagerService pm, DexOptHelper dexOptHelper,
+            ArraySet<String> pkgs, boolean isPostBootUpdate) {
         long lowStorageThreshold = getLowStorageThreshold();
-        int status = idleOptimizePackages(pm, pkgs, lowStorageThreshold, isPostBootUpdate);
+        int status = idleOptimizePackages(pm, dexOptHelper, pkgs, lowStorageThreshold,
+                isPostBootUpdate);
         logStatus(status);
         synchronized (mLock) {
             mLastExecutionStatus = status;
@@ -557,8 +562,8 @@
     }
 
     @Status
-    private int idleOptimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
-            long lowStorageThreshold, boolean isPostBootUpdate) {
+    private int idleOptimizePackages(PackageManagerService pm, DexOptHelper dexOptHelper,
+            ArraySet<String> pkgs, long lowStorageThreshold, boolean isPostBootUpdate) {
         ArraySet<String> updatedPackages = new ArraySet<>();
         ArraySet<String> updatedPackagesDueToSecondaryDex = new ArraySet<>();
 
@@ -595,7 +600,7 @@
                             // Should be aborted by the scheduler.
                             return abortCode;
                         }
-                        @DexOptResult int downgradeResult = downgradePackage(pm, pkg,
+                        @DexOptResult int downgradeResult = downgradePackage(pm, dexOptHelper, pkg,
                                 /* isForPrimaryDex= */ true, isPostBootUpdate);
                         if (downgradeResult == PackageDexOptimizer.DEX_OPT_PERFORMED) {
                             updatedPackages.add(pkg);
@@ -606,7 +611,7 @@
                             return status;
                         }
                         if (supportSecondaryDex) {
-                            downgradeResult = downgradePackage(pm, pkg,
+                            downgradeResult = downgradePackage(pm, dexOptHelper, pkg,
                                     /* isForPrimaryDex= */false, isPostBootUpdate);
                             status = convertPackageDexOptimizerStatusToInternal(downgradeResult);
                             if (status != STATUS_OK) {
@@ -620,8 +625,9 @@
                 }
             }
 
-            @Status int primaryResult = optimizePackages(pm, pkgs, lowStorageThreshold,
-                    /*isForPrimaryDex=*/ true, updatedPackages, isPostBootUpdate);
+            @Status int primaryResult = optimizePackages(dexOptHelper, pkgs,
+                    lowStorageThreshold, /*isForPrimaryDex=*/ true, updatedPackages,
+                    isPostBootUpdate);
             if (primaryResult != STATUS_OK) {
                 return primaryResult;
             }
@@ -630,9 +636,9 @@
                 return STATUS_OK;
             }
 
-            @Status int secondaryResult = optimizePackages(pm, pkgs, lowStorageThreshold,
-                    /*isForPrimaryDex*/ false, updatedPackagesDueToSecondaryDex,
-                    isPostBootUpdate);
+            @Status int secondaryResult = optimizePackages(dexOptHelper, pkgs,
+                    lowStorageThreshold, /*isForPrimaryDex*/ false,
+                    updatedPackagesDueToSecondaryDex, isPostBootUpdate);
             return secondaryResult;
         } finally {
             // Always let the pinner service know about changes.
@@ -645,9 +651,9 @@
     }
 
     @Status
-    private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
-            long lowStorageThreshold, boolean isForPrimaryDex, ArraySet<String> updatedPackages,
-            boolean isPostBootUpdate) {
+    private int optimizePackages(DexOptHelper dexOptHelper,
+            ArraySet<String> pkgs, long lowStorageThreshold, boolean isForPrimaryDex,
+            ArraySet<String> updatedPackages, boolean isPostBootUpdate) {
         for (String pkg : pkgs) {
             int abortCode = abortIdleOptimizations(lowStorageThreshold);
             if (abortCode != STATUS_OK) {
@@ -655,7 +661,8 @@
                 return abortCode;
             }
 
-            @DexOptResult int result = optimizePackage(pm, pkg, isForPrimaryDex, isPostBootUpdate);
+            @DexOptResult int result = optimizePackage(dexOptHelper, pkg, isForPrimaryDex,
+                    isPostBootUpdate);
             if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
                 updatedPackages.add(pkg);
             } else if (result != PackageDexOptimizer.DEX_OPT_SKIPPED) {
@@ -674,7 +681,7 @@
      * @return PackageDexOptimizer.DEX_*
      */
     @DexOptResult
-    private int downgradePackage(PackageManagerService pm, String pkg,
+    private int downgradePackage(PackageManagerService pm, DexOptHelper dexOptHelper, String pkg,
             boolean isForPrimaryDex, boolean isPostBootUpdate) {
         if (DEBUG) {
             Slog.d(TAG, "Downgrading " + pkg);
@@ -697,10 +704,10 @@
                 // remove their compiler artifacts from dalvik cache.
                 pm.deleteOatArtifactsOfPackage(pkg);
             } else {
-                result = performDexOptPrimary(pm, pkg, reason, dexoptFlags);
+                result = performDexOptPrimary(dexOptHelper, pkg, reason, dexoptFlags);
             }
         } else {
-            result = performDexOptSecondary(pm, pkg, reason, dexoptFlags);
+            result = performDexOptSecondary(dexOptHelper, pkg, reason, dexoptFlags);
         }
 
         if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -726,14 +733,14 @@
      *
      * Optimize package if needed. Note that there can be no race between
      * concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
-     * @param pm An instance of PackageManagerService
+     * @param dexOptHelper An instance of DexOptHelper
      * @param pkg The package to be downgraded.
      * @param isForPrimaryDex Apps can have several dex file, primary and secondary.
      * @param isPostBootUpdate is post boot update or not.
      * @return PackageDexOptimizer#DEX_OPT_*
      */
     @DexOptResult
-    private int optimizePackage(PackageManagerService pm, String pkg,
+    private int optimizePackage(DexOptHelper dexOptHelper, String pkg,
             boolean isForPrimaryDex, boolean isPostBootUpdate) {
         int reason = isPostBootUpdate ? PackageManagerService.REASON_POST_BOOT
                 : PackageManagerService.REASON_BACKGROUND_DEXOPT;
@@ -746,26 +753,27 @@
         // System server share the same code path as primary dex files.
         // PackageManagerService will select the right optimization path for it.
         if (isForPrimaryDex || PLATFORM_PACKAGE_NAME.equals(pkg)) {
-            return performDexOptPrimary(pm, pkg, reason, dexoptFlags);
+            return performDexOptPrimary(dexOptHelper, pkg, reason, dexoptFlags);
         } else {
-            return performDexOptSecondary(pm, pkg, reason, dexoptFlags);
+            return performDexOptSecondary(dexOptHelper, pkg, reason, dexoptFlags);
         }
     }
 
     @DexOptResult
-    private int performDexOptPrimary(PackageManagerService pm, String pkg, int reason,
+    private int performDexOptPrimary(DexOptHelper dexOptHelper, String pkg, int reason,
             int dexoptFlags) {
         return trackPerformDexOpt(pkg, /*isForPrimaryDex=*/ true,
-                () -> pm.performDexOptWithStatus(new DexoptOptions(pkg, reason, dexoptFlags)));
+                () -> dexOptHelper.performDexOptWithStatus(
+                        new DexoptOptions(pkg, reason, dexoptFlags)));
     }
 
     @DexOptResult
-    private int performDexOptSecondary(PackageManagerService pm, String pkg, int reason,
+    private int performDexOptSecondary(DexOptHelper dexOptHelper, String pkg, int reason,
             int dexoptFlags) {
         DexoptOptions dexoptOptions = new DexoptOptions(pkg, reason,
                 dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX);
         return trackPerformDexOpt(pkg, /*isForPrimaryDex=*/ false,
-                () -> pm.performDexOpt(dexoptOptions)
+                () -> dexOptHelper.performDexOpt(dexoptOptions)
                     ? PackageDexOptimizer.DEX_OPT_PERFORMED : PackageDexOptimizer.DEX_OPT_FAILED
         );
     }
diff --git a/services/core/java/com/android/server/pm/ComputerLocked.java b/services/core/java/com/android/server/pm/ComputerLocked.java
index f63cc4e..b83f987 100644
--- a/services/core/java/com/android/server/pm/ComputerLocked.java
+++ b/services/core/java/com/android/server/pm/ComputerLocked.java
@@ -61,7 +61,7 @@
         return mService.mInstantAppInstallerActivity;
     }
     protected ApplicationInfo androidApplication() {
-        return mService.mAndroidApplication;
+        return mService.getCoreAndroidApplication();
     }
 
     public @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 40668d7..0feb9c5 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -83,6 +83,7 @@
     private final UserManagerInternal mUserManagerInternal;
     private final PermissionManagerServiceInternal mPermissionManager;
     private final RemovePackageHelper mRemovePackageHelper;
+    // TODO(b/201815903): remove dependency to InitAndSystemPackageHelper
     private final InitAndSystemPackageHelper mInitAndSystemPackageHelper;
     private final AppDataHelper mAppDataHelper;
 
@@ -104,8 +105,7 @@
         mUserManagerInternal = mPm.mInjector.getUserManagerInternal();
         mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal();
         mRemovePackageHelper = new RemovePackageHelper(mPm, mAppDataHelper);
-        mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(mPm, mRemovePackageHelper,
-                mAppDataHelper);
+        mInitAndSystemPackageHelper = mPm.getInitAndSystemPackageHelper();
     }
 
     /**
@@ -282,8 +282,7 @@
                             Slog.i(TAG, "Enabling system stub after removal; pkg: "
                                     + stubPkg.getPackageName());
                         }
-                        mInitAndSystemPackageHelper.enableCompressedPackage(stubPkg, stubPs,
-                                mPm.mDefParseFlags, mPm.getDirsToScanAsSystem());
+                        mInitAndSystemPackageHelper.enableCompressedPackage(stubPkg, stubPs);
                     } else if (DEBUG_COMPRESSION) {
                         Slog.i(TAG, "System stub disabled for all users, leaving uncompressed "
                                 + "after removal; pkg: " + stubPkg.getPackageName());
@@ -424,8 +423,7 @@
             PackageSetting disabledPs = deleteInstalledSystemPackage(action, ps, allUserHandles,
                     flags, outInfo, writeSettings);
             mInitAndSystemPackageHelper.restoreDisabledSystemPackageLIF(
-                    action, ps, allUserHandles, outInfo, writeSettings, mPm.mDefParseFlags,
-                    mPm.getDirsToScanAsSystem(), disabledPs);
+                    action, ps, allUserHandles, outInfo, writeSettings, disabledPs);
         } else {
             if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.getPackageName());
             deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
new file mode 100644
index 0000000..9390284
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -0,0 +1,667 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.pm.PackageManagerService.REASON_BOOT_AFTER_OTA;
+import static com.android.server.pm.PackageManagerService.REASON_CMDLINE;
+import static com.android.server.pm.PackageManagerService.REASON_FIRST_BOOT;
+import static com.android.server.pm.PackageManagerService.STUB_SUFFIX;
+import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter;
+import static com.android.server.pm.PackageManagerServiceUtils.REMOVE_IF_NULL_PKG;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.dex.ArtManager;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.logging.MetricsLogger;
+import com.android.server.apphibernation.AppHibernationManagerInternal;
+import com.android.server.apphibernation.AppHibernationService;
+import com.android.server.pm.dex.DexManager;
+import com.android.server.pm.dex.DexoptOptions;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
+
+final class DexOptHelper {
+    private static final long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
+
+    private final PackageManagerService mPm;
+
+    public boolean isDexOptDialogShown() {
+        return mDexOptDialogShown;
+    }
+
+    @GuardedBy("mPm.mLock")
+    private boolean mDexOptDialogShown;
+
+    DexOptHelper(PackageManagerService pm) {
+        mPm = pm;
+    }
+
+    /*
+     * Return the prebuilt profile path given a package base code path.
+     */
+    private static String getPrebuildProfilePath(AndroidPackage pkg) {
+        return pkg.getBaseApkPath() + ".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}.
+     */
+    public int[] performDexOptUpgrade(List<AndroidPackage> 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 (AndroidPackage pkg : pkgs) {
+            numberOfPackagesVisited++;
+
+            boolean useProfileForDexopt = false;
+
+            if ((mPm.isFirstBoot() || mPm.isDeviceUpgrading()) && pkg.isSystem()) {
+                // 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 (profileFile.exists()) {
+                    try {
+                        // We could also do this lazily before calling dexopt in
+                        // PackageDexOptimizer to prevent this happening on first boot. The issue
+                        // is that we don't have a good way to say "do this only once".
+                        if (!mPm.mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
+                                pkg.getUid(), pkg.getPackageName(),
+                                ArtManager.getProfileName(null))) {
+                            Log.e(TAG, "Installer failed to copy system profile!");
+                        } else {
+                            // Disabled as this causes speed-profile compilation during first boot
+                            // even if things are already compiled.
+                            // useProfileForDexopt = true;
+                        }
+                    } catch (Exception e) {
+                        Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ",
+                                e);
+                    }
+                } else {
+                    PackageSetting disabledPs = mPm.mSettings.getDisabledSystemPkgLPr(
+                            pkg.getPackageName());
+                    // Handle compressed APKs in this path. Only do this for stubs with profiles to
+                    // minimize the number off apps being speed-profile compiled during first boot.
+                    // The other paths will not change the filter.
+                    if (disabledPs != null && disabledPs.getPkg().isStub()) {
+                        // The package is the stub one, remove the stub suffix to get the normal
+                        // package and APK names.
+                        String systemProfilePath = getPrebuildProfilePath(disabledPs.getPkg())
+                                .replace(STUB_SUFFIX, "");
+                        profileFile = new File(systemProfilePath);
+                        // If we have a profile for a compressed APK, copy it to the reference
+                        // location.
+                        // Note that copying the profile here will cause it to override the
+                        // reference profile every OTA even though the existing reference profile
+                        // may have more data. We can't copy during decompression since the
+                        // directories are not set up at that point.
+                        if (profileFile.exists()) {
+                            try {
+                                // We could also do this lazily before calling dexopt in
+                                // PackageDexOptimizer to prevent this happening on first boot. The
+                                // issue is that we don't have a good way to say "do this only
+                                // once".
+                                if (!mPm.mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
+                                        pkg.getUid(), pkg.getPackageName(),
+                                        ArtManager.getProfileName(null))) {
+                                    Log.e(TAG, "Failed to copy system profile for stub package!");
+                                } else {
+                                    useProfileForDexopt = true;
+                                }
+                            } catch (Exception e) {
+                                Log.e(TAG, "Failed to copy profile "
+                                        + profileFile.getAbsolutePath() + " ", e);
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
+                if (DEBUG_DEXOPT) {
+                    Log.i(TAG, "Skipping update of non-optimizable app " + pkg.getPackageName());
+                }
+                numberOfPackagesSkipped++;
+                continue;
+            }
+
+            if (DEBUG_DEXOPT) {
+                Log.i(TAG, "Updating app " + numberOfPackagesVisited + " of "
+                        + numberOfPackagesToDexopt + ": " + pkg.getPackageName());
+            }
+
+            if (showDialog) {
+                try {
+                    ActivityManager.getService().showBootMessage(
+                            mPm.mContext.getResources().getString(R.string.android_upgrading_apk,
+                                    numberOfPackagesVisited, numberOfPackagesToDexopt), true);
+                } catch (RemoteException e) {
+                }
+                synchronized (mPm.mLock) {
+                    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;
+            }
+
+            if (SystemProperties.getBoolean(mPm.PRECOMPILE_LAYOUTS, false)) {
+                mPm.mArtManagerService.compileLayouts(pkg);
+            }
+
+            // 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;
+            }
+            int primaryDexOptStatus = performDexOptTraced(new DexoptOptions(
+                    pkg.getPackageName(),
+                    pkgCompilationReason,
+                    dexoptFlags));
+
+            switch (primaryDexOptStatus) {
+                case PackageDexOptimizer.DEX_OPT_PERFORMED:
+                    numberOfPackagesOptimized++;
+                    break;
+                case PackageDexOptimizer.DEX_OPT_SKIPPED:
+                    numberOfPackagesSkipped++;
+                    break;
+                case PackageDexOptimizer.DEX_OPT_CANCELLED:
+                    // ignore this case
+                    break;
+                case PackageDexOptimizer.DEX_OPT_FAILED:
+                    numberOfPackagesFailed++;
+                    break;
+                default:
+                    Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStatus);
+                    break;
+            }
+        }
+
+        return new int[]{numberOfPackagesOptimized, numberOfPackagesSkipped,
+                numberOfPackagesFailed};
+    }
+
+    public void performPackageDexOptUpgradeIfNeeded() {
+        PackageManagerServiceUtils.enforceSystemOrRoot(
+                "Only the system can request package update");
+
+        // We need to re-extract after an OTA.
+        boolean causeUpgrade = mPm.isDeviceUpgrading();
+
+        // 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 = mPm.isFirstBoot() || mPm.isPreNUpgrade();
+
+        if (!causeUpgrade && !causeFirstBoot) {
+            return;
+        }
+
+        List<PackageSetting> pkgSettings;
+        synchronized (mPm.mLock) {
+            pkgSettings = getPackagesForDexopt(mPm.mSettings.getPackagesLocked().values(), mPm);
+        }
+
+        List<AndroidPackage> pkgs = new ArrayList<>(pkgSettings.size());
+        for (int index = 0; index < pkgSettings.size(); index++) {
+            pkgs.add(pkgSettings.get(index).getPkg());
+        }
+
+        final long startTime = System.nanoTime();
+        final int[] stats = performDexOptUpgrade(pkgs, mPm.isPreNUpgrade() /* showDialog */,
+                causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT_AFTER_OTA,
+                false /* bootComplete */);
+
+        final int elapsedTimeSeconds =
+                (int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime);
+
+        MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_dexopted", stats[0]);
+        MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_skipped", stats[1]);
+        MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_failed", stats[2]);
+        MetricsLogger.histogram(
+                mPm.mContext, "opt_dialog_num_total", getOptimizablePackages().size());
+        MetricsLogger.histogram(mPm.mContext, "opt_dialog_time_s", elapsedTimeSeconds);
+    }
+
+    public ArraySet<String> getOptimizablePackages() {
+        ArraySet<String> pkgs = new ArraySet<>();
+        synchronized (mPm.mLock) {
+            for (AndroidPackage p : mPm.mPackages.values()) {
+                if (PackageDexOptimizer.canOptimizePackage(p)) {
+                    pkgs.add(p.getPackageName());
+                }
+            }
+        }
+        if (AppHibernationService.isAppHibernationEnabled()) {
+            AppHibernationManagerInternal appHibernationManager =
+                    mPm.mInjector.getLocalService(AppHibernationManagerInternal.class);
+            pkgs.removeIf(pkgName -> appHibernationManager.isHibernatingGlobally(pkgName));
+        }
+        return pkgs;
+    }
+
+    /*package*/ boolean performDexOpt(DexoptOptions options) {
+        if (mPm.getInstantAppPackageName(Binder.getCallingUid()) != null) {
+            return false;
+        } else if (mPm.isInstantApp(options.getPackageName(), UserHandle.getCallingUserId())) {
+            return false;
+        }
+
+        if (options.isDexoptOnlySecondaryDex()) {
+            return mPm.getDexManager().dexoptSecondaryDex(options);
+        } else {
+            int dexoptStatus = performDexOptWithStatus(options);
+            return dexoptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
+        }
+    }
+
+    /**
+     * Perform dexopt on the given package and return one of following result:
+     * {@link PackageDexOptimizer#DEX_OPT_SKIPPED}
+     * {@link PackageDexOptimizer#DEX_OPT_PERFORMED}
+     * {@link PackageDexOptimizer#DEX_OPT_CANCELLED}
+     * {@link PackageDexOptimizer#DEX_OPT_FAILED}
+     */
+    @PackageDexOptimizer.DexOptResult
+    /* package */ int performDexOptWithStatus(DexoptOptions options) {
+        return performDexOptTraced(options);
+    }
+
+    private int performDexOptTraced(DexoptOptions options) {
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+        try {
+            return performDexOptInternal(options);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        }
+    }
+
+    // Run dexopt on a given package. Returns true if dexopt did not fail, i.e.
+    // if the package can now be considered up to date for the given filter.
+    private int performDexOptInternal(DexoptOptions options) {
+        AndroidPackage p;
+        PackageSetting pkgSetting;
+        synchronized (mPm.mLock) {
+            p = mPm.mPackages.get(options.getPackageName());
+            pkgSetting = mPm.mSettings.getPackageLPr(options.getPackageName());
+            if (p == null || pkgSetting == null) {
+                // Package could not be found. Report failure.
+                return PackageDexOptimizer.DEX_OPT_FAILED;
+            }
+            mPm.getPackageUsage().maybeWriteAsync(mPm.mSettings.getPackagesLocked());
+            mPm.mCompilerStats.maybeWriteAsync();
+        }
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mPm.mInstallLock) {
+                return performDexOptInternalWithDependenciesLI(p, pkgSetting, options);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
+    private int performDexOptInternalWithDependenciesLI(AndroidPackage p,
+            @NonNull PackageSetting pkgSetting, DexoptOptions options) {
+        // System server gets a special path.
+        if (PLATFORM_PACKAGE_NAME.equals(p.getPackageName())) {
+            return mPm.getDexManager().dexoptSystemServer(options);
+        }
+
+        // Select the dex optimizer based on the force parameter.
+        // Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
+        //       allocate an object here.
+        PackageDexOptimizer pdo = options.isForce()
+                ? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPm.mPackageDexOptimizer)
+                : mPm.mPackageDexOptimizer;
+
+        // Dexopt all dependencies first. Note: we ignore the return value and march on
+        // on errors.
+        // Note that we are going to call performDexOpt on those libraries as many times as
+        // they are referenced in packages. When we do a batch of performDexOpt (for example
+        // at boot, or background job), the passed 'targetCompilerFilter' stays the same,
+        // and the first package that uses the library will dexopt it. The
+        // others will see that the compiled code for the library is up to date.
+        Collection<SharedLibraryInfo> deps = SharedLibraryHelper.findSharedLibraries(pkgSetting);
+        final String[] instructionSets = getAppDexInstructionSets(
+                AndroidPackageUtils.getPrimaryCpuAbi(p, pkgSetting),
+                AndroidPackageUtils.getSecondaryCpuAbi(p, pkgSetting));
+        if (!deps.isEmpty()) {
+            DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
+                    options.getCompilationReason(), options.getCompilerFilter(),
+                    options.getSplitName(),
+                    options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
+            for (SharedLibraryInfo info : deps) {
+                AndroidPackage depPackage = null;
+                PackageSetting depPackageSetting = null;
+                synchronized (mPm.mLock) {
+                    depPackage = mPm.mPackages.get(info.getPackageName());
+                    depPackageSetting = mPm.mSettings.getPackageLPr(info.getPackageName());
+                }
+                if (depPackage != null && depPackageSetting != null) {
+                    // TODO: Analyze and investigate if we (should) profile libraries.
+                    pdo.performDexOpt(depPackage, depPackageSetting, instructionSets,
+                            mPm.getOrCreateCompilerPackageStats(depPackage),
+                            mPm.getDexManager().getPackageUseInfoOrDefault(
+                                    depPackage.getPackageName()), libraryOptions);
+                } else {
+                    // TODO(ngeoffray): Support dexopting system shared libraries.
+                }
+            }
+        }
+
+        return pdo.performDexOpt(p, pkgSetting, instructionSets,
+                mPm.getOrCreateCompilerPackageStats(p),
+                mPm.getDexManager().getPackageUseInfoOrDefault(p.getPackageName()), options);
+    }
+
+    public void forceDexOpt(String packageName) {
+        PackageManagerServiceUtils.enforceSystemOrRoot("forceDexOpt");
+
+        AndroidPackage pkg;
+        PackageSetting pkgSetting;
+        synchronized (mPm.mLock) {
+            pkg = mPm.mPackages.get(packageName);
+            pkgSetting = mPm.mSettings.getPackageLPr(packageName);
+            if (pkg == null || pkgSetting == null) {
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+        }
+
+        synchronized (mPm.mInstallLock) {
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+
+            // Whoever is calling forceDexOpt wants a compiled package.
+            // Don't use profiles since that may cause compilation to be skipped.
+            final int res = performDexOptInternalWithDependenciesLI(pkg, pkgSetting,
+                    new DexoptOptions(packageName,
+                            getDefaultCompilerFilter(),
+                            DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE));
+
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+            if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
+                throw new IllegalStateException("Failed to dexopt: " + res);
+            }
+        }
+    }
+
+    public boolean performDexOptMode(String packageName,
+            boolean checkProfiles, String targetCompilerFilter, boolean force,
+            boolean bootComplete, String splitName) {
+        PackageManagerServiceUtils.enforceSystemOrRootOrShell("performDexOptMode");
+
+        int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0)
+                | (force ? DexoptOptions.DEXOPT_FORCE : 0)
+                | (bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0);
+        return performDexOpt(new DexoptOptions(packageName, REASON_CMDLINE,
+                targetCompilerFilter, splitName, flags));
+    }
+
+    public boolean performDexOptSecondary(String packageName, String compilerFilter,
+            boolean force) {
+        int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX
+                | DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES
+                | DexoptOptions.DEXOPT_BOOT_COMPLETE
+                | (force ? DexoptOptions.DEXOPT_FORCE : 0);
+        return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags));
+    }
+
+    // Sort apps by importance for dexopt ordering. Important apps are given
+    // more priority in case the device runs out of space.
+    public static List<PackageSetting> getPackagesForDexopt(
+            Collection<PackageSetting> packages,
+            PackageManagerService packageManagerService) {
+        return getPackagesForDexopt(packages, packageManagerService, DEBUG_DEXOPT);
+    }
+
+    public static List<PackageSetting> getPackagesForDexopt(
+            Collection<PackageSetting> pkgSettings,
+            PackageManagerService packageManagerService,
+            boolean debug) {
+        List<PackageSetting> result = new LinkedList<>();
+        ArrayList<PackageSetting> remainingPkgSettings = new ArrayList<>(pkgSettings);
+
+        // First, remove all settings without available packages
+        remainingPkgSettings.removeIf(REMOVE_IF_NULL_PKG);
+
+        ArrayList<PackageSetting> sortTemp = new ArrayList<>(remainingPkgSettings.size());
+
+        // Give priority to core apps.
+        applyPackageFilter(pkgSetting -> pkgSetting.getPkg().isCoreApp(), result,
+                remainingPkgSettings, sortTemp, packageManagerService);
+
+        // Give priority to system apps that listen for pre boot complete.
+        Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+        final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
+        applyPackageFilter(pkgSetting -> pkgNames.contains(pkgSetting.getPackageName()), result,
+                remainingPkgSettings, sortTemp, packageManagerService);
+
+        // Give priority to apps used by other apps.
+        DexManager dexManager = packageManagerService.getDexManager();
+        applyPackageFilter(pkgSetting ->
+                        dexManager.getPackageUseInfoOrDefault(pkgSetting.getPackageName())
+                                .isAnyCodePathUsedByOtherApps(),
+                result, remainingPkgSettings, sortTemp, packageManagerService);
+
+        // Filter out packages that aren't recently used, add all remaining apps.
+        // TODO: add a property to control this?
+        Predicate<PackageSetting> remainingPredicate;
+        if (!remainingPkgSettings.isEmpty()
+                && packageManagerService.isHistoricalPackageUsageAvailable()) {
+            if (debug) {
+                Log.i(TAG, "Looking at historical package use");
+            }
+            // Get the package that was used last.
+            PackageSetting lastUsed = Collections.max(remainingPkgSettings,
+                    (pkgSetting1, pkgSetting2) -> Long.compare(
+                            pkgSetting1.getPkgState().getLatestForegroundPackageUseTimeInMills(),
+                            pkgSetting2.getPkgState().getLatestForegroundPackageUseTimeInMills()));
+            if (debug) {
+                Log.i(TAG, "Taking package " + lastUsed.getPackageName()
+                        + " as reference in time use");
+            }
+            long estimatedPreviousSystemUseTime = lastUsed.getPkgState()
+                    .getLatestForegroundPackageUseTimeInMills();
+            // Be defensive if for some reason package usage has bogus data.
+            if (estimatedPreviousSystemUseTime != 0) {
+                final long cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS;
+                remainingPredicate = pkgSetting -> pkgSetting.getPkgState()
+                        .getLatestForegroundPackageUseTimeInMills() >= cutoffTime;
+            } else {
+                // No meaningful historical info. Take all.
+                remainingPredicate = pkgSetting -> true;
+            }
+            sortPackagesByUsageDate(remainingPkgSettings, packageManagerService);
+        } else {
+            // No historical info. Take all.
+            remainingPredicate = pkgSetting -> true;
+        }
+        applyPackageFilter(remainingPredicate, result, remainingPkgSettings, sortTemp,
+                packageManagerService);
+
+        if (debug) {
+            Log.i(TAG, "Packages to be dexopted: " + packagesToString(result));
+            Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgSettings));
+        }
+
+        return result;
+    }
+
+    // Apply the given {@code filter} to all packages in {@code packages}. If tested positive, the
+    // package will be removed from {@code packages} and added to {@code result} with its
+    // dependencies. If usage data is available, the positive packages will be sorted by usage
+    // data (with {@code sortTemp} as temporary storage).
+    private static void applyPackageFilter(
+            Predicate<PackageSetting> filter,
+            Collection<PackageSetting> result,
+            Collection<PackageSetting> packages,
+            @NonNull List<PackageSetting> sortTemp,
+            PackageManagerService packageManagerService) {
+        for (PackageSetting pkgSetting : packages) {
+            if (filter.test(pkgSetting)) {
+                sortTemp.add(pkgSetting);
+            }
+        }
+
+        sortPackagesByUsageDate(sortTemp, packageManagerService);
+        packages.removeAll(sortTemp);
+
+        for (PackageSetting pkgSetting : sortTemp) {
+            result.add(pkgSetting);
+
+            List<PackageSetting> deps =
+                    packageManagerService.findSharedNonSystemLibraries(pkgSetting);
+            if (!deps.isEmpty()) {
+                deps.removeAll(result);
+                result.addAll(deps);
+                packages.removeAll(deps);
+            }
+        }
+
+        sortTemp.clear();
+    }
+
+    // Sort a list of apps by their last usage, most recently used apps first. The order of
+    // packages without usage data is undefined (but they will be sorted after the packages
+    // that do have usage data).
+    private static void sortPackagesByUsageDate(List<PackageSetting> pkgSettings,
+            PackageManagerService packageManagerService) {
+        if (!packageManagerService.isHistoricalPackageUsageAvailable()) {
+            return;
+        }
+
+        Collections.sort(pkgSettings, (pkgSetting1, pkgSetting2) ->
+                Long.compare(
+                        pkgSetting2.getPkgState().getLatestForegroundPackageUseTimeInMills(),
+                        pkgSetting1.getPkgState().getLatestForegroundPackageUseTimeInMills())
+        );
+    }
+
+    private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
+        List<ResolveInfo> ris = null;
+        try {
+            ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId)
+                    .getList();
+        } catch (RemoteException e) {
+        }
+        ArraySet<String> pkgNames = new ArraySet<String>();
+        if (ris != null) {
+            for (ResolveInfo ri : ris) {
+                pkgNames.add(ri.activityInfo.packageName);
+            }
+        }
+        return pkgNames;
+    }
+
+    public static String packagesToString(List<PackageSetting> pkgSettings) {
+        StringBuilder sb = new StringBuilder();
+        for (int index = 0; index < pkgSettings.size(); index++) {
+            if (sb.length() > 0) {
+                sb.append(", ");
+            }
+            sb.append(pkgSettings.get(index).getPackageName());
+        }
+        return sb.toString();
+    }
+
+     /**
+     * Requests that files preopted on a secondary system partition be copied to the data partition
+     * if possible.  Note that the actual copying of the files is accomplished by init for security
+     * reasons. This simply requests that the copy takes place and awaits confirmation of its
+     * completion. See platform/system/extras/cppreopt/ for the implementation of the actual copy.
+     */
+    public static void requestCopyPreoptedFiles() {
+        final int WAIT_TIME_MS = 100;
+        final String CP_PREOPT_PROPERTY = "sys.cppreopt";
+        if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) {
+            SystemProperties.set(CP_PREOPT_PROPERTY, "requested");
+            // We will wait for up to 100 seconds.
+            final long timeStart = SystemClock.uptimeMillis();
+            final long timeEnd = timeStart + 100 * 1000;
+            long timeNow = timeStart;
+            while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) {
+                try {
+                    Thread.sleep(WAIT_TIME_MS);
+                } catch (InterruptedException e) {
+                    // Do nothing
+                }
+                timeNow = SystemClock.uptimeMillis();
+                if (timeNow > timeEnd) {
+                    SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out");
+                    Slog.wtf(TAG, "cppreopt did not finish!");
+                    break;
+                }
+            }
+
+            Slog.i(TAG, "cppreopts took " + (timeNow - timeStart) + " ms");
+        }
+    }
+
+    /*package*/ void controlDexOptBlocking(boolean block) {
+        mPm.mPackageDexOptimizer.controlDexOptBlocking(block);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
index 9ba69f8..722198f 100644
--- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
@@ -35,8 +35,12 @@
 import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX;
 import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
 import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
+import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
+import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE;
+import static com.android.server.pm.PackageManagerService.SCAN_INITIAL;
 import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
 import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
+import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS;
 import static com.android.server.pm.PackageManagerService.TAG;
 import static com.android.server.pm.PackageManagerServiceUtils.decompressFile;
 import static com.android.server.pm.PackageManagerServiceUtils.getCompressedFiles;
@@ -55,6 +59,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.system.ErrnoException;
+import android.util.ArrayMap;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
@@ -62,6 +67,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.F2fsUtils;
 import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.content.om.OverlayConfig;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.EventLogTags;
@@ -90,60 +96,129 @@
     private final RemovePackageHelper mRemovePackageHelper;
     private final AppDataHelper mAppDataHelper;
 
+    private final List<ScanPartition> mDirsToScanAsSystem;
+    private final int mScanFlags;
+    private final int mSystemParseFlags;
+    private final int mSystemScanFlags;
+
+    /**
+     * Tracks new system packages [received in an OTA] that we expect to
+     * find updated user-installed versions. Keys are package name, values
+     * are package location.
+     */
+    private final ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
+
     // TODO(b/198166813): remove PMS dependency
     InitAndSystemPackageHelper(PackageManagerService pm, RemovePackageHelper removePackageHelper,
             AppDataHelper appDataHelper) {
         mPm = pm;
         mRemovePackageHelper = removePackageHelper;
         mAppDataHelper = appDataHelper;
+        mDirsToScanAsSystem = getSystemScanPartitions();
+        // Set flag to monitor and not change apk file paths when scanning install directories.
+        int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
+        if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()) {
+            mScanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
+        } else {
+            mScanFlags = scanFlags;
+        }
+        mSystemParseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
+        mSystemScanFlags = scanFlags | SCAN_AS_SYSTEM;
     }
 
+    private List<ScanPartition> getSystemScanPartitions() {
+        final List<ScanPartition> scanPartitions = new ArrayList<>();
+        scanPartitions.addAll(mPm.mInjector.getSystemPartitions());
+        scanPartitions.addAll(getApexScanPartitions());
+        Slog.d(TAG, "Directories scanned as system partitions: " + scanPartitions);
+        return scanPartitions;
+    }
+
+    private List<ScanPartition> getApexScanPartitions() {
+        final List<ScanPartition> scanPartitions = new ArrayList<>();
+        final List<ApexManager.ActiveApexInfo> activeApexInfos =
+                mPm.mApexManager.getActiveApexInfos();
+        for (int i = 0; i < activeApexInfos.size(); i++) {
+            final ScanPartition scanPartition = resolveApexToScanPartition(activeApexInfos.get(i));
+            if (scanPartition != null) {
+                scanPartitions.add(scanPartition);
+            }
+        }
+        return scanPartitions;
+    }
+
+    private static @Nullable ScanPartition resolveApexToScanPartition(
+            ApexManager.ActiveApexInfo apexInfo) {
+        for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
+            ScanPartition sp = SYSTEM_PARTITIONS.get(i);
+            if (apexInfo.preInstalledApexPath.getAbsolutePath().startsWith(
+                    sp.getFolder().getAbsolutePath())) {
+                return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX);
+            }
+        }
+        return null;
+    }
+
+    public OverlayConfig setUpSystemPackages(
+            WatchedArrayMap<String, PackageSetting> packageSettings, int[] userIds,
+            long startTime) {
+        PackageParser2 packageParser = mPm.mInjector.getScanningCachingPackageParser();
+
+        ExecutorService executorService = ParallelPackageParser.makeExecutorService();
+        // Prepare apex package info before scanning APKs, these information are needed when
+        // scanning apk in apex.
+        mPm.mApexManager.scanApexPackagesTraced(packageParser, executorService);
+
+        scanSystemDirs(packageParser, executorService);
+        // Parse overlay configuration files to set default enable state, mutability, and
+        // priority of system overlays.
+        OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance(
+                consumer -> mPm.forEachPackage(
+                        pkg -> consumer.accept(pkg, pkg.isSystem())));
+        cleanupSystemPackagesAndInstallStubs(packageParser, executorService, packageSettings,
+                startTime, userIds);
+        packageParser.close();
+        return overlayConfig;
+    }
     /**
      * First part of init dir scanning
      */
-    // TODO(b/197876467): consolidate this with cleanupSystemPackagesAndInstallStubs
     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
-    public void scanSystemDirs(List<ScanPartition>  dirsToScanAsSystem,
-            boolean isUpgrade, PackageParser2 packageParser,
-            ExecutorService executorService, AndroidPackage platformPackage,
-            boolean isPreNMR1Upgrade, int systemParseFlags, int systemScanFlags) {
+    private void scanSystemDirs(PackageParser2 packageParser, ExecutorService executorService) {
         File frameworkDir = new File(Environment.getRootDirectory(), "framework");
 
         // Collect vendor/product/system_ext overlay packages. (Do this before scanning
         // any apps.)
         // For security and version matching reason, only consider overlay packages if they
         // reside in the right directory.
-        for (int i = dirsToScanAsSystem.size() - 1; i >= 0; i--) {
-            final ScanPartition partition = dirsToScanAsSystem.get(i);
+        for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
+            final ScanPartition partition = mDirsToScanAsSystem.get(i);
             if (partition.getOverlayFolder() == null) {
                 continue;
             }
-            scanDirTracedLI(partition.getOverlayFolder(), systemParseFlags,
-                    systemScanFlags | partition.scanFlag, 0,
-                    packageParser, executorService, platformPackage, isUpgrade,
-                    isPreNMR1Upgrade);
+            scanDirTracedLI(partition.getOverlayFolder(), mSystemParseFlags,
+                    mSystemScanFlags | partition.scanFlag, 0,
+                    packageParser, executorService);
         }
 
-        scanDirTracedLI(frameworkDir, systemParseFlags,
-                systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
-                packageParser, executorService, platformPackage, isUpgrade, isPreNMR1Upgrade);
+        scanDirTracedLI(frameworkDir, mSystemParseFlags,
+                mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
+                packageParser, executorService);
         if (!mPm.mPackages.containsKey("android")) {
             throw new IllegalStateException(
                     "Failed to load frameworks package; check log for warnings");
         }
 
-        for (int i = 0, size = dirsToScanAsSystem.size(); i < size; i++) {
-            final ScanPartition partition = dirsToScanAsSystem.get(i);
+        for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
+            final ScanPartition partition = mDirsToScanAsSystem.get(i);
             if (partition.getPrivAppFolder() != null) {
-                scanDirTracedLI(partition.getPrivAppFolder(), systemParseFlags,
-                        systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
-                        packageParser, executorService, platformPackage, isUpgrade,
-                        isPreNMR1Upgrade);
+                scanDirTracedLI(partition.getPrivAppFolder(), mSystemParseFlags,
+                        mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
+                        packageParser, executorService);
             }
-            scanDirTracedLI(partition.getAppFolder(), systemParseFlags,
-                    systemScanFlags | partition.scanFlag, 0,
-                    packageParser, executorService, platformPackage, isUpgrade,
-                    isPreNMR1Upgrade);
+            scanDirTracedLI(partition.getAppFolder(), mSystemParseFlags,
+                    mSystemScanFlags | partition.scanFlag, 0,
+                    packageParser, executorService);
         }
     }
 
@@ -151,20 +226,17 @@
      * Second part of init dir scanning
      */
     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
-    public void cleanupSystemPackagesAndInstallStubs(List<ScanPartition> dirsToScanAsSystem,
-            boolean isUpgrade, PackageParser2 packageParser,
-            ExecutorService executorService, boolean onlyCore,
+    private void cleanupSystemPackagesAndInstallStubs(PackageParser2 packageParser,
+            ExecutorService executorService,
             WatchedArrayMap<String, PackageSetting> packageSettings,
-            long startTime, File appInstallDir, AndroidPackage platformPackage,
-            boolean isPreNMR1Upgrade, int scanFlags, int systemParseFlags, int systemScanFlags,
-            int[] userIds) {
+            long startTime, int[] userIds) {
         // Prune any system packages that no longer exist.
         final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
         // Stub packages must either be replaced with full versions in the /data
         // partition or be disabled.
         final List<String> stubSystemApps = new ArrayList<>();
 
-        if (!onlyCore) {
+        if (!mPm.isOnlyCoreApps()) {
             // do this first before mucking with mPackages for the "expecting better" case
             final int numPackages = mPm.mPackages.size();
             for (int index = 0; index < numPackages; index++) {
@@ -209,7 +281,7 @@
                                         + "; scanned versionCode="
                                         + scannedPkg.getLongVersionCode());
                         mRemovePackageHelper.removePackageLI(scannedPkg, true);
-                        mPm.mExpectingBetter.put(ps.getPackageName(), ps.getPath());
+                        mExpectingBetter.put(ps.getPackageName(), ps.getPath());
                     }
 
                     continue;
@@ -233,9 +305,7 @@
                         // We're expecting that the system app should remain disabled, but add
                         // it to expecting better to recover in case the data version cannot
                         // be scanned.
-                        // TODO(b/197869066): mExpectingBetter should be able to pulled out into
-                        // this class and used only by the PMS initialization
-                        mPm.mExpectingBetter.put(disabledPs.getPackageName(), disabledPs.getPath());
+                        mExpectingBetter.put(disabledPs.getPackageName(), disabledPs.getPath());
                     }
                 }
             }
@@ -252,19 +322,18 @@
                 + " , timePerPackage: "
                 + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
                 + " , cached: " + cachedSystemApps);
-        if (isUpgrade && systemPackagesCount > 0) {
+        if (mPm.isDeviceUpgrading() && systemPackagesCount > 0) {
             //CHECKSTYLE:OFF IndentationCheck
             FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
                     BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME,
                     systemScanTime / systemPackagesCount);
             //CHECKSTYLE:ON IndentationCheck
         }
-        if (!onlyCore) {
+        if (!mPm.isOnlyCoreApps()) {
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                     SystemClock.uptimeMillis());
-            scanDirTracedLI(appInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
-                    packageParser, executorService, platformPackage, isUpgrade,
-                    isPreNMR1Upgrade);
+            scanDirTracedLI(mPm.getAppInstallDir(), 0, mScanFlags | SCAN_REQUIRE_KNOWN, 0,
+                    packageParser, executorService);
 
         }
 
@@ -274,7 +343,7 @@
                     + unfinishedTasks);
         }
 
-        if (!onlyCore) {
+        if (!mPm.isOnlyCoreApps()) {
             final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
             // Remove disable package settings for updated system apps that were
             // removed via an OTA. If the update is no longer present, remove the
@@ -308,7 +377,7 @@
                     mRemovePackageHelper.removePackageLI(pkg, true);
                     try {
                         final File codePath = new File(pkg.getPath());
-                        scanPackageHelper.scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
+                        scanPackageHelper.scanPackageTracedLI(codePath, 0, mScanFlags, 0, null);
                     } catch (PackageManagerException e) {
                         Slog.e(TAG, "Failed to parse updated, ex-system package: "
                                 + e.getMessage());
@@ -332,27 +401,27 @@
              * the userdata partition actually showed up. If they never
              * appeared, crawl back and revive the system version.
              */
-            for (int i = 0; i < mPm.mExpectingBetter.size(); i++) {
-                final String packageName = mPm.mExpectingBetter.keyAt(i);
+            for (int i = 0; i < mExpectingBetter.size(); i++) {
+                final String packageName = mExpectingBetter.keyAt(i);
                 if (!mPm.mPackages.containsKey(packageName)) {
-                    final File scanFile = mPm.mExpectingBetter.valueAt(i);
+                    final File scanFile = mExpectingBetter.valueAt(i);
 
                     logCriticalInfo(Log.WARN, "Expected better " + packageName
                             + " but never showed up; reverting to system");
 
                     @ParsingPackageUtils.ParseFlags int reparseFlags = 0;
                     @PackageManagerService.ScanFlags int rescanFlags = 0;
-                    for (int i1 = dirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
-                        final ScanPartition partition = dirsToScanAsSystem.get(i1);
+                    for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
+                        final ScanPartition partition = mDirsToScanAsSystem.get(i1);
                         if (partition.containsPrivApp(scanFile)) {
-                            reparseFlags = systemParseFlags;
-                            rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
+                            reparseFlags = mSystemParseFlags;
+                            rescanFlags = mSystemScanFlags | SCAN_AS_PRIVILEGED
                                     | partition.scanFlag;
                             break;
                         }
                         if (partition.containsApp(scanFile)) {
-                            reparseFlags = systemParseFlags;
-                            rescanFlags = systemScanFlags | partition.scanFlag;
+                            reparseFlags = mSystemParseFlags;
+                            rescanFlags = mSystemScanFlags | partition.scanFlag;
                             break;
                         }
                     }
@@ -378,7 +447,7 @@
 
             // Uncompress and install any stubbed system applications.
             // This must be done last to ensure all stubs are replaced or disabled.
-            installSystemStubPackages(stubSystemApps, scanFlags);
+            installSystemStubPackages(stubSystemApps, mScanFlags);
 
             final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
                     - cachedSystemApps;
@@ -390,7 +459,7 @@
                     + " , timePerPackage: "
                     + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
                     + " , cached: " + cachedNonSystemApps);
-            if (isUpgrade && dataPackagesCount > 0) {
+            if (mPm.isDeviceUpgrading() && dataPackagesCount > 0) {
                 //CHECKSTYLE:OFF IndentationCheck
                 FrameworkStatsLog.write(
                         FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
@@ -399,19 +468,17 @@
                 //CHECKSTYLE:OFF IndentationCheck
             }
         }
-        mPm.mExpectingBetter.clear();
+        mExpectingBetter.clear();
 
         mPm.mSettings.pruneRenamedPackagesLPw();
     }
 
     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
     private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
-            long currentTime, PackageParser2 packageParser, ExecutorService executorService,
-            AndroidPackage platformPackage, boolean isUpgrade, boolean isPreNMR1Upgrade) {
+            long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
         try {
-            scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService,
-                    platformPackage, isUpgrade, isPreNMR1Upgrade);
+            scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
@@ -419,8 +486,7 @@
 
     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
     private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
-            PackageParser2 packageParser, ExecutorService executorService,
-            AndroidPackage platformPackage, boolean isUpgrade, boolean isPreNMR1Upgrade) {
+            PackageParser2 packageParser, ExecutorService executorService) {
         final File[] files = scanDir.listFiles();
         if (ArrayUtils.isEmpty(files)) {
             Log.d(TAG, "No files in app dir " + scanDir);
@@ -465,8 +531,7 @@
                 }
                 try {
                     scanPackageHelper.addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
-                            currentTime, null, platformPackage, isUpgrade,
-                            isPreNMR1Upgrade);
+                            currentTime, null);
                 } catch (PackageManagerException e) {
                     errorCode = e.error;
                     errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage();
@@ -564,9 +629,8 @@
      */
     @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
     public boolean enableCompressedPackage(AndroidPackage stubPkg,
-            @NonNull PackageSetting stubPkgSetting, int defParseFlags,
-            List<ScanPartition> dirsToScanAsSystem) {
-        final int parseFlags = defParseFlags | ParsingPackageUtils.PARSE_CHATTY
+            @NonNull PackageSetting stubPkgSetting) {
+        final int parseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_CHATTY
                 | ParsingPackageUtils.PARSE_ENFORCE_CODE;
         synchronized (mPm.mInstallLock) {
             final AndroidPackage pkg;
@@ -599,7 +663,7 @@
                     installPackageFromSystemLIF(stubPkg.getPath(),
                             mPm.mUserManager.getUserIds() /*allUserHandles*/,
                             null /*origUserHandles*/,
-                            true /*writeSettings*/, defParseFlags, dirsToScanAsSystem);
+                            true /*writeSettings*/);
                 } catch (PackageManagerException pme) {
                     // Serious WTF; we have to be able to install the stub
                     Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(),
@@ -715,7 +779,7 @@
             // we cannot retrieve the setting {@link Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL}.
             // When we no longer need to read that setting, cblock release can occur always
             // occur here directly
-            if (!mPm.mSystemReady) {
+            if (!mPm.isSystemReady()) {
                 if (mPm.mReleaseOnSystemReady == null) {
                     mPm.mReleaseOnSystemReady = new ArrayList<>();
                 }
@@ -749,7 +813,7 @@
     public void restoreDisabledSystemPackageLIF(DeletePackageAction action,
             PackageSetting deletedPs, @NonNull int[] allUserHandles,
             @Nullable PackageRemovedInfo outInfo,
-            boolean writeSettings, int defParseFlags, List<ScanPartition> dirsToScanAsSystem,
+            boolean writeSettings,
             PackageSetting disabledPs)
             throws SystemDeleteException {
         // writer
@@ -768,8 +832,7 @@
         if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
         try {
             installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles,
-                    outInfo == null ? null : outInfo.mOrigUsers, writeSettings, defParseFlags,
-                    dirsToScanAsSystem);
+                    outInfo == null ? null : outInfo.mOrigUsers, writeSettings);
         } catch (PackageManagerException e) {
             Slog.w(TAG, "Failed to restore system package:" + deletedPs.getPackageName() + ": "
                     + e.getMessage());
@@ -808,17 +871,16 @@
      */
     @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
     private void installPackageFromSystemLIF(@NonNull String codePathString,
-            @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings,
-            int defParseFlags, List<ScanPartition> dirsToScanAsSystem)
+            @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings)
             throws PackageManagerException {
         final File codePath = new File(codePathString);
         @ParsingPackageUtils.ParseFlags int parseFlags =
-                defParseFlags
+                mPm.getDefParseFlags()
                         | ParsingPackageUtils.PARSE_MUST_BE_APK
                         | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
         @PackageManagerService.ScanFlags int scanFlags = SCAN_AS_SYSTEM;
-        for (int i = dirsToScanAsSystem.size() - 1; i >= 0; i--) {
-            ScanPartition partition = dirsToScanAsSystem.get(i);
+        for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
+            ScanPartition partition = mDirsToScanAsSystem.get(i);
             if (partition.containsFile(codePath)) {
                 scanFlags |= partition.scanFlag;
                 if (partition.containsPrivApp(codePath)) {
@@ -894,4 +956,8 @@
             }
         }
     }
+
+    public boolean isExpectingBetter(String packageName) {
+        return mExpectingBetter.containsKey(packageName);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index cb390f0..7569900 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -38,12 +38,9 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.backup.IBackupManager;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.DataLoaderType;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
@@ -53,7 +50,6 @@
 import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.component.ComponentMutateUtils;
 import android.content.pm.parsing.component.ParsedInstrumentation;
-import android.content.pm.pkg.PackageUserState;
 import android.os.Binder;
 import android.os.Message;
 import android.os.Process;
@@ -67,11 +63,8 @@
 import android.util.Log;
 import android.util.Slog;
 
-import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.ResolverActivity;
 import com.android.internal.util.ArrayUtils;
-import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
@@ -478,46 +471,11 @@
         final String pkgName = pkg.getPackageName();
         if (mPm.mCustomResolverComponentName != null
                 && mPm.mCustomResolverComponentName.getPackageName().equals(pkg.getPackageName())) {
-            setUpCustomResolverActivity(pkg, pkgSetting);
+            mPm.setUpCustomResolverActivity(pkg, pkgSetting);
         }
 
         if (pkg.getPackageName().equals("android")) {
-            synchronized (mPm.mLock) {
-                // Set up information for our fall-back user intent resolution activity.
-                mPm.mPlatformPackage = pkg;
-
-                // The instance stored in PackageManagerService is special cased to be non-user
-                // specific, so initialize all the needed fields here.
-                mPm.mAndroidApplication = PackageInfoUtils.generateApplicationInfo(pkg, 0,
-                        PackageUserState.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting);
-
-                if (!mPm.mResolverReplaced) {
-                    mPm.mResolveActivity.applicationInfo = mPm.mAndroidApplication;
-                    mPm.mResolveActivity.name = ResolverActivity.class.getName();
-                    mPm.mResolveActivity.packageName = mPm.mAndroidApplication.packageName;
-                    mPm.mResolveActivity.processName = "system:ui";
-                    mPm.mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
-                    mPm.mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
-                    mPm.mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
-                    mPm.mResolveActivity.theme = R.style.Theme_Material_Dialog_Alert;
-                    mPm.mResolveActivity.exported = true;
-                    mPm.mResolveActivity.enabled = true;
-                    mPm.mResolveActivity.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
-                    mPm.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;
-                    mPm.mResolveInfo.activityInfo = mPm.mResolveActivity;
-                    mPm.mResolveInfo.priority = 0;
-                    mPm.mResolveInfo.preferredOrder = 0;
-                    mPm.mResolveInfo.match = 0;
-                    mPm.mResolveComponentName = new ComponentName(
-                            mPm.mAndroidApplication.packageName, mPm.mResolveActivity.name);
-                }
-                PackageManagerService.onChanged();
-            }
+            mPm.setPlatformPackage(pkg, pkgSetting);
         }
 
         ArrayList<AndroidPackage> clientLibPkgs = null;
@@ -633,37 +591,6 @@
         Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
     }
 
-    private void setUpCustomResolverActivity(AndroidPackage pkg, PackageSetting pkgSetting) {
-        synchronized (mPm.mLock) {
-            mPm.mResolverReplaced = true;
-
-            // The instance created in PackageManagerService is special cased to be non-user
-            // specific, so initialize all the needed fields here.
-            ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(pkg, 0,
-                    PackageUserState.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting);
-
-            // Set up information for custom user intent resolution activity.
-            mPm.mResolveActivity.applicationInfo = appInfo;
-            mPm.mResolveActivity.name = mPm.mCustomResolverComponentName.getClassName();
-            mPm.mResolveActivity.packageName = pkg.getPackageName();
-            mPm.mResolveActivity.processName = pkg.getProcessName();
-            mPm.mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
-            mPm.mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS
-                    | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
-            mPm.mResolveActivity.theme = 0;
-            mPm.mResolveActivity.exported = true;
-            mPm.mResolveActivity.enabled = true;
-            mPm.mResolveInfo.activityInfo = mPm.mResolveActivity;
-            mPm.mResolveInfo.priority = 0;
-            mPm.mResolveInfo.preferredOrder = 0;
-            mPm.mResolveInfo.match = 0;
-            mPm.mResolveComponentName = mPm.mCustomResolverComponentName;
-            PackageManagerService.onChanged();
-            Slog.i(TAG, "Replacing default ResolverActivity with custom activity: "
-                    + mPm.mResolveComponentName);
-        }
-    }
-
     /**
      * If the database version for this type of package (internal storage or
      * external storage) is less than the version where package signatures
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
index cf10f34..bfb5f76 100644
--- a/services/core/java/com/android/server/pm/InstallParams.java
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -734,7 +734,7 @@
 
         // Retrieve PackageSettings and parse package
         @ParsingPackageUtils.ParseFlags final int parseFlags =
-                mPm.mDefParseFlags | ParsingPackageUtils.PARSE_CHATTY
+                mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_CHATTY
                 | ParsingPackageUtils.PARSE_ENFORCE_CODE
                 | (onExternal ? ParsingPackageUtils.PARSE_EXTERNAL_STORAGE : 0);
 
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 9595e15..68801d6 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -128,7 +128,7 @@
                 PLATFORM_PACKAGE_NAME.equals(pkgSetting.getPkg().getPackageName());
         synchronized (mPackageManagerService.mLock) {
             // Important: the packages we need to run with ab-ota compiler-reason.
-            important = PackageManagerServiceUtils.getPackagesForDexopt(
+            important = DexOptHelper.getPackagesForDexopt(
                     mPackageManagerService.mSettings.getPackagesLocked().values(),
                     mPackageManagerService, DEBUG_DEXOPT);
             // Remove Platform Package from A/B OTA b/160735835.
@@ -160,7 +160,7 @@
         long spaceAvailable = getAvailableSpace();
         if (spaceAvailable < BULK_DELETE_THRESHOLD) {
             Log.i(TAG, "Low on space, deleting oat files in an attempt to free up space: "
-                    + PackageManagerServiceUtils.packagesToString(others));
+                    + DexOptHelper.packagesToString(others));
             for (PackageSetting pkg : others) {
                 mPackageManagerService.deleteOatArtifactsOfPackage(pkg.getPackageName());
             }
diff --git a/services/core/java/com/android/server/pm/PackageManagerNative.java b/services/core/java/com/android/server/pm/PackageManagerNative.java
new file mode 100644
index 0000000..37daf11
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageManagerNative.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
+
+import static com.android.server.pm.PackageManagerService.TAG;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageChangeObserver;
+import android.content.pm.IPackageManagerNative;
+import android.content.pm.IStagedApexObserver;
+import android.content.pm.PackageInfo;
+import android.content.pm.StagedApexInfo;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Slog;
+
+import java.util.Arrays;
+
+final class PackageManagerNative extends IPackageManagerNative.Stub {
+    private final PackageManagerService mPm;
+
+    PackageManagerNative(PackageManagerService pm) {
+        mPm = pm;
+    }
+
+    @Override
+    public void registerPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
+        synchronized (mPm.mPackageChangeObservers) {
+            try {
+                observer.asBinder().linkToDeath(
+                        new PackageChangeObserverDeathRecipient(observer), 0);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.getMessage());
+            }
+            mPm.mPackageChangeObservers.add(observer);
+            Log.d(TAG, "Size of mPackageChangeObservers after registry is "
+                    + mPm.mPackageChangeObservers.size());
+        }
+    }
+
+    @Override
+    public void unregisterPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
+        synchronized (mPm.mPackageChangeObservers) {
+            mPm.mPackageChangeObservers.remove(observer);
+            Log.d(TAG, "Size of mPackageChangeObservers after unregistry is "
+                    + mPm.mPackageChangeObservers.size());
+        }
+    }
+
+    @Override
+    public String[] getAllPackages() {
+        return mPm.getAllPackages().toArray(new String[0]);
+    }
+
+    @Override
+    public String[] getNamesForUids(int[] uids) throws RemoteException {
+        String[] names = null;
+        String[] results = null;
+        try {
+            if (uids == null || uids.length == 0) {
+                return null;
+            }
+            names = mPm.getNamesForUids(uids);
+            results = (names != null) ? names : new String[uids.length];
+            // massage results so they can be parsed by the native binder
+            for (int i = results.length - 1; i >= 0; --i) {
+                if (results[i] == null) {
+                    results[i] = "";
+                }
+            }
+            return results;
+        } catch (Throwable t) {
+            // STOPSHIP(186558987): revert addition of try/catch/log
+            Slog.e(TAG, "uids: " + Arrays.toString(uids));
+            Slog.e(TAG, "names: " + Arrays.toString(names));
+            Slog.e(TAG, "results: " + Arrays.toString(results));
+            Slog.e(TAG, "throwing exception", t);
+            throw t;
+        }
+    }
+
+    // NB: this differentiates between preloads and sideloads
+    @Override
+    public String getInstallerForPackage(String packageName) throws RemoteException {
+        final String installerName = mPm.getInstallerPackageName(packageName);
+        if (!TextUtils.isEmpty(installerName)) {
+            return installerName;
+        }
+        // differentiate between preload and sideload
+        int callingUser = UserHandle.getUserId(Binder.getCallingUid());
+        ApplicationInfo appInfo = mPm.getApplicationInfo(packageName,
+                /*flags*/ 0,
+                /*userId*/ callingUser);
+        if (appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+            return "preload";
+        }
+        return "";
+    }
+
+    @Override
+    public long getVersionCodeForPackage(String packageName) throws RemoteException {
+        try {
+            int callingUser = UserHandle.getUserId(Binder.getCallingUid());
+            PackageInfo pInfo = mPm.getPackageInfo(packageName, 0, callingUser);
+            if (pInfo != null) {
+                return pInfo.getLongVersionCode();
+            }
+        } catch (Exception e) {
+        }
+        return 0;
+    }
+
+    @Override
+    public int getTargetSdkVersionForPackage(String packageName) throws RemoteException {
+        int targetSdk = mPm.getTargetSdkVersion(packageName);
+        if (targetSdk != -1) {
+            return targetSdk;
+        }
+
+        throw new RemoteException("Couldn't get targetSdkVersion for package " + packageName);
+    }
+
+    @Override
+    public boolean isPackageDebuggable(String packageName) throws RemoteException {
+        int callingUser = UserHandle.getCallingUserId();
+        ApplicationInfo appInfo = mPm.getApplicationInfo(packageName, 0, callingUser);
+        if (appInfo != null) {
+            return (0 != (appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE));
+        }
+
+        throw new RemoteException("Couldn't get debug flag for package " + packageName);
+    }
+
+    @Override
+    public boolean[] isAudioPlaybackCaptureAllowed(String[] packageNames)
+            throws RemoteException {
+        int callingUser = UserHandle.getUserId(Binder.getCallingUid());
+        boolean[] results = new boolean[packageNames.length];
+        for (int i = results.length - 1; i >= 0; --i) {
+            ApplicationInfo appInfo = mPm.getApplicationInfo(packageNames[i], 0, callingUser);
+            results[i] = appInfo != null && appInfo.isAudioPlaybackCaptureAllowed();
+        }
+        return results;
+    }
+
+    @Override
+    public int getLocationFlags(String packageName) throws RemoteException {
+        int callingUser = UserHandle.getUserId(Binder.getCallingUid());
+        ApplicationInfo appInfo = mPm.getApplicationInfo(packageName,
+                /*flags*/ 0,
+                /*userId*/ callingUser);
+        if (appInfo == null) {
+            throw new RemoteException(
+                    "Couldn't get ApplicationInfo for package " + packageName);
+        }
+        return ((appInfo.isSystemApp() ? IPackageManagerNative.LOCATION_SYSTEM : 0)
+                | (appInfo.isVendor() ? IPackageManagerNative.LOCATION_VENDOR : 0)
+                | (appInfo.isProduct() ? IPackageManagerNative.LOCATION_PRODUCT : 0));
+    }
+
+    @Override
+    public String getModuleMetadataPackageName() throws RemoteException {
+        return mPm.getModuleMetadataPackageName();
+    }
+
+    @Override
+    public boolean hasSha256SigningCertificate(String packageName, byte[] certificate)
+            throws RemoteException {
+        return mPm.hasSigningCertificate(packageName, certificate, CERT_INPUT_SHA256);
+    }
+
+    @Override
+    public boolean hasSystemFeature(String featureName, int version) {
+        return mPm.hasSystemFeature(featureName, version);
+    }
+
+    @Override
+    public void registerStagedApexObserver(IStagedApexObserver observer) {
+        mPm.mInstallerService.getStagingManager().registerStagedApexObserver(observer);
+    }
+
+    @Override
+    public void unregisterStagedApexObserver(IStagedApexObserver observer) {
+        mPm.mInstallerService.getStagingManager().unregisterStagedApexObserver(observer);
+    }
+
+    @Override
+    public String[] getStagedApexModuleNames() {
+        return mPm.mInstallerService.getStagingManager()
+                .getStagedApexModuleNames().toArray(new String[0]);
+    }
+
+    @Override
+    @Nullable
+    public StagedApexInfo getStagedApexInfo(String moduleName) {
+        return mPm.mInstallerService.getStagingManager().getStagedApexInfo(moduleName);
+    }
+
+    private final class PackageChangeObserverDeathRecipient implements IBinder.DeathRecipient {
+        private final IPackageChangeObserver mObserver;
+
+        PackageChangeObserverDeathRecipient(IPackageChangeObserver observer) {
+            mObserver = observer;
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mPm.mPackageChangeObservers) {
+                mPm.mPackageChangeObservers.remove(mObserver);
+                Log.d(TAG, "Size of mPackageChangeObservers after removing dead observer is "
+                        + mPm.mPackageChangeObservers.size());
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 192bd43..3eaa26e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -48,10 +48,8 @@
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
 import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME;
-import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
 import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter;
 import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
 import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
 
@@ -100,10 +98,8 @@
 import android.content.pm.IPackageInstaller;
 import android.content.pm.IPackageLoadingProgressCallback;
 import android.content.pm.IPackageManager;
-import android.content.pm.IPackageManagerNative;
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
-import android.content.pm.IStagedApexObserver;
 import android.content.pm.IncrementalStatesInfo;
 import android.content.pm.InstallSourceInfo;
 import android.content.pm.InstantAppInfo;
@@ -137,13 +133,11 @@
 import android.content.pm.Signature;
 import android.content.pm.SigningDetails;
 import android.content.pm.SigningInfo;
-import android.content.pm.StagedApexInfo;
 import android.content.pm.SuspendDialogInfo;
 import android.content.pm.TestUtilityService;
 import android.content.pm.UserInfo;
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VersionedPackage;
-import android.content.pm.dex.ArtManager;
 import android.content.pm.dex.IArtManager;
 import android.content.pm.overlay.OverlayPaths;
 import android.content.pm.parsing.ParsingPackageUtils;
@@ -216,10 +210,10 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.ResolverActivity;
 import com.android.internal.content.F2fsUtils;
 import com.android.internal.content.PackageHelper;
 import com.android.internal.content.om.OverlayConfig;
-import com.android.internal.logging.MetricsLogger;
 import com.android.internal.telephony.CarrierAppUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
@@ -238,7 +232,6 @@
 import com.android.server.SystemConfig;
 import com.android.server.Watchdog;
 import com.android.server.apphibernation.AppHibernationManagerInternal;
-import com.android.server.apphibernation.AppHibernationService;
 import com.android.server.compat.CompatChange;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.Installer.InstallerException;
@@ -246,7 +239,6 @@
 import com.android.server.pm.dex.ArtManagerService;
 import com.android.server.pm.dex.ArtUtils;
 import com.android.server.pm.dex.DexManager;
-import com.android.server.pm.dex.DexoptOptions;
 import com.android.server.pm.dex.PackageDexUsage;
 import com.android.server.pm.dex.ViewCompiler;
 import com.android.server.pm.parsing.PackageCacher;
@@ -300,7 +292,6 @@
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -312,9 +303,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.BiConsumer;
@@ -388,9 +377,6 @@
     static final boolean DEBUG_ABI_SELECTION = false;
     public static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
 
-    /** REMOVE. According to Svet, this was only used to reset permissions during development. */
-    static final boolean CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE = false;
-
     static final boolean HIDE_EPHEMERAL_APIS = false;
 
     static final String PRECOMPILE_LAYOUTS = "pm.precompile_layouts";
@@ -457,40 +443,40 @@
         PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface PackageStartability {}
+    private @interface PackageStartability {}
 
     /**
      * Used as the result code of the {@link #getPackageStartability} to indicate
      * the given package is allowed to start.
      */
-    static final int PACKAGE_STARTABILITY_OK = 0;
+    private static final int PACKAGE_STARTABILITY_OK = 0;
 
     /**
      * Used as the result code of the {@link #getPackageStartability} to indicate
      * the given package is <b>not</b> allowed to start because it's not found
      * (could be due to that package is invisible to the given user).
      */
-    static final int PACKAGE_STARTABILITY_NOT_FOUND = 1;
+    private static final int PACKAGE_STARTABILITY_NOT_FOUND = 1;
 
     /**
      * Used as the result code of the {@link #getPackageStartability} to indicate
      * the given package is <b>not</b> allowed to start because it's not a system app
      * and the system is running in safe mode.
      */
-    static final int PACKAGE_STARTABILITY_NOT_SYSTEM = 2;
+    private static final int PACKAGE_STARTABILITY_NOT_SYSTEM = 2;
 
     /**
      * Used as the result code of the {@link #getPackageStartability} to indicate
      * the given package is <b>not</b> allowed to start because it's currently frozen.
      */
-    static final int PACKAGE_STARTABILITY_FROZEN = 3;
+    private static final int PACKAGE_STARTABILITY_FROZEN = 3;
 
     /**
      * Used as the result code of the {@link #getPackageStartability} to indicate
      * the given package is <b>not</b> allowed to start because it doesn't support
      * direct boot.
      */
-    static final int PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED = 4;
+    private static final int PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED = 4;
 
     private static final String STATIC_SHARED_LIB_DELIMITER = "_";
     /** Extension of the compressed packages */
@@ -585,19 +571,6 @@
 
     public static final int REASON_LAST = REASON_SHARED;
 
-    /**
-     * The initial enabled state of the cache before other checks are done.
-     */
-    private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = true;
-
-    /**
-     * Whether to skip all other checks and force the cache to be enabled.
-     *
-     * Setting this to true will cause the cache to be named "debug" to avoid eviction from
-     * build fingerprint changes.
-     */
-    private static final boolean FORCE_PACKAGE_PARSED_CACHE_ENABLED = false;
-
     static final String RANDOM_DIR_PREFIX = "~~";
 
     final Handler mHandler;
@@ -609,17 +582,14 @@
     private final int mSdkVersion;
     final Context mContext;
     final boolean mFactoryTest;
-    final boolean mOnlyCore;
+    private final boolean mOnlyCore;
     final DisplayMetrics mMetrics;
-    final int mDefParseFlags;
-    final String[] mSeparateProcesses;
-    final boolean mIsUpgrade;
-    final boolean mIsPreNUpgrade;
-    final boolean mIsPreNMR1Upgrade;
-    final boolean mIsPreQUpgrade;
-
-    @GuardedBy("mLock")
-    private boolean mDexOptDialogShown;
+    private final int mDefParseFlags;
+    private final String[] mSeparateProcesses;
+    private final boolean mIsUpgrade;
+    private final boolean mIsPreNUpgrade;
+    private final boolean mIsPreNMR1Upgrade;
+    private final boolean mIsPreQUpgrade;
 
     // Used for privilege escalation. MUST NOT BE CALLED WITH mPackages
     // LOCK HELD.  Can be called with mInstallLock held.
@@ -667,13 +637,6 @@
                                    "PackageManagerService.mIsolatedOwners");
 
     /**
-     * Tracks new system packages [received in an OTA] that we expect to
-     * find updated user-installed versions. Keys are package name, values
-     * are package location.
-     */
-    final ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
-
-    /**
      * Tracks existing packages prior to receiving an OTA. Keys are package name.
      * Only non-null during an OTA, and even then it is nulled again once systemReady().
      */
@@ -719,7 +682,7 @@
     @GuardedBy("mLoadedVolumes")
     final ArraySet<String> mLoadedVolumes = new ArraySet<>();
 
-    boolean mFirstBoot;
+    private boolean mFirstBoot;
 
     final boolean mIsEngBuild;
     private final boolean mIsUserDebugBuild;
@@ -765,12 +728,10 @@
     public static final List<ScanPartition> SYSTEM_PARTITIONS = Collections.unmodifiableList(
             PackagePartitions.getOrderedPartitions(ScanPartition::new));
 
-    private final List<ScanPartition> mDirsToScanAsSystem;
-
-    final OverlayConfig mOverlayConfig;
+    private @NonNull final OverlayConfig mOverlayConfig;
 
     @GuardedBy("itself")
-    final private ArrayList<IPackageChangeObserver> mPackageChangeObservers =
+    final ArrayList<IPackageChangeObserver> mPackageChangeObservers =
         new ArrayList<>();
 
     // Cached parsed flag value. Invalidated on each flag change.
@@ -877,7 +838,7 @@
     int mPendingEnableRollbackToken = 0;
 
     @Watched(manual = true)
-    volatile boolean mSystemReady;
+    private volatile boolean mSystemReady;
     @Watched(manual = true)
     private volatile boolean mSafeMode;
     @Watched
@@ -885,16 +846,16 @@
             new WatchedSparseBooleanArray();
 
     @Watched(manual = true)
-    ApplicationInfo mAndroidApplication;
+    private ApplicationInfo mAndroidApplication;
     @Watched(manual = true)
-    final ActivityInfo mResolveActivity = new ActivityInfo();
-    final ResolveInfo mResolveInfo = new ResolveInfo();
+    private final ActivityInfo mResolveActivity = new ActivityInfo();
+    private final ResolveInfo mResolveInfo = new ResolveInfo();
     @Watched(manual = true)
     ComponentName mResolveComponentName;
-    AndroidPackage mPlatformPackage;
+    private AndroidPackage mPlatformPackage;
     ComponentName mCustomResolverComponentName;
 
-    boolean mResolverReplaced = false;
+    private boolean mResolverReplaced = false;
 
     @NonNull
     final DomainVerificationManagerInternal mDomainVerificationManager;
@@ -1011,6 +972,7 @@
     private final AppDataHelper mAppDataHelper;
     private final PreferredActivityHelper mPreferredActivityHelper;
     private final ResolveIntentHelper mResolveIntentHelper;
+    private final DexOptHelper mDexOptHelper;
 
     /**
      * Invalidate the package info cache, which includes updating the cached computer.
@@ -1540,7 +1502,7 @@
 
         m.installAllowlistedSystemPackages();
         ServiceManager.addService("package", m);
-        final PackageManagerNative pmn = m.new PackageManagerNative();
+        final PackageManagerNative pmn = new PackageManagerNative(m);
         ServiceManager.addService("package_native", pmn);
         return m;
     }
@@ -1557,39 +1519,6 @@
         }
     }
 
-    /**
-     * Requests that files preopted on a secondary system partition be copied to the data partition
-     * if possible.  Note that the actual copying of the files is accomplished by init for security
-     * reasons. This simply requests that the copy takes place and awaits confirmation of its
-     * completion. See platform/system/extras/cppreopt/ for the implementation of the actual copy.
-     */
-    private static void requestCopyPreoptedFiles() {
-        final int WAIT_TIME_MS = 100;
-        final String CP_PREOPT_PROPERTY = "sys.cppreopt";
-        if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) {
-            SystemProperties.set(CP_PREOPT_PROPERTY, "requested");
-            // We will wait for up to 100 seconds.
-            final long timeStart = SystemClock.uptimeMillis();
-            final long timeEnd = timeStart + 100 * 1000;
-            long timeNow = timeStart;
-            while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) {
-                try {
-                    Thread.sleep(WAIT_TIME_MS);
-                } catch (InterruptedException e) {
-                    // Do nothing
-                }
-                timeNow = SystemClock.uptimeMillis();
-                if (timeNow > timeEnd) {
-                    SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out");
-                    Slog.wtf(TAG, "cppreopt did not finish!");
-                    break;
-                }
-            }
-
-            Slog.i(TAG, "cppreopts took " + (timeNow - timeStart) + " ms");
-        }
-    }
-
     // Link watchables to the class
     private void registerObserver() {
         mPackages.registerObserver(mWatcher);
@@ -1639,7 +1568,6 @@
         mDefaultAppProvider = testParams.defaultAppProvider;
         mLegacyPermissionManager = testParams.legacyPermissionManagerInternal;
         mDexManager = testParams.dexManager;
-        mDirsToScanAsSystem = testParams.dirsToScanAsSystem;
         mFactoryTest = testParams.factoryTest;
         mIncrementalManager = testParams.incrementalManager;
         mInstallerService = testParams.installerService;
@@ -1700,15 +1628,14 @@
         mIncrementalVersion = testParams.incrementalVersion;
         mDomainVerificationConnection = new DomainVerificationConnection(this);
 
-        mBroadcastHelper = new BroadcastHelper(mInjector);
-        mAppDataHelper = new AppDataHelper(this);
-        mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
-        mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(this, mRemovePackageHelper,
-                mAppDataHelper);
-        mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
-                mInitAndSystemPackageHelper, mAppDataHelper);
-        mPreferredActivityHelper = new PreferredActivityHelper(this);
-        mResolveIntentHelper = new ResolveIntentHelper(this, mPreferredActivityHelper);
+        mBroadcastHelper = testParams.broadcastHelper;
+        mAppDataHelper = testParams.appDataHelper;
+        mRemovePackageHelper = testParams.removePackageHelper;
+        mInitAndSystemPackageHelper = testParams.initAndSystemPackageHelper;
+        mDeletePackageHelper = testParams.deletePackageHelper;
+        mPreferredActivityHelper = testParams.preferredActivityHelper;
+        mResolveIntentHelper = testParams.resolveIntentHelper;
+        mDexOptHelper = testParams.dexOptHelper;
 
         invalidatePackageInfoCache();
     }
@@ -1831,22 +1758,8 @@
         mApexManager = injector.getApexManager();
         mAppsFilter = mInjector.getAppsFilter();
 
-        final List<ScanPartition> scanPartitions = new ArrayList<>();
-        final List<ApexManager.ActiveApexInfo> activeApexInfos = mApexManager.getActiveApexInfos();
-        for (int i = 0; i < activeApexInfos.size(); i++) {
-            final ScanPartition scanPartition = resolveApexToScanPartition(activeApexInfos.get(i));
-            if (scanPartition != null) {
-                scanPartitions.add(scanPartition);
-            }
-        }
-
         mInstantAppRegistry = new InstantAppRegistry(this, mPermissionManager, mPmInternal);
 
-        mDirsToScanAsSystem = new ArrayList<>();
-        mDirsToScanAsSystem.addAll(injector.getSystemPartitions());
-        mDirsToScanAsSystem.addAll(scanPartitions);
-        Slog.d(TAG, "Directories scanned as system partitions: " + mDirsToScanAsSystem);
-
         mAppInstallDir = new File(Environment.getDataDirectory(), "app");
         mAppLib32InstallDir = getAppLib32InstallDir();
 
@@ -1863,6 +1776,7 @@
                 mInitAndSystemPackageHelper, mAppDataHelper);
         mPreferredActivityHelper = new PreferredActivityHelper(this);
         mResolveIntentHelper = new ResolveIntentHelper(this, mPreferredActivityHelper);
+        mDexOptHelper = new DexOptHelper(this);
 
         synchronized (mLock) {
             // Create the computer as soon as the state objects have been installed.  The
@@ -1927,7 +1841,7 @@
             mPermissionManager.readLegacyPermissionStateTEMP();
 
             if (!mOnlyCore && mFirstBoot) {
-                requestCopyPreoptedFiles();
+                DexOptHelper.requestCopyPreoptedFiles();
             }
 
             String customResolverActivityName = Resources.getSystem().getString(
@@ -1984,40 +1898,12 @@
                 }
             }
 
-            mCacheDir = preparePackageParserCache(mIsEngBuild);
+            mCacheDir = PackageManagerServiceUtils.preparePackageParserCache(
+                    mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion);
 
-            // Set flag to monitor and not change apk file paths when
-            // scanning install directories.
-            int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
-
-            if (mIsUpgrade || mFirstBoot) {
-                scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
-            }
-
-            final int systemParseFlags = mDefParseFlags | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
-            final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM;
-
-            PackageParser2 packageParser = injector.getScanningCachingPackageParser();
-
-            ExecutorService executorService = ParallelPackageParser.makeExecutorService();
-            // Prepare apex package info before scanning APKs, these information are needed when
-            // scanning apk in apex.
-            mApexManager.scanApexPackagesTraced(packageParser, executorService);
-
-            mInitAndSystemPackageHelper.scanSystemDirs(mDirsToScanAsSystem, mIsUpgrade,
-                    packageParser, executorService, mPlatformPackage, mIsPreNMR1Upgrade,
-                    systemParseFlags, systemScanFlags);
-            // Parse overlay configuration files to set default enable state, mutability, and
-            // priority of system overlays.
-            mOverlayConfig = OverlayConfig.initializeSystemInstance(
-                    consumer -> mPmInternal.forEachPackage(
-                            pkg -> consumer.accept(pkg, pkg.isSystem())));
             final int[] userIds = mUserManager.getUserIds();
-            mInitAndSystemPackageHelper.cleanupSystemPackagesAndInstallStubs(mDirsToScanAsSystem,
-                    mIsUpgrade, packageParser, executorService, mOnlyCore, packageSettings,
-                    startTime, mAppInstallDir, mPlatformPackage, mIsPreNMR1Upgrade,
-                    scanFlags, systemParseFlags, systemScanFlags, userIds);
-            packageParser.close();
+            mOverlayConfig = mInitAndSystemPackageHelper.setUpSystemPackages(packageSettings,
+                    userIds, startTime);
 
             // Resolve the storage manager.
             mStorageManagerPackage = getStorageManagerPackageName();
@@ -2073,7 +1959,7 @@
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                     SystemClock.uptimeMillis());
             Slog.i(TAG, "Time to scan packages: "
-                    + ((SystemClock.uptimeMillis()-startTime)/1000f)
+                    + ((SystemClock.uptimeMillis() - startTime) / 1000f)
                     + " seconds");
 
             // If the build fingerprint has changed since the last time we booted,
@@ -2276,80 +2162,6 @@
         setUpInstantAppInstallerActivityLP(getInstantAppInstallerLPr());
     }
 
-    private @Nullable File preparePackageParserCache(boolean forEngBuild) {
-        if (!FORCE_PACKAGE_PARSED_CACHE_ENABLED) {
-            if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) {
-                return null;
-            }
-
-            // Disable package parsing on eng builds to allow for faster incremental development.
-            if (forEngBuild) {
-                return null;
-            }
-
-            if (SystemProperties.getBoolean("pm.boot.disable_package_cache", false)) {
-                Slog.i(TAG, "Disabling package parser cache due to system property.");
-                return null;
-            }
-        }
-
-        // The base directory for the package parser cache lives under /data/system/.
-        final File cacheBaseDir = Environment.getPackageCacheDirectory();
-        if (!FileUtils.createDir(cacheBaseDir)) {
-            return null;
-        }
-
-        // There are several items that need to be combined together to safely
-        // identify cached items. In particular, changing the value of certain
-        // feature flags should cause us to invalidate any caches.
-        final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug"
-                : SystemProperties.digestOf("ro.build.fingerprint");
-
-        // Reconcile cache directories, keeping only what we'd actually use.
-        for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) {
-            if (Objects.equals(cacheName, cacheDir.getName())) {
-                Slog.d(TAG, "Keeping known cache " + cacheDir.getName());
-            } else {
-                Slog.d(TAG, "Destroying unknown cache " + cacheDir.getName());
-                FileUtils.deleteContentsAndDir(cacheDir);
-            }
-        }
-
-        // Return the versioned package cache directory.
-        File cacheDir = FileUtils.createDir(cacheBaseDir, cacheName);
-
-        if (cacheDir == null) {
-            // Something went wrong. Attempt to delete everything and return.
-            Slog.wtf(TAG, "Cache directory cannot be created - wiping base dir " + cacheBaseDir);
-            FileUtils.deleteContentsAndDir(cacheBaseDir);
-            return null;
-        }
-
-        // The following is a workaround to aid development on non-numbered userdebug
-        // builds or cases where "adb sync" is used on userdebug builds. If we detect that
-        // the system partition is newer.
-        //
-        // NOTE: When no BUILD_NUMBER is set by the build system, it defaults to a build
-        // that starts with "eng." to signify that this is an engineering build and not
-        // destined for release.
-        if (mIsUserDebugBuild && mIncrementalVersion.startsWith("eng.")) {
-            Slog.w(TAG, "Wiping cache directory because the system partition changed.");
-
-            // Heuristic: If the /system directory has been modified recently due to an "adb sync"
-            // or a regular make, then blow away the cache. Note that mtimes are *NOT* reliable
-            // in general and should not be used for production changes. In this specific case,
-            // we know that they will work.
-            File frameworkDir =
-                    new File(Environment.getRootDirectory(), "framework");
-            if (cacheDir.lastModified() < frameworkDir.lastModified()) {
-                FileUtils.deleteContents(cacheBaseDir);
-                cacheDir = FileUtils.createDir(cacheBaseDir, cacheName);
-            }
-        }
-
-        return cacheDir;
-    }
-
     @Override
     public boolean isFirstBoot() {
         // allow instant applications
@@ -2968,7 +2780,6 @@
                 filterCallingUid, userId);
     }
 
-
     @Override
     public void deletePreloadsFileCache() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CLEAR_APP_CACHE,
@@ -4005,7 +3816,8 @@
     public List<String> getAllPackages() {
         // Allow iorapd to call this method.
         if (Binder.getCallingUid() != Process.IORAPD_UID) {
-            enforceSystemOrRootOrShell("getAllPackages is limited to privileged callers");
+            PackageManagerServiceUtils.enforceSystemOrRootOrShell(
+                    "getAllPackages is limited to privileged callers");
         }
         final int callingUid = Binder.getCallingUid();
         final int callingUserId = UserHandle.getUserId(callingUid);
@@ -4972,34 +4784,6 @@
     }
 
     /**
-     * Enforces that only the system UID or root's UID can call a method exposed
-     * via Binder.
-     *
-     * @param message used as message if SecurityException is thrown
-     * @throws SecurityException if the caller is not system or root
-     */
-    private static void enforceSystemOrRoot(String message) {
-        final int uid = Binder.getCallingUid();
-        if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) {
-            throw new SecurityException(message);
-        }
-    }
-
-    /**
-     * Enforces that only the system UID or root's UID or shell's UID can call
-     * a method exposed via Binder.
-     *
-     * @param message used as message if SecurityException is thrown
-     * @throws SecurityException if the caller is not system or shell
-     */
-    private static void enforceSystemOrRootOrShell(String message) {
-        final int uid = Binder.getCallingUid();
-        if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID && uid != Process.SHELL_UID) {
-            throw new SecurityException(message);
-        }
-    }
-
-    /**
      * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
      * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userId} is not for the caller.
      *
@@ -5034,7 +4818,7 @@
 
     @Override
     public void performFstrimIfNeeded() {
-        enforceSystemOrRoot("Only the system can request fstrim");
+        PackageManagerServiceUtils.enforceSystemOrRoot("Only the system can request fstrim");
 
         // Before everything else, see whether we need to fstrim.
         try {
@@ -5056,7 +4840,7 @@
                 if (doTrim) {
                     final boolean dexOptDialogShown;
                     synchronized (mLock) {
-                        dexOptDialogShown = mDexOptDialogShown;
+                        dexOptDialogShown = mDexOptHelper.isDexOptDialogShown();
                     }
                     if (!isFirstBoot() && dexOptDialogShown) {
                         try {
@@ -5078,208 +4862,7 @@
 
     @Override
     public void updatePackagesIfNeeded() {
-        enforceSystemOrRoot("Only the system can request package update");
-
-        // We need to re-extract after an OTA.
-        boolean causeUpgrade = isDeviceUpgrading();
-
-        // 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;
-
-        if (!causeUpgrade && !causeFirstBoot) {
-            return;
-        }
-
-        List<PackageSetting> pkgSettings;
-        synchronized (mLock) {
-            pkgSettings = PackageManagerServiceUtils.getPackagesForDexopt(
-                    mSettings.getPackagesLocked().values(), this);
-        }
-
-        List<AndroidPackage> pkgs = new ArrayList<>(pkgSettings.size());
-        for (int index = 0; index < pkgSettings.size(); index++) {
-            pkgs.add(pkgSettings.get(index).getPkg());
-        }
-
-        final long startTime = System.nanoTime();
-        final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
-                    causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT_AFTER_OTA,
-                    false /* bootComplete */);
-
-        final int elapsedTimeSeconds =
-                (int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime);
-
-        MetricsLogger.histogram(mContext, "opt_dialog_num_dexopted", stats[0]);
-        MetricsLogger.histogram(mContext, "opt_dialog_num_skipped", stats[1]);
-        MetricsLogger.histogram(mContext, "opt_dialog_num_failed", stats[2]);
-        MetricsLogger.histogram(mContext, "opt_dialog_num_total", getOptimizablePackages().size());
-        MetricsLogger.histogram(mContext, "opt_dialog_time_s", elapsedTimeSeconds);
-    }
-
-    /*
-     * Return the prebuilt profile path given a package base code path.
-     */
-    private static String getPrebuildProfilePath(AndroidPackage pkg) {
-        return pkg.getBaseApkPath() + ".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<AndroidPackage> 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 (AndroidPackage pkg : pkgs) {
-            numberOfPackagesVisited++;
-
-            boolean useProfileForDexopt = false;
-
-            if ((isFirstBoot() || isDeviceUpgrading()) && pkg.isSystem()) {
-                // 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 (profileFile.exists()) {
-                    try {
-                        // We could also do this lazily before calling dexopt in
-                        // PackageDexOptimizer to prevent this happening on first boot. The issue
-                        // is that we don't have a good way to say "do this only once".
-                        if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
-                                pkg.getUid(), pkg.getPackageName(),
-                                ArtManager.getProfileName(null))) {
-                            Log.e(TAG, "Installer failed to copy system profile!");
-                        } else {
-                            // Disabled as this causes speed-profile compilation during first boot
-                            // even if things are already compiled.
-                            // useProfileForDexopt = true;
-                        }
-                    } catch (Exception e) {
-                        Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ",
-                                e);
-                    }
-                } else {
-                    PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(
-                            pkg.getPackageName());
-                    // Handle compressed APKs in this path. Only do this for stubs with profiles to
-                    // minimize the number off apps being speed-profile compiled during first boot.
-                    // The other paths will not change the filter.
-                    if (disabledPs != null && disabledPs.getPkg().isStub()) {
-                        // The package is the stub one, remove the stub suffix to get the normal
-                        // package and APK names.
-                        String systemProfilePath = getPrebuildProfilePath(disabledPs.getPkg())
-                                .replace(STUB_SUFFIX, "");
-                        profileFile = new File(systemProfilePath);
-                        // If we have a profile for a compressed APK, copy it to the reference
-                        // location.
-                        // Note that copying the profile here will cause it to override the
-                        // reference profile every OTA even though the existing reference profile
-                        // may have more data. We can't copy during decompression since the
-                        // directories are not set up at that point.
-                        if (profileFile.exists()) {
-                            try {
-                                // We could also do this lazily before calling dexopt in
-                                // PackageDexOptimizer to prevent this happening on first boot. The
-                                // issue is that we don't have a good way to say "do this only
-                                // once".
-                                if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
-                                        pkg.getUid(), pkg.getPackageName(),
-                                        ArtManager.getProfileName(null))) {
-                                    Log.e(TAG, "Failed to copy system profile for stub package!");
-                                } else {
-                                    useProfileForDexopt = true;
-                                }
-                            } catch (Exception e) {
-                                Log.e(TAG, "Failed to copy profile " +
-                                        profileFile.getAbsolutePath() + " ", e);
-                            }
-                        }
-                    }
-                }
-            }
-
-            if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
-                if (DEBUG_DEXOPT) {
-                    Log.i(TAG, "Skipping update of non-optimizable app " + pkg.getPackageName());
-                }
-                numberOfPackagesSkipped++;
-                continue;
-            }
-
-            if (DEBUG_DEXOPT) {
-                Log.i(TAG, "Updating app " + numberOfPackagesVisited + " of " +
-                        numberOfPackagesToDexopt + ": " + pkg.getPackageName());
-            }
-
-            if (showDialog) {
-                try {
-                    ActivityManager.getService().showBootMessage(
-                            mContext.getResources().getString(R.string.android_upgrading_apk,
-                                    numberOfPackagesVisited, numberOfPackagesToDexopt), true);
-                } catch (RemoteException e) {
-                }
-                synchronized (mLock) {
-                    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;
-            }
-
-            if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
-                mArtManagerService.compileLayouts(pkg);
-            }
-
-            // 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;
-            }
-            int primaryDexOptStaus = performDexOptTraced(new DexoptOptions(
-                    pkg.getPackageName(),
-                    pkgCompilationReason,
-                    dexoptFlags));
-
-            switch (primaryDexOptStaus) {
-                case PackageDexOptimizer.DEX_OPT_PERFORMED:
-                    numberOfPackagesOptimized++;
-                    break;
-                case PackageDexOptimizer.DEX_OPT_SKIPPED:
-                    numberOfPackagesSkipped++;
-                    break;
-                case PackageDexOptimizer.DEX_OPT_CANCELLED:
-                    // ignore this case
-                    break;
-                case PackageDexOptimizer.DEX_OPT_FAILED:
-                    numberOfPackagesFailed++;
-                    break;
-                default:
-                    Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStaus);
-                    break;
-            }
-        }
-
-        return new int[] { numberOfPackagesOptimized, numberOfPackagesSkipped,
-                numberOfPackagesFailed };
+        mDexOptHelper.performPackageDexOptUpgradeIfNeeded();
     }
 
     @Override
@@ -5373,13 +4956,8 @@
     public boolean performDexOptMode(String packageName,
             boolean checkProfiles, String targetCompilerFilter, boolean force,
             boolean bootComplete, String splitName) {
-        enforceSystemOrRootOrShell("performDexOptMode");
-
-        int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0) |
-                (force ? DexoptOptions.DEXOPT_FORCE : 0) |
-                (bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0);
-        return performDexOpt(new DexoptOptions(packageName, REASON_CMDLINE,
-                targetCompilerFilter, splitName, flags));
+        return mDexOptHelper.performDexOptMode(packageName, checkProfiles, targetCompilerFilter,
+                force, bootComplete, splitName);
     }
 
     /**
@@ -5392,147 +4970,7 @@
     @Override
     public boolean performDexOptSecondary(String packageName, String compilerFilter,
             boolean force) {
-        int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX |
-                DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES |
-                DexoptOptions.DEXOPT_BOOT_COMPLETE |
-                (force ? DexoptOptions.DEXOPT_FORCE : 0);
-        return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags));
-    }
-
-    /*package*/ boolean performDexOpt(DexoptOptions options) {
-        if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
-            return false;
-        } else if (isInstantApp(options.getPackageName(), UserHandle.getCallingUserId())) {
-            return false;
-        }
-
-        if (options.isDexoptOnlySecondaryDex()) {
-            return mDexManager.dexoptSecondaryDex(options);
-        } else {
-            int dexoptStatus = performDexOptWithStatus(options);
-            return dexoptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
-        }
-    }
-
-    /*package*/ void controlDexOptBlocking(boolean block) {
-        mPackageDexOptimizer.controlDexOptBlocking(block);
-    }
-
-    /**
-     * Perform dexopt on the given package and return one of following result:
-     *  {@link PackageDexOptimizer#DEX_OPT_SKIPPED}
-     *  {@link PackageDexOptimizer#DEX_OPT_PERFORMED}
-     *  {@link PackageDexOptimizer#DEX_OPT_CANCELLED}
-     *  {@link PackageDexOptimizer#DEX_OPT_FAILED}
-     */
-    @PackageDexOptimizer.DexOptResult
-    /* package */ int performDexOptWithStatus(DexoptOptions options) {
-        return performDexOptTraced(options);
-    }
-
-    private int performDexOptTraced(DexoptOptions options) {
-        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
-        try {
-            return performDexOptInternal(options);
-        } finally {
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-        }
-    }
-
-    // Run dexopt on a given package. Returns true if dexopt did not fail, i.e.
-    // if the package can now be considered up to date for the given filter.
-    private int performDexOptInternal(DexoptOptions options) {
-        AndroidPackage p;
-        PackageSetting pkgSetting;
-        synchronized (mLock) {
-            p = mPackages.get(options.getPackageName());
-            pkgSetting = mSettings.getPackageLPr(options.getPackageName());
-            if (p == null || pkgSetting == null) {
-                // Package could not be found. Report failure.
-                return PackageDexOptimizer.DEX_OPT_FAILED;
-            }
-            mPackageUsage.maybeWriteAsync(mSettings.getPackagesLocked());
-            mCompilerStats.maybeWriteAsync();
-        }
-        final long callingId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mInstallLock) {
-                return performDexOptInternalWithDependenciesLI(p, pkgSetting, options);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(callingId);
-        }
-    }
-
-    public ArraySet<String> getOptimizablePackages() {
-        ArraySet<String> pkgs = new ArraySet<>();
-        synchronized (mLock) {
-            for (AndroidPackage p : mPackages.values()) {
-                if (PackageDexOptimizer.canOptimizePackage(p)) {
-                    pkgs.add(p.getPackageName());
-                }
-            }
-        }
-        if (AppHibernationService.isAppHibernationEnabled()) {
-            AppHibernationManagerInternal appHibernationManager =
-                    mInjector.getLocalService(AppHibernationManagerInternal.class);
-            pkgs.removeIf(pkgName -> appHibernationManager.isHibernatingGlobally(pkgName));
-        }
-        return pkgs;
-    }
-
-    private int performDexOptInternalWithDependenciesLI(AndroidPackage p,
-            @NonNull PackageSetting pkgSetting, DexoptOptions options) {
-        // System server gets a special path.
-        if (PLATFORM_PACKAGE_NAME.equals(p.getPackageName())) {
-            return mDexManager.dexoptSystemServer(options);
-        }
-
-        // Select the dex optimizer based on the force parameter.
-        // Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
-        //       allocate an object here.
-        PackageDexOptimizer pdo = options.isForce()
-                ? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer)
-                : mPackageDexOptimizer;
-
-        // Dexopt all dependencies first. Note: we ignore the return value and march on
-        // on errors.
-        // Note that we are going to call performDexOpt on those libraries as many times as
-        // they are referenced in packages. When we do a batch of performDexOpt (for example
-        // at boot, or background job), the passed 'targetCompilerFilter' stays the same,
-        // and the first package that uses the library will dexopt it. The
-        // others will see that the compiled code for the library is up to date.
-        Collection<SharedLibraryInfo> deps = findSharedLibraries(pkgSetting);
-        final String[] instructionSets = getAppDexInstructionSets(
-                AndroidPackageUtils.getPrimaryCpuAbi(p, pkgSetting),
-                AndroidPackageUtils.getSecondaryCpuAbi(p, pkgSetting));
-        if (!deps.isEmpty()) {
-            DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
-                    options.getCompilationReason(), options.getCompilerFilter(),
-                    options.getSplitName(),
-                    options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
-            for (SharedLibraryInfo info : deps) {
-                AndroidPackage depPackage = null;
-                PackageSetting depPackageSetting = null;
-                synchronized (mLock) {
-                    depPackage = mPackages.get(info.getPackageName());
-                    depPackageSetting = mSettings.getPackageLPr(info.getPackageName());
-                }
-                if (depPackage != null && depPackageSetting != null) {
-                    // TODO: Analyze and investigate if we (should) profile libraries.
-                    pdo.performDexOpt(depPackage, depPackageSetting, instructionSets,
-                            getOrCreateCompilerPackageStats(depPackage),
-                            mDexManager.getPackageUseInfoOrDefault(depPackage.getPackageName()),
-                            libraryOptions);
-                } else {
-                    // TODO(ngeoffray): Support dexopting system shared libraries.
-                }
-            }
-        }
-
-        return pdo.performDexOpt(p, pkgSetting, instructionSets,
-                getOrCreateCompilerPackageStats(p),
-                mDexManager.getPackageUseInfoOrDefault(p.getPackageName()), options);
+        return mDexOptHelper.performDexOptSecondary(packageName, compilerFilter, force);
     }
 
     /**
@@ -5554,35 +4992,8 @@
         return mDexManager;
     }
 
-    private static List<SharedLibraryInfo> findSharedLibraries(PackageSetting pkgSetting) {
-        if (!pkgSetting.getPkgState().getUsesLibraryInfos().isEmpty()) {
-            ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
-            Set<String> collectedNames = new HashSet<>();
-            for (SharedLibraryInfo info : pkgSetting.getPkgState().getUsesLibraryInfos()) {
-                findSharedLibrariesRecursive(info, retValue, collectedNames);
-            }
-            return retValue;
-        } else {
-            return Collections.emptyList();
-        }
-    }
-
-    private static void findSharedLibrariesRecursive(SharedLibraryInfo info,
-            ArrayList<SharedLibraryInfo> collected, Set<String> collectedNames) {
-        if (!collectedNames.contains(info.getName())) {
-            collectedNames.add(info.getName());
-            collected.add(info);
-
-            if (info.getDependencies() != null) {
-                for (SharedLibraryInfo dep : info.getDependencies()) {
-                    findSharedLibrariesRecursive(dep, collected, collectedNames);
-                }
-            }
-        }
-    }
-
     List<PackageSetting> findSharedNonSystemLibraries(PackageSetting pkgSetting) {
-        List<SharedLibraryInfo> deps = findSharedLibraries(pkgSetting);
+        List<SharedLibraryInfo> deps = SharedLibraryHelper.findSharedLibraries(pkgSetting);
         if (!deps.isEmpty()) {
             List<PackageSetting> retValue = new ArrayList<>();
             synchronized (mLock) {
@@ -5686,33 +5097,7 @@
 
     @Override
     public void forceDexOpt(String packageName) {
-        enforceSystemOrRoot("forceDexOpt");
-
-        AndroidPackage pkg;
-        PackageSetting pkgSetting;
-        synchronized (mLock) {
-            pkg = mPackages.get(packageName);
-            pkgSetting = mSettings.getPackageLPr(packageName);
-            if (pkg == null || pkgSetting == null) {
-                throw new IllegalArgumentException("Unknown package: " + packageName);
-            }
-        }
-
-        synchronized (mInstallLock) {
-            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
-
-            // Whoever is calling forceDexOpt wants a compiled package.
-            // Don't use profiles since that may cause compilation to be skipped.
-            final int res = performDexOptInternalWithDependenciesLI(pkg, pkgSetting,
-                    new DexoptOptions(packageName,
-                            getDefaultCompilerFilter(),
-                            DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE));
-
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-            if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
-                throw new IllegalStateException("Failed to dexopt: " + res);
-            }
-        }
+        mDexOptHelper.forceDexOpt(packageName);
     }
 
     int[] resolveUserIds(int userId) {
@@ -6971,7 +6356,8 @@
 
     @Override
     public void finishPackageInstall(int token, boolean didLaunch) {
-        enforceSystemOrRoot("Only the system is allowed to finish installs");
+        PackageManagerServiceUtils.enforceSystemOrRoot(
+                "Only the system is allowed to finish installs");
 
         if (DEBUG_INSTALL) {
             Slog.v(TAG, "BM finishing package install for " + token);
@@ -7367,18 +6753,6 @@
         return mDevicePolicyManager;
     }
 
-    private static @Nullable ScanPartition resolveApexToScanPartition(
-            ApexManager.ActiveApexInfo apexInfo) {
-        for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
-            ScanPartition sp = SYSTEM_PARTITIONS.get(i);
-            if (apexInfo.preInstalledApexPath.getAbsolutePath().startsWith(
-                    sp.getFolder().getAbsolutePath())) {
-                return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX);
-            }
-        }
-        return null;
-    }
-
     @Override
     public boolean setBlockUninstallForUser(String packageName, boolean blockUninstall,
             int userId) {
@@ -7414,7 +6788,8 @@
 
     @Override
     public boolean setRequiredForSystemUser(String packageName, boolean systemUserApp) {
-        enforceSystemOrRoot("setRequiredForSystemUser can only be run by the system or root");
+        PackageManagerServiceUtils.enforceSystemOrRoot(
+                "setRequiredForSystemUser can only be run by the system or root");
         synchronized (mLock) {
             PackageSetting ps = mSettings.getPackageLPr(packageName);
             if (ps == null) {
@@ -7433,7 +6808,8 @@
 
     @Override
     public void clearApplicationProfileData(String packageName) {
-        enforceSystemOrRoot("Only the system can clear all profile data");
+        PackageManagerServiceUtils.enforceSystemOrRoot(
+                "Only the system can clear all profile data");
 
         final AndroidPackage pkg;
         synchronized (mLock) {
@@ -8431,8 +7807,7 @@
             if (isSystemStub
                     && (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                     || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
-                if (!mInitAndSystemPackageHelper.enableCompressedPackage(deletedPkg, pkgSetting,
-                        mDefParseFlags, mDirsToScanAsSystem)) {
+                if (!mInitAndSystemPackageHelper.enableCompressedPackage(deletedPkg, pkgSetting)) {
                     Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable "
                             + "commpressed package " + setting.getPackageName());
                     updateAllowed[i] = false;
@@ -8888,7 +8263,8 @@
 
     @Override
     public void enterSafeMode() {
-        enforceSystemOrRoot("Only the system can request entering safe mode");
+        PackageManagerServiceUtils.enforceSystemOrRoot(
+                "Only the system can request entering safe mode");
 
         if (!mSystemReady) {
             mSafeMode = true;
@@ -8897,7 +8273,8 @@
 
     @Override
     public void systemReady() {
-        enforceSystemOrRoot("Only the system can claim the system is ready");
+        PackageManagerServiceUtils.enforceSystemOrRoot(
+                "Only the system can claim the system is ready");
 
         final ContentResolver resolver = mContext.getContentResolver();
         if (mReleaseOnSystemReady != null) {
@@ -9056,6 +8433,9 @@
         mBackgroundDexOptService.systemReady();
     }
 
+    /**
+     * Used by SystemServer
+     */
     public void waitForAppDataPrepared() {
         if (mPrepareAppDataFuture == null) {
             return;
@@ -9487,200 +8867,6 @@
         }
     }
 
-    private final class PackageChangeObserverDeathRecipient implements IBinder.DeathRecipient {
-        private final IPackageChangeObserver mObserver;
-
-        PackageChangeObserverDeathRecipient(IPackageChangeObserver observer) {
-            mObserver = observer;
-        }
-
-        @Override
-        public void binderDied() {
-            synchronized (mPackageChangeObservers) {
-                mPackageChangeObservers.remove(mObserver);
-                Log.d(TAG, "Size of mPackageChangeObservers after removing dead observer is "
-                    + mPackageChangeObservers.size());
-            }
-        }
-    }
-
-    private class PackageManagerNative extends IPackageManagerNative.Stub {
-        @Override
-        public void registerPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
-          synchronized (mPackageChangeObservers) {
-            try {
-                observer.asBinder().linkToDeath(
-                    new PackageChangeObserverDeathRecipient(observer), 0);
-            } catch (RemoteException e) {
-              Log.e(TAG, e.getMessage());
-            }
-            mPackageChangeObservers.add(observer);
-            Log.d(TAG, "Size of mPackageChangeObservers after registry is "
-                + mPackageChangeObservers.size());
-          }
-        }
-
-        @Override
-        public void unregisterPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
-          synchronized (mPackageChangeObservers) {
-            mPackageChangeObservers.remove(observer);
-            Log.d(TAG, "Size of mPackageChangeObservers after unregistry is "
-                + mPackageChangeObservers.size());
-          }
-        }
-
-        @Override
-        public String[] getAllPackages() {
-            return PackageManagerService.this.getAllPackages().toArray(new String[0]);
-        }
-
-        @Override
-        public String[] getNamesForUids(int[] uids) throws RemoteException {
-            String[] names = null;
-            String[] results = null;
-            try {
-                if (uids == null || uids.length == 0) {
-                    return null;
-                }
-                names = PackageManagerService.this.getNamesForUids(uids);
-                results = (names != null) ? names : new String[uids.length];
-                // massage results so they can be parsed by the native binder
-                for (int i = results.length - 1; i >= 0; --i) {
-                    if (results[i] == null) {
-                        results[i] = "";
-                    }
-                }
-                return results;
-            } catch (Throwable t) {
-                // STOPSHIP(186558987): revert addition of try/catch/log
-                Slog.e(TAG, "uids: " + Arrays.toString(uids));
-                Slog.e(TAG, "names: " + Arrays.toString(names));
-                Slog.e(TAG, "results: " + Arrays.toString(results));
-                Slog.e(TAG, "throwing exception", t);
-                throw t;
-            }
-        }
-
-        // NB: this differentiates between preloads and sideloads
-        @Override
-        public String getInstallerForPackage(String packageName) throws RemoteException {
-            final String installerName = getInstallerPackageName(packageName);
-            if (!TextUtils.isEmpty(installerName)) {
-                return installerName;
-            }
-            // differentiate between preload and sideload
-            int callingUser = UserHandle.getUserId(Binder.getCallingUid());
-            ApplicationInfo appInfo = getApplicationInfo(packageName,
-                                    /*flags*/ 0,
-                                    /*userId*/ callingUser);
-            if (appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                return "preload";
-            }
-            return "";
-        }
-
-        @Override
-        public long getVersionCodeForPackage(String packageName) throws RemoteException {
-            try {
-                int callingUser = UserHandle.getUserId(Binder.getCallingUid());
-                PackageInfo pInfo = getPackageInfo(packageName, 0, callingUser);
-                if (pInfo != null) {
-                    return pInfo.getLongVersionCode();
-                }
-            } catch (Exception e) {
-            }
-            return 0;
-        }
-
-        @Override
-        public int getTargetSdkVersionForPackage(String packageName) throws RemoteException {
-            int targetSdk = getTargetSdkVersion(packageName);
-            if (targetSdk != -1) {
-                return targetSdk;
-            }
-
-            throw new RemoteException("Couldn't get targetSdkVersion for package " + packageName);
-        }
-
-        @Override
-        public boolean isPackageDebuggable(String packageName) throws RemoteException {
-            int callingUser = UserHandle.getCallingUserId();
-            ApplicationInfo appInfo = getApplicationInfo(packageName, 0, callingUser);
-            if (appInfo != null) {
-                return (0 != (appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE));
-            }
-
-            throw new RemoteException("Couldn't get debug flag for package " + packageName);
-        }
-
-        @Override
-        public boolean[] isAudioPlaybackCaptureAllowed(String[] packageNames)
-                throws RemoteException {
-            int callingUser = UserHandle.getUserId(Binder.getCallingUid());
-            boolean[] results = new boolean[packageNames.length];
-            for (int i = results.length - 1; i >= 0; --i) {
-                ApplicationInfo appInfo = getApplicationInfo(packageNames[i], 0, callingUser);
-                results[i] = appInfo != null && appInfo.isAudioPlaybackCaptureAllowed();
-            }
-            return results;
-        }
-
-        @Override
-        public int getLocationFlags(String packageName) throws RemoteException {
-            int callingUser = UserHandle.getUserId(Binder.getCallingUid());
-            ApplicationInfo appInfo = getApplicationInfo(packageName,
-                    /*flags*/ 0,
-                    /*userId*/ callingUser);
-            if (appInfo == null) {
-                throw new RemoteException(
-                        "Couldn't get ApplicationInfo for package " + packageName);
-            }
-            return ((appInfo.isSystemApp() ? IPackageManagerNative.LOCATION_SYSTEM : 0)
-                    | (appInfo.isVendor() ? IPackageManagerNative.LOCATION_VENDOR : 0)
-                    | (appInfo.isProduct() ? IPackageManagerNative.LOCATION_PRODUCT : 0));
-        }
-
-        @Override
-        public String getModuleMetadataPackageName() throws RemoteException {
-            return PackageManagerService.this.mModuleInfoProvider.getPackageName();
-        }
-
-        @Override
-        public boolean hasSha256SigningCertificate(String packageName, byte[] certificate)
-                throws RemoteException {
-            return PackageManagerService.this.hasSigningCertificate(
-                packageName, certificate, CERT_INPUT_SHA256);
-        }
-
-        @Override
-        public boolean hasSystemFeature(String featureName, int version) {
-            return PackageManagerService.this.hasSystemFeature(featureName, version);
-        }
-
-        @Override
-        public void registerStagedApexObserver(IStagedApexObserver observer) {
-            mInstallerService.getStagingManager().registerStagedApexObserver(observer);
-        }
-
-        @Override
-        public void unregisterStagedApexObserver(IStagedApexObserver observer) {
-            mInstallerService.getStagingManager().unregisterStagedApexObserver(observer);
-        }
-
-        @Override
-        public String[] getStagedApexModuleNames() {
-            return mInstallerService.getStagingManager()
-                    .getStagedApexModuleNames().toArray(new String[0]);
-        }
-
-        @Override
-        @Nullable
-        public StagedApexInfo getStagedApexInfo(String moduleName) {
-            return mInstallerService.getStagingManager().getStagedApexInfo(moduleName);
-        }
-
-    }
-
     private AndroidPackage getPackage(String packageName) {
         return mComputer.getPackage(packageName);
     }
@@ -10174,17 +9360,6 @@
                 SparseArray<String> profileOwnerPackages) {
             mProtectedPackages.setDeviceAndProfileOwnerPackages(
                     deviceOwnerUserId, deviceOwnerPackage, profileOwnerPackages);
-
-            final ArraySet<Integer> usersWithPoOrDo = new ArraySet<>();
-            if (deviceOwnerPackage != null) {
-                usersWithPoOrDo.add(deviceOwnerUserId);
-            }
-            final int sz = profileOwnerPackages.size();
-            for (int i = 0; i < sz; i++) {
-                if (profileOwnerPackages.valueAt(i) != null) {
-                    usersWithPoOrDo.add(profileOwnerPackages.keyAt(i));
-                }
-            }
         }
 
         @Override
@@ -11437,7 +10612,7 @@
      * Temporary method that wraps mSettings.writeLPr() and calls mPermissionManager's
      * writeLegacyPermissionsTEMP() beforehand.
      *
-     * TODO(zhanghai): This should be removed once we finish migration of permission storage.
+     * TODO(b/182523293): This should be removed once we finish migration of permission storage.
      */
     void writeSettingsLPrTEMP() {
         mPermissionManager.writeLegacyPermissionsTEMP(mSettings.mPermissions);
@@ -11678,10 +10853,6 @@
         return mCacheDir;
     }
 
-    List<ScanPartition> getDirsToScanAsSystem() {
-        return mDirsToScanAsSystem;
-    }
-
     PackageProperty getPackageProperty() {
         return mPackageProperty;
     }
@@ -11795,4 +10966,126 @@
     ResolveInfo getInstantAppInstallerInfo() {
         return mInstantAppInstallerInfo;
     }
+
+    PackageUsage getPackageUsage() {
+        return mPackageUsage;
+    }
+
+    String getModuleMetadataPackageName() {
+        return mModuleInfoProvider.getPackageName();
+    }
+
+    File getAppInstallDir() {
+        return mAppInstallDir;
+    }
+
+    boolean isExpectingBetter(String packageName) {
+        return mInitAndSystemPackageHelper.isExpectingBetter(packageName);
+    }
+
+    int getDefParseFlags() {
+        return mDefParseFlags;
+    }
+
+    void setUpCustomResolverActivity(AndroidPackage pkg, PackageSetting pkgSetting) {
+        synchronized (mLock) {
+            mResolverReplaced = true;
+
+            // The instance created in PackageManagerService is special cased to be non-user
+            // specific, so initialize all the needed fields here.
+            ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(pkg, 0,
+                    PackageUserState.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting);
+
+            // Set up information for custom user intent resolution activity.
+            mResolveActivity.applicationInfo = appInfo;
+            mResolveActivity.name = mCustomResolverComponentName.getClassName();
+            mResolveActivity.packageName = pkg.getPackageName();
+            mResolveActivity.processName = pkg.getProcessName();
+            mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+            mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS
+                    | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
+            mResolveActivity.theme = 0;
+            mResolveActivity.exported = true;
+            mResolveActivity.enabled = true;
+            mResolveInfo.activityInfo = mResolveActivity;
+            mResolveInfo.priority = 0;
+            mResolveInfo.preferredOrder = 0;
+            mResolveInfo.match = 0;
+            mResolveComponentName = mCustomResolverComponentName;
+            PackageManagerService.onChanged();
+            Slog.i(TAG, "Replacing default ResolverActivity with custom activity: "
+                    + mResolveComponentName);
+        }
+    }
+
+    void setPlatformPackage(AndroidPackage pkg, PackageSetting pkgSetting) {
+        synchronized (mLock) {
+            // Set up information for our fall-back user intent resolution activity.
+            mPlatformPackage = pkg;
+
+            // The instance stored in PackageManagerService is special cased to be non-user
+            // specific, so initialize all the needed fields here.
+            mAndroidApplication = PackageInfoUtils.generateApplicationInfo(pkg, 0,
+                    PackageUserState.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting);
+
+            if (!mResolverReplaced) {
+                mResolveActivity.applicationInfo = mAndroidApplication;
+                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);
+            }
+            PackageManagerService.onChanged();
+        }
+    }
+
+    ResolveInfo getResolveInfo() {
+        return mResolveInfo;
+    }
+
+    ApplicationInfo getCoreAndroidApplication() {
+        return mAndroidApplication;
+    }
+
+    boolean isSystemReady() {
+        return mSystemReady;
+    }
+
+    AndroidPackage getPlatformPackage() {
+        return mPlatformPackage;
+    }
+
+    boolean isPreNUpgrade() {
+        return mIsPreNUpgrade;
+    }
+
+    boolean isPreNMR1Upgrade() {
+        return mIsPreNMR1Upgrade;
+    }
+
+    InitAndSystemPackageHelper getInitAndSystemPackageHelper() {
+        return mInitAndSystemPackageHelper;
+    }
+
+    boolean isOverlayMutable(String packageName) {
+        return mOverlayConfig.isMutable(packageName);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index c1c32dd..9327c5f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -102,4 +102,12 @@
     public int sdkInt = Build.VERSION.SDK_INT;
     public BackgroundDexOptService backgroundDexOptService;
     public final String incrementalVersion = Build.VERSION.INCREMENTAL;
+    public BroadcastHelper broadcastHelper;
+    public AppDataHelper appDataHelper;
+    public RemovePackageHelper removePackageHelper;
+    public InitAndSystemPackageHelper initAndSystemPackageHelper;
+    public DeletePackageHelper deletePackageHelper;
+    public PreferredActivityHelper preferredActivityHelper;
+    public ResolveIntentHelper resolveIntentHelper;
+    public DexOptHelper dexOptHelper;
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 9f586bc..e124e04 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -23,7 +23,6 @@
 
 import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION;
 import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
-import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
 import static com.android.server.pm.PackageManagerService.DEBUG_INTENT_MATCHING;
 import static com.android.server.pm.PackageManagerService.DEBUG_PREFERRED;
 import static com.android.server.pm.PackageManagerService.RANDOM_DIR_PREFIX;
@@ -33,7 +32,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
-import android.app.AppGlobals;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
 import android.content.Context;
@@ -45,7 +43,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.content.pm.SharedLibraryInfo;
 import android.content.pm.Signature;
 import android.content.pm.SigningDetails;
 import android.content.pm.parsing.ApkLiteParseUtils;
@@ -53,14 +50,13 @@
 import android.content.pm.parsing.component.ParsedMainComponent;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Debug;
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Process;
-import android.os.RemoteException;
 import android.os.SystemProperties;
-import android.os.UserHandle;
 import android.os.incremental.IncrementalManager;
 import android.os.incremental.V4Signature;
 import android.os.incremental.V4Signature.HashingInfo;
@@ -86,11 +82,9 @@
 import com.android.server.EventLogTags;
 import com.android.server.IntentResolver;
 import com.android.server.compat.PlatformCompat;
-import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.PackageDexUsage;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
-import com.android.server.utils.WatchedLongSparseArray;
 
 import dalvik.system.VMRuntime;
 
@@ -112,14 +106,10 @@
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.Date;
-import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
+import java.util.Objects;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.zip.GZIPInputStream;
@@ -130,7 +120,6 @@
  * {@hide}
  */
 public class PackageManagerServiceUtils {
-    private static final long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
     private static final long MAX_CRITICAL_INFO_DUMP_SIZE = 3 * 1000 * 1000; // 3MB
 
     public final static Predicate<PackageSetting> REMOVE_IF_NULL_PKG =
@@ -150,151 +139,18 @@
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S)
     private static final long ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS = 161252188;
 
-    private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
-        List<ResolveInfo> ris = null;
-        try {
-            ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId)
-                    .getList();
-        } catch (RemoteException e) {
-        }
-        ArraySet<String> pkgNames = new ArraySet<String>();
-        if (ris != null) {
-            for (ResolveInfo ri : ris) {
-                pkgNames.add(ri.activityInfo.packageName);
-            }
-        }
-        return pkgNames;
-    }
+    /**
+     * The initial enabled state of the cache before other checks are done.
+     */
+    private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = true;
 
-    // Sort a list of apps by their last usage, most recently used apps first. The order of
-    // packages without usage data is undefined (but they will be sorted after the packages
-    // that do have usage data).
-    public static void sortPackagesByUsageDate(List<PackageSetting> pkgSettings,
-            PackageManagerService packageManagerService) {
-        if (!packageManagerService.isHistoricalPackageUsageAvailable()) {
-            return;
-        }
-
-        Collections.sort(pkgSettings, (pkgSetting1, pkgSetting2) ->
-                Long.compare(
-                        pkgSetting2.getPkgState().getLatestForegroundPackageUseTimeInMills(),
-                        pkgSetting1.getPkgState().getLatestForegroundPackageUseTimeInMills())
-        );
-    }
-
-    // Apply the given {@code filter} to all packages in {@code packages}. If tested positive, the
-    // package will be removed from {@code packages} and added to {@code result} with its
-    // dependencies. If usage data is available, the positive packages will be sorted by usage
-    // data (with {@code sortTemp} as temporary storage).
-    private static void applyPackageFilter(
-            Predicate<PackageSetting> filter,
-            Collection<PackageSetting> result,
-            Collection<PackageSetting> packages,
-            @NonNull List<PackageSetting> sortTemp,
-            PackageManagerService packageManagerService) {
-        for (PackageSetting pkgSetting : packages) {
-            if (filter.test(pkgSetting)) {
-                sortTemp.add(pkgSetting);
-            }
-        }
-
-        sortPackagesByUsageDate(sortTemp, packageManagerService);
-        packages.removeAll(sortTemp);
-
-        for (PackageSetting pkgSetting : sortTemp) {
-            result.add(pkgSetting);
-
-            List<PackageSetting> deps =
-                    packageManagerService.findSharedNonSystemLibraries(pkgSetting);
-            if (!deps.isEmpty()) {
-                deps.removeAll(result);
-                result.addAll(deps);
-                packages.removeAll(deps);
-            }
-        }
-
-        sortTemp.clear();
-    }
-
-    // Sort apps by importance for dexopt ordering. Important apps are given
-    // more priority in case the device runs out of space.
-    public static List<PackageSetting> getPackagesForDexopt(
-            Collection<PackageSetting> packages,
-            PackageManagerService packageManagerService) {
-        return getPackagesForDexopt(packages, packageManagerService, DEBUG_DEXOPT);
-    }
-
-    public static List<PackageSetting> getPackagesForDexopt(
-            Collection<PackageSetting> pkgSettings,
-            PackageManagerService packageManagerService,
-            boolean debug) {
-        List<PackageSetting> result = new LinkedList<>();
-        ArrayList<PackageSetting> remainingPkgSettings = new ArrayList<>(pkgSettings);
-
-        // First, remove all settings without available packages
-        remainingPkgSettings.removeIf(REMOVE_IF_NULL_PKG);
-
-        ArrayList<PackageSetting> sortTemp = new ArrayList<>(remainingPkgSettings.size());
-
-        // Give priority to core apps.
-        applyPackageFilter(pkgSetting -> pkgSetting.getPkg().isCoreApp(), result,
-                remainingPkgSettings, sortTemp, packageManagerService);
-
-        // Give priority to system apps that listen for pre boot complete.
-        Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
-        final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
-        applyPackageFilter(pkgSetting -> pkgNames.contains(pkgSetting.getPackageName()), result,
-                remainingPkgSettings, sortTemp, packageManagerService);
-
-        // Give priority to apps used by other apps.
-        DexManager dexManager = packageManagerService.getDexManager();
-        applyPackageFilter(pkgSetting ->
-                dexManager.getPackageUseInfoOrDefault(pkgSetting.getPackageName())
-                        .isAnyCodePathUsedByOtherApps(),
-                result, remainingPkgSettings, sortTemp, packageManagerService);
-
-        // Filter out packages that aren't recently used, add all remaining apps.
-        // TODO: add a property to control this?
-        Predicate<PackageSetting> remainingPredicate;
-        if (!remainingPkgSettings.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) {
-            if (debug) {
-                Log.i(TAG, "Looking at historical package use");
-            }
-            // Get the package that was used last.
-            PackageSetting lastUsed = Collections.max(remainingPkgSettings,
-                    (pkgSetting1, pkgSetting2) -> Long.compare(
-                            pkgSetting1.getPkgState().getLatestForegroundPackageUseTimeInMills(),
-                            pkgSetting2.getPkgState().getLatestForegroundPackageUseTimeInMills()));
-            if (debug) {
-                Log.i(TAG, "Taking package " + lastUsed.getPackageName()
-                        + " as reference in time use");
-            }
-            long estimatedPreviousSystemUseTime = lastUsed.getPkgState()
-                    .getLatestForegroundPackageUseTimeInMills();
-            // Be defensive if for some reason package usage has bogus data.
-            if (estimatedPreviousSystemUseTime != 0) {
-                final long cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS;
-                remainingPredicate = pkgSetting -> pkgSetting.getPkgState()
-                        .getLatestForegroundPackageUseTimeInMills() >= cutoffTime;
-            } else {
-                // No meaningful historical info. Take all.
-                remainingPredicate = pkgSetting -> true;
-            }
-            sortPackagesByUsageDate(remainingPkgSettings, packageManagerService);
-        } else {
-            // No historical info. Take all.
-            remainingPredicate = pkgSetting -> true;
-        }
-        applyPackageFilter(remainingPredicate, result, remainingPkgSettings, sortTemp,
-                packageManagerService);
-
-        if (debug) {
-            Log.i(TAG, "Packages to be dexopted: " + packagesToString(result));
-            Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgSettings));
-        }
-
-        return result;
-    }
+    /**
+     * Whether to skip all other checks and force the cache to be enabled.
+     *
+     * Setting this to true will cause the cache to be named "debug" to avoid eviction from
+     * build fingerprint changes.
+     */
+    private static final boolean FORCE_PACKAGE_PARSED_CACHE_ENABLED = false;
 
     /**
      * Checks if the package was inactive during since <code>thresholdTimeinMillis</code>.
@@ -343,17 +199,6 @@
         }
     }
 
-    public static String packagesToString(List<PackageSetting> pkgSettings) {
-        StringBuilder sb = new StringBuilder();
-        for (int index = 0; index < pkgSettings.size(); index++) {
-            if (sb.length() > 0) {
-                sb.append(", ");
-            }
-            sb.append(pkgSettings.get(index).getPackageName());
-        }
-        return sb.toString();
-    }
-
     /**
      * Verifies that the given string {@code isa} is a valid supported isa on
      * the running device.
@@ -1277,4 +1122,107 @@
         }
         return StorageEnums.UNKNOWN;
     }
+
+    /**
+     * Enforces that only the system UID or root's UID or shell's UID can call
+     * a method exposed via Binder.
+     *
+     * @param message used as message if SecurityException is thrown
+     * @throws SecurityException if the caller is not system or shell
+     */
+    public static void enforceSystemOrRootOrShell(String message) {
+        final int uid = Binder.getCallingUid();
+        if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID && uid != Process.SHELL_UID) {
+            throw new SecurityException(message);
+        }
+    }
+
+    /**
+     * Enforces that only the system UID or root's UID can call a method exposed
+     * via Binder.
+     *
+     * @param message used as message if SecurityException is thrown
+     * @throws SecurityException if the caller is not system or root
+     */
+    public static void enforceSystemOrRoot(String message) {
+        final int uid = Binder.getCallingUid();
+        if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) {
+            throw new SecurityException(message);
+        }
+    }
+
+    public static @Nullable File preparePackageParserCache(boolean forEngBuild,
+            boolean isUserDebugBuild, String incrementalVersion) {
+        if (!FORCE_PACKAGE_PARSED_CACHE_ENABLED) {
+            if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) {
+                return null;
+            }
+
+            // Disable package parsing on eng builds to allow for faster incremental development.
+            if (forEngBuild) {
+                return null;
+            }
+
+            if (SystemProperties.getBoolean("pm.boot.disable_package_cache", false)) {
+                Slog.i(TAG, "Disabling package parser cache due to system property.");
+                return null;
+            }
+        }
+
+        // The base directory for the package parser cache lives under /data/system/.
+        final File cacheBaseDir = Environment.getPackageCacheDirectory();
+        if (!FileUtils.createDir(cacheBaseDir)) {
+            return null;
+        }
+
+        // There are several items that need to be combined together to safely
+        // identify cached items. In particular, changing the value of certain
+        // feature flags should cause us to invalidate any caches.
+        final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug"
+                : SystemProperties.digestOf("ro.build.fingerprint");
+
+        // Reconcile cache directories, keeping only what we'd actually use.
+        for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) {
+            if (Objects.equals(cacheName, cacheDir.getName())) {
+                Slog.d(TAG, "Keeping known cache " + cacheDir.getName());
+            } else {
+                Slog.d(TAG, "Destroying unknown cache " + cacheDir.getName());
+                FileUtils.deleteContentsAndDir(cacheDir);
+            }
+        }
+
+        // Return the versioned package cache directory.
+        File cacheDir = FileUtils.createDir(cacheBaseDir, cacheName);
+
+        if (cacheDir == null) {
+            // Something went wrong. Attempt to delete everything and return.
+            Slog.wtf(TAG, "Cache directory cannot be created - wiping base dir " + cacheBaseDir);
+            FileUtils.deleteContentsAndDir(cacheBaseDir);
+            return null;
+        }
+
+        // The following is a workaround to aid development on non-numbered userdebug
+        // builds or cases where "adb sync" is used on userdebug builds. If we detect that
+        // the system partition is newer.
+        //
+        // NOTE: When no BUILD_NUMBER is set by the build system, it defaults to a build
+        // that starts with "eng." to signify that this is an engineering build and not
+        // destined for release.
+        if (isUserDebugBuild && incrementalVersion.startsWith("eng.")) {
+            Slog.w(TAG, "Wiping cache directory because the system partition changed.");
+
+            // Heuristic: If the /system directory has been modified recently due to an "adb sync"
+            // or a regular make, then blow away the cache. Note that mtimes are *NOT* reliable
+            // in general and should not be used for production changes. In this specific case,
+            // we know that they will work.
+            File frameworkDir =
+                    new File(Environment.getRootDirectory(), "framework");
+            if (cacheDir.lastModified() < frameworkDir.lastModified()) {
+                FileUtils.deleteContents(cacheBaseDir);
+                cacheDir = FileUtils.createDir(cacheBaseDir, cacheName);
+            }
+        }
+
+        return cacheDir;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageRemovedInfo.java b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
index 74e1050..a60d2c8 100644
--- a/services/core/java/com/android/server/pm/PackageRemovedInfo.java
+++ b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
@@ -32,7 +32,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.server.LocalServices;
 
-public final class PackageRemovedInfo {
+final class PackageRemovedInfo {
     final PackageSender mPackageSender;
     String mRemovedPackage;
     String mInstallerPackageName;
diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
index 8c05ba7..68e880b 100644
--- a/services/core/java/com/android/server/pm/PreferredActivityHelper.java
+++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
@@ -135,7 +135,7 @@
             Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
                     + " is holding mLock", new Throwable());
         }
-        if (!mPm.mSystemReady) {
+        if (!mPm.isSystemReady()) {
             // We might get called before system is ready because of package changes etc, but
             // finding preferred activity depends on settings provider, so we ignore the update
             // before that.
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index cead177..f5010e4 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -164,7 +164,7 @@
                         & PackageManagerInternal.RESOLVE_NON_RESOLVER_ONLY) != 0) {
                     return null;
                 }
-                ri = new ResolveInfo(mPm.mResolveInfo);
+                ri = new ResolveInfo(mPm.getResolveInfo());
                 // if all resolve options are browsers, mark the resolver's info as if it were
                 // also a browser.
                 ri.handleAllWebDataURI = browserCount == n;
@@ -586,7 +586,7 @@
                     if (ri == null) {
                         continue;
                     }
-                    if (ri == mPm.mResolveInfo) {
+                    if (ri == mPm.getResolveInfo()) {
                         // ACK!  Must do something better with this.
                     }
                     ai = ri.activityInfo;
diff --git a/services/core/java/com/android/server/pm/ScanPackageHelper.java b/services/core/java/com/android/server/pm/ScanPackageHelper.java
index 616aec5..8018011 100644
--- a/services/core/java/com/android/server/pm/ScanPackageHelper.java
+++ b/services/core/java/com/android/server/pm/ScanPackageHelper.java
@@ -219,8 +219,7 @@
             PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage);
         }
 
-        return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
-                mPm.mPlatformPackage, mPm.mIsUpgrade, mPm.mIsPreNMR1Upgrade);
+        return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user);
     }
 
     // TODO(b/199937291): scanPackageNewLI() and scanPackageOnlyLI() should be merged.
@@ -259,7 +258,7 @@
             } else {
                 isUpdatedSystemApp = disabledPkgSetting != null;
             }
-            applyPolicy(parsedPackage, scanFlags, mPm.mPlatformPackage, isUpdatedSystemApp);
+            applyPolicy(parsedPackage, scanFlags, mPm.getPlatformPackage(), isUpdatedSystemApp);
             assertPackageIsValid(parsedPackage, parseFlags, scanFlags);
 
             SharedUserSetting sharedUserSetting = null;
@@ -275,8 +274,8 @@
                     }
                 }
             }
-            String platformPackageName = mPm.mPlatformPackage == null
-                    ? null : mPm.mPlatformPackage.getPackageName();
+            String platformPackageName = mPm.getPlatformPackage() == null
+                    ? null : mPm.getPlatformPackage().getPackageName();
             final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
                     pkgSetting == null ? null : pkgSetting.getPkg(), pkgSetting, disabledPkgSetting,
                     originalPkgSetting, realPkgName, parseFlags, scanFlags,
@@ -767,8 +766,7 @@
     public AndroidPackage addForInitLI(ParsedPackage parsedPackage,
             @ParsingPackageUtils.ParseFlags int parseFlags,
             @PackageManagerService.ScanFlags int scanFlags, long currentTime,
-            @Nullable UserHandle user, AndroidPackage platformPackage, boolean isUpgrade,
-            boolean isPreNMR1Upgrade) throws PackageManagerException {
+            @Nullable UserHandle user) throws PackageManagerException {
         final boolean scanSystemPartition =
                 (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
         final String renamedPkgName;
@@ -776,8 +774,11 @@
         final boolean isSystemPkgUpdated;
         final boolean pkgAlreadyExists;
         PackageSetting pkgSetting;
+        AndroidPackage platformPackage;
+        final boolean isUpgrade = mPm.isDeviceUpgrading();
 
         synchronized (mPm.mLock) {
+            platformPackage = mPm.getPlatformPackage();
             renamedPkgName = mPm.mSettings.getRenamedPackageLPr(
                     AndroidPackageUtils.getRealPackageOrNull(parsedPackage));
             final String realPkgName = getRealPackageName(parsedPackage,
@@ -826,8 +827,8 @@
                 if (isSystemPkgUpdated) {
                     // we're updating the disabled package, so, scan it as the package setting
                     boolean isPlatformPackage = platformPackage != null
-                            && Objects.equals(platformPackage.getPackageName(),
-                            parsedPackage.getPackageName());
+                            && platformPackage.getPackageName().equals(
+                                    parsedPackage.getPackageName());
                     final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
                             null, disabledPkgSetting /* pkgSetting */,
                             null /* disabledPkgSetting */, null /* originalPkgSetting */,
@@ -911,8 +912,7 @@
         // TODO(b/136132412): skip for Incremental installation
         final boolean skipVerify = scanSystemPartition
                 || (forceCollect && canSkipForcedPackageVerification(parsedPackage));
-        collectCertificatesLI(pkgSetting, parsedPackage, forceCollect, skipVerify,
-                isPreNMR1Upgrade);
+        collectCertificatesLI(pkgSetting, parsedPackage, forceCollect, skipVerify);
 
         // Reset profile if the application version is changed
         maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);
@@ -1075,11 +1075,10 @@
     }
 
     private void collectCertificatesLI(PackageSetting ps, ParsedPackage parsedPackage,
-            boolean forceCollect, boolean skipVerify, boolean mIsPreNMR1Upgrade)
-            throws PackageManagerException {
+            boolean forceCollect, boolean skipVerify) throws PackageManagerException {
         // When upgrading from pre-N MR1, verify the package time stamp using the package
         // directory and not the APK file.
-        final long lastModifiedTime = mIsPreNMR1Upgrade
+        final long lastModifiedTime = mPm.isPreNMR1Upgrade()
                 ? new File(parsedPackage.getPath()).lastModified()
                 : getLastModifiedTime(parsedPackage);
         final Settings.VersionInfo settingsVersionForPackage =
@@ -1250,7 +1249,7 @@
         synchronized (mPm.mLock) {
             // The special "android" package can only be defined once
             if (pkg.getPackageName().equals("android")) {
-                if (mPm.mAndroidApplication != null) {
+                if (mPm.getCoreAndroidApplication() != null) {
                     Slog.w(TAG, "*************************************************");
                     Slog.w(TAG, "Core android package being redefined.  Skipping.");
                     Slog.w(TAG, " codePath=" + pkg.getPath());
@@ -1401,7 +1400,7 @@
             // to the user-installed location. If we don't allow this change, any newer,
             // user-installed version of the application will be ignored.
             if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
-                if (mPm.mExpectingBetter.containsKey(pkg.getPackageName())) {
+                if (mPm.isExpectingBetter(pkg.getPackageName())) {
                     Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package "
                             + pkg.getPackageName());
                 } else {
@@ -1482,9 +1481,7 @@
                     if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
                         // This must be an update to a system overlay. Immutable overlays cannot be
                         // upgraded.
-                        Objects.requireNonNull(mPm.mOverlayConfig,
-                                "Parsing non-system dir before overlay configs are initialized");
-                        if (!mPm.mOverlayConfig.isMutable(pkg.getPackageName())) {
+                        if (!mPm.isOverlayMutable(pkg.getPackageName())) {
                             throw new PackageManagerException("Overlay "
                                     + pkg.getPackageName()
                                     + " is static and cannot be upgraded.");
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 7126909..f89d9eb 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3002,17 +3002,6 @@
             }
         }
 
-        // If the build is setup to drop runtime permissions
-        // on update drop the files before loading them.
-        if (PackageManagerService.CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE) {
-            final VersionInfo internal = getInternalVersion();
-            if (!Build.FINGERPRINT.equals(internal.fingerprint)) {
-                for (UserInfo user : users) {
-                    mRuntimePermissionsPersistence.deleteUserRuntimePermissionsFile(user.id);
-                }
-            }
-        }
-
         final int N = mPendingPackages.size();
 
         for (int i = 0; i < N; i++) {
diff --git a/services/core/java/com/android/server/pm/SharedLibraryHelper.java b/services/core/java/com/android/server/pm/SharedLibraryHelper.java
index 55e90a5..2582316 100644
--- a/services/core/java/com/android/server/pm/SharedLibraryHelper.java
+++ b/services/core/java/com/android/server/pm/SharedLibraryHelper.java
@@ -41,8 +41,10 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 final class SharedLibraryHelper {
     private static final boolean DEBUG_SHARED_LIBRARIES = false;
@@ -323,4 +325,32 @@
         }
         return versionedLib.get(version);
     }
+
+    public static List<SharedLibraryInfo> findSharedLibraries(PackageSetting pkgSetting) {
+        if (!pkgSetting.getPkgState().getUsesLibraryInfos().isEmpty()) {
+            ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
+            Set<String> collectedNames = new HashSet<>();
+            for (SharedLibraryInfo info : pkgSetting.getPkgState().getUsesLibraryInfos()) {
+                findSharedLibrariesRecursive(info, retValue, collectedNames);
+            }
+            return retValue;
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    private static void findSharedLibrariesRecursive(SharedLibraryInfo info,
+            ArrayList<SharedLibraryInfo> collected, Set<String> collectedNames) {
+        if (!collectedNames.contains(info.getName())) {
+            collectedNames.add(info.getName());
+            collected.add(info);
+
+            if (info.getDependencies() != null) {
+                for (SharedLibraryInfo dep : info.getDependencies()) {
+                    findSharedLibrariesRecursive(dep, collected, collectedNames);
+                }
+            }
+        }
+    }
+
 }
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index a82cb9e..b49c8da 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -132,7 +132,7 @@
         final AppDataHelper appDataHelper = new AppDataHelper(mPm);
         final ArrayList<PackageFreezer> freezers = new ArrayList<>();
         final ArrayList<AndroidPackage> loaded = new ArrayList<>();
-        final int parseFlags = mPm.mDefParseFlags | ParsingPackageUtils.PARSE_EXTERNAL_STORAGE;
+        final int parseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_EXTERNAL_STORAGE;
 
         final Settings.VersionInfo ver;
         final List<PackageSetting> packages;
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
index 72bc77e..3ee2348 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
@@ -105,7 +105,7 @@
         rule.system().validateFinalState()
         whenever(appHibernationManager.isHibernatingGlobally(TEST_PACKAGE_2_NAME)).thenReturn(true)
 
-        val optimizablePkgs = pm.optimizablePackages
+        val optimizablePkgs = DexOptHelper(pm).optimizablePackages
 
         assertTrue(optimizablePkgs.contains(TEST_PACKAGE_NAME))
         assertFalse(optimizablePkgs.contains(TEST_PACKAGE_2_NAME))