Merge "Break install up into phases"
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 0053745..5bf323a 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -1048,11 +1048,14 @@
                         final String otherPackageName =
                                 (other != null && other.getComponentName() != null)
                                         ? other.getComponentName().getPackageName() : "?";
-                        throw new PackageManagerException(
-                                INSTALL_FAILED_CONFLICTING_PROVIDER,
-                                "Can't install because provider name " + names[j]
-                                        + " (in package " + pkg.applicationInfo.packageName
-                                        + ") is already used by " + otherPackageName);
+                        // if we're installing over the same already-installed package, this is ok
+                        if (otherPackageName != pkg.packageName) {
+                            throw new PackageManagerException(
+                                    INSTALL_FAILED_CONFLICTING_PROVIDER,
+                                    "Can't install because provider name " + names[j]
+                                            + " (in package " + pkg.applicationInfo.packageName
+                                            + ") is already used by " + otherPackageName);
+                        }
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index db7a109..91af0ec 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -111,7 +111,8 @@
 import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
 import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
 import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS;
-import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+import static com.android.server.pm.permission.PermissionsState
+        .PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
 
 import android.Manifest;
 import android.annotation.IntDef;
@@ -303,7 +304,8 @@
 import com.android.server.pm.dex.PackageDexUsage;
 import com.android.server.pm.permission.BasePermission;
 import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
-import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy
+        .DefaultPermissionGrantedCallback;
 import com.android.server.pm.permission.PermissionManagerInternal;
 import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
 import com.android.server.pm.permission.PermissionManagerService;
@@ -8584,7 +8586,10 @@
                             null /* originalPkgSetting */, null, parseFlags, scanFlags,
                             (pkg == mPlatformPackage), user);
                     applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage);
-                    scanPackageOnlyLI(request, mFactoryTest, -1L);
+                    final ScanResult scanResult = scanPackageOnlyLI(request, mFactoryTest, -1L);
+                    if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) {
+                        scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting);
+                    }
                 }
             }
         }
@@ -9807,6 +9812,11 @@
         /** Whether or not the package scan was successful */
         public final boolean success;
         /**
+         * Whether or not the original PackageSetting needs to be updated with this result on
+         * commit.
+         */
+        public final boolean existingSettingCopied;
+        /**
          * The final package settings. This may be the same object passed in
          * the {@link ScanRequest}, but, with modified values.
          */
@@ -9816,11 +9826,12 @@
         public ScanResult(
                 ScanRequest request, boolean success,
                 @Nullable PackageSetting pkgSetting,
-                @Nullable List<String> changedAbiCodePath) {
+                @Nullable List<String> changedAbiCodePath, boolean existingSettingCopied) {
             this.request = request;
             this.success = success;
             this.pkgSetting = pkgSetting;
             this.changedAbiCodePath = changedAbiCodePath;
+            this.existingSettingCopied = existingSettingCopied;
         }
     }
 
@@ -9897,26 +9908,34 @@
     private @ScanFlags int adjustScanFlags(@ScanFlags int scanFlags,
             PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user,
             PackageParser.Package pkg) {
-        if (disabledPkgSetting != null) {
+
+        // TODO(patb): Do away entirely with disabledPkgSetting here. PkgSetting will always contain
+        // the correct isSystem value now that we don't disable system packages before scan.
+        final PackageSetting systemPkgSetting =
+                (scanFlags & SCAN_NEW_INSTALL) != 0 && disabledPkgSetting == null
+                        && pkgSetting != null && pkgSetting.isSystem()
+                        ? pkgSetting
+                        : disabledPkgSetting;
+        if (systemPkgSetting != null)  {
             // updated system application, must at least have SCAN_AS_SYSTEM
             scanFlags |= SCAN_AS_SYSTEM;
-            if ((disabledPkgSetting.pkgPrivateFlags
+            if ((systemPkgSetting.pkgPrivateFlags
                     & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
                 scanFlags |= SCAN_AS_PRIVILEGED;
             }
-            if ((disabledPkgSetting.pkgPrivateFlags
+            if ((systemPkgSetting.pkgPrivateFlags
                     & ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
                 scanFlags |= SCAN_AS_OEM;
             }
-            if ((disabledPkgSetting.pkgPrivateFlags
+            if ((systemPkgSetting.pkgPrivateFlags
                     & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
                 scanFlags |= SCAN_AS_VENDOR;
             }
-            if ((disabledPkgSetting.pkgPrivateFlags
+            if ((systemPkgSetting.pkgPrivateFlags
                     & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0) {
                 scanFlags |= SCAN_AS_PRODUCT;
             }
-            if ((disabledPkgSetting.pkgPrivateFlags
+            if ((systemPkgSetting.pkgPrivateFlags
                     & ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0) {
                 scanFlags |= SCAN_AS_PRODUCT_SERVICES;
             }
@@ -10046,11 +10065,18 @@
         final PackageSetting disabledPkgSetting = request.disabledPkgSetting;
         final UserHandle user = request.user;
         final String realPkgName = request.realPkgName;
-        final PackageSetting pkgSetting = result.pkgSetting;
         final List<String> changedAbiCodePath = result.changedAbiCodePath;
-        final boolean newPkgSettingCreated = (result.pkgSetting != request.pkgSetting);
-
-        if (newPkgSettingCreated) {
+        final PackageSetting pkgSetting;
+        if (result.existingSettingCopied) {
+            pkgSetting = request.pkgSetting;
+            pkgSetting.updateFrom(result.pkgSetting);
+            pkg.mExtras = pkgSetting;
+            if (pkgSetting.sharedUser != null
+                    && pkgSetting.sharedUser.removePackage(result.pkgSetting)) {
+                pkgSetting.sharedUser.addPackage(pkgSetting);
+            }
+        } else {
+            pkgSetting = result.pkgSetting;
             if (originalPkgSetting != null) {
                 mSettings.addRenamedPackageLPw(pkg.packageName, originalPkgSetting.name);
             }
@@ -10357,7 +10383,6 @@
         String primaryCpuAbiFromSettings = null;
         String secondaryCpuAbiFromSettings = null;
         boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
-
         if (!needToDeriveAbi) {
             if (pkgSetting != null) {
                 primaryCpuAbiFromSettings = pkgSetting.primaryCpuAbiString;
@@ -10401,6 +10426,11 @@
                     UserManagerService.getInstance(), usesStaticLibraries,
                     pkg.usesStaticLibrariesVersions);
         } else {
+            if (!createNewPackage) {
+                // make a deep copy to avoid modifying any existing system state.
+                pkgSetting = new PackageSetting(pkgSetting);
+                pkgSetting.pkg = pkg;
+            }
             // REMOVE SharedUserSetting from method; update in a separate call.
             //
             // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
@@ -10433,8 +10463,10 @@
             final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0;
             setInstantAppForUser(pkgSetting, userId, instantApp, fullApp);
         }
-
-        if (disabledPkgSetting != null) {
+        // TODO(patb): see if we can do away with disabled check here.
+        if (disabledPkgSetting != null
+                || (0 != (scanFlags & SCAN_NEW_INSTALL)
+                && pkgSetting != null && pkgSetting.isSystem())) {
             pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
         }
 
@@ -10617,7 +10649,8 @@
             pkgSetting.volumeUuid = volumeUuid;
         }
 
-        return new ScanResult(request, true, pkgSetting, changedAbiCodePath);
+        return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
+                !createNewPackage /* existingSettingCopied */);
     }
 
     /**
@@ -10832,7 +10865,7 @@
             }
 
             // A package name must be unique; don't allow duplicates
-            if (mPackages.containsKey(pkg.packageName)) {
+            if ((scanFlags & SCAN_NEW_INSTALL) == 0 && mPackages.containsKey(pkg.packageName)) {
                 throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
                         "Application package " + pkg.packageName
                         + " already installed.  Skipping duplicate.");
@@ -10841,7 +10874,8 @@
             if (pkg.applicationInfo.isStaticSharedLibrary()) {
                 // Static libs have a synthetic package name containing the version
                 // but we still want the base name to be unique.
-                if (mPackages.containsKey(pkg.manifestPackageName)) {
+                if ((scanFlags & SCAN_NEW_INSTALL) == 0
+                        && mPackages.containsKey(pkg.manifestPackageName)) {
                     throw new PackageManagerException(
                             "Duplicate static shared lib provider package");
                 }
@@ -11930,7 +11964,9 @@
         // Remove the parent package setting
         PackageSetting ps = (PackageSetting) pkg.mExtras;
         if (ps != null) {
-            removePackageLI(ps, chatty);
+            removePackageLI(ps.name, chatty);
+        } else if (DEBUG_REMOVE && chatty) {
+            Log.d(TAG, "Not removing package " + pkg.packageName + "; mExtras == null");
         }
         // Remove the child package setting
         final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
@@ -11938,23 +11974,22 @@
             PackageParser.Package childPkg = pkg.childPackages.get(i);
             ps = (PackageSetting) childPkg.mExtras;
             if (ps != null) {
-                removePackageLI(ps, chatty);
+                removePackageLI(ps.name, chatty);
             }
         }
     }
 
-    void removePackageLI(PackageSetting ps, boolean chatty) {
+    void removePackageLI(String packageName, boolean chatty) {
         if (DEBUG_INSTALL) {
             if (chatty)
-                Log.d(TAG, "Removing package " + ps.name);
+                Log.d(TAG, "Removing package " + packageName);
         }
 
         // writer
         synchronized (mPackages) {
-            mPackages.remove(ps.name);
-            final PackageParser.Package pkg = ps.pkg;
-            if (pkg != null) {
-                cleanPackageDataStructuresLILPw(pkg, chatty);
+            final PackageParser.Package removedPackage = mPackages.remove(packageName);
+            if (removedPackage != null) {
+                cleanPackageDataStructuresLILPw(removedPackage, chatty);
             }
         }
     }
@@ -14655,68 +14690,6 @@
         String origPermission;
     }
 
-    /*
-     * Install a non-existing package.
-     */
-    private void installNewPackageLIF(PackageParser.Package pkg, final @ParseFlags int parseFlags,
-            final @ScanFlags int scanFlags, UserHandle user, String installerPackageName,
-            String volumeUuid, PackageInstalledInfo res, int installReason) {
-        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installNewPackage");
-
-        // Remember this for later, in case we need to rollback this install
-        String pkgName = pkg.packageName;
-
-        if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
-
-        synchronized(mPackages) {
-            final String renamedPackage = mSettings.getRenamedPackageLPr(pkgName);
-            if (renamedPackage != null) {
-                // A package with the same name is already installed, though
-                // it has been renamed to an older name.  The package we
-                // are trying to install should be installed as an update to
-                // the existing one, but that has not been requested, so bail.
-                res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
-                        + " without first uninstalling package running as "
-                        + renamedPackage);
-                return;
-            }
-            if (mPackages.containsKey(pkgName)) {
-                // Don't allow installation over an existing package with the same name.
-                res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
-                        + " without first uninstalling.");
-                return;
-            }
-        }
-
-        try {
-            final PackageParser.Package newPackage;
-            List<ScanResult> scanResults = scanPackageTracedLI(pkg, parseFlags, scanFlags,
-                    System.currentTimeMillis(), user);
-            commitSuccessfulScanResults(scanResults);
-            // TODO(b/109941548): Child packages may return >1 result with the first being the base;
-            // we need to treat child packages as an atomic install and remove this hack
-            final ScanResult basePackageScanResult = scanResults.get(0);
-            newPackage = basePackageScanResult.pkgSetting.pkg;
-            updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason);
-
-            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
-                prepareAppDataAfterInstallLIF(newPackage);
-            } else {
-                // Remove package from internal structures, but keep around any
-                // data that might have already existed
-                deletePackageLIF(pkgName, UserHandle.ALL, false, null,
-                        PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
-            }
-        } catch (PackageManagerException e) {
-            destroyAppDataLIF(pkg, UserHandle.USER_ALL,
-                    StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
-            destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
-            res.setError("Package couldn't be installed in " + pkg.codePath, e);
-        }
-
-        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-    }
-
     private static void updateDigest(MessageDigest digest, File file) throws IOException {
         try (DigestInputStream digestStream =
                 new DigestInputStream(new FileInputStream(file), digest)) {
@@ -14724,482 +14697,6 @@
         }
     }
 
-    private void replacePackageLIF(PackageParser.Package pkg, final @ParseFlags int parseFlags,
-            final @ScanFlags int scanFlags, UserHandle user, String installerPackageName,
-            PackageInstalledInfo res, int installReason) {
-        final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
-
-        final PackageParser.Package oldPackage;
-        final PackageSetting ps;
-        final String pkgName = pkg.packageName;
-        final int[] allUsers;
-        final int[] installedUsers;
-
-        synchronized(mPackages) {
-            oldPackage = mPackages.get(pkgName);
-            if (DEBUG_INSTALL) Slog.d(TAG, "replacePackageLI: new=" + pkg + ", old=" + oldPackage);
-
-            // don't allow upgrade to target a release SDK from a pre-release SDK
-            final boolean oldTargetsPreRelease = oldPackage.applicationInfo.targetSdkVersion
-                    == android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
-            final boolean newTargetsPreRelease = pkg.applicationInfo.targetSdkVersion
-                    == android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
-            if (oldTargetsPreRelease
-                    && !newTargetsPreRelease
-                    && ((parseFlags & PackageParser.PARSE_FORCE_SDK) == 0)) {
-                Slog.w(TAG, "Can't install package targeting released sdk");
-                res.setReturnCode(PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE);
-                return;
-            }
-
-            ps = mSettings.mPackages.get(pkgName);
-
-            // verify signatures are valid
-            final KeySetManagerService ksms = mSettings.mKeySetManagerService;
-            if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) {
-                if (!ksms.checkUpgradeKeySetLocked(ps, pkg)) {
-                    res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
-                            "New package not signed by keys specified by upgrade-keysets: "
-                                    + pkgName);
-                    return;
-                }
-            } else {
-
-                // default to original signature matching
-                if (!pkg.mSigningDetails.checkCapability(oldPackage.mSigningDetails,
-                        PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
-                                && !oldPackage.mSigningDetails.checkCapability(
-                                        pkg.mSigningDetails,
-                                        PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
-                    res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
-                            "New package has a different signature: " + pkgName);
-                    return;
-                }
-            }
-
-            // don't allow a system upgrade unless the upgrade hash matches
-            if (oldPackage.restrictUpdateHash != null && oldPackage.isSystem()) {
-                final byte[] digestBytes;
-                try {
-                    final MessageDigest digest = MessageDigest.getInstance("SHA-512");
-                    updateDigest(digest, new File(pkg.baseCodePath));
-                    if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
-                        for (String path : pkg.splitCodePaths) {
-                            updateDigest(digest, new File(path));
-                        }
-                    }
-                    digestBytes = digest.digest();
-                } catch (NoSuchAlgorithmException | IOException e) {
-                    res.setError(INSTALL_FAILED_INVALID_APK,
-                            "Could not compute hash: " + pkgName);
-                    return;
-                }
-                if (!Arrays.equals(oldPackage.restrictUpdateHash, digestBytes)) {
-                    res.setError(INSTALL_FAILED_INVALID_APK,
-                            "New package fails restrict-update check: " + pkgName);
-                    return;
-                }
-                // retain upgrade restriction
-                pkg.restrictUpdateHash = oldPackage.restrictUpdateHash;
-            }
-
-            // Check for shared user id changes
-            String invalidPackageName =
-                    getParentOrChildPackageChangedSharedUser(oldPackage, pkg);
-            if (invalidPackageName != null) {
-                res.setError(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
-                        "Package " + invalidPackageName + " tried to change user "
-                                + oldPackage.mSharedUserId);
-                return;
-            }
-
-            // check if the new package supports all of the abis which the old package supports
-            boolean oldPkgSupportMultiArch = oldPackage.applicationInfo.secondaryCpuAbi != null;
-            boolean newPkgSupportMultiArch = pkg.applicationInfo.secondaryCpuAbi != null;
-            if (isSystemApp(oldPackage) && oldPkgSupportMultiArch && !newPkgSupportMultiArch) {
-                res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
-                        "Update to package " + pkgName + " doesn't support multi arch");
-                return;
-            }
-
-            // In case of rollback, remember per-user/profile install state
-            allUsers = sUserManager.getUserIds();
-            installedUsers = ps.queryInstalledUsers(allUsers, true);
-
-            // don't allow an upgrade from full to ephemeral
-            if (isInstantApp) {
-                if (user == null || user.getIdentifier() == UserHandle.USER_ALL) {
-                    for (int currentUser : allUsers) {
-                        if (!ps.getInstantApp(currentUser)) {
-                            // can't downgrade from full to instant
-                            Slog.w(TAG, "Can't replace full app with instant app: " + pkgName
-                                    + " for user: " + currentUser);
-                            res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
-                            return;
-                        }
-                    }
-                } else if (!ps.getInstantApp(user.getIdentifier())) {
-                    // can't downgrade from full to instant
-                    Slog.w(TAG, "Can't replace full app with instant app: " + pkgName
-                            + " for user: " + user.getIdentifier());
-                    res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
-                    return;
-                }
-            }
-        }
-
-        // Update what is removed
-        res.removedInfo = new PackageRemovedInfo(this);
-        res.removedInfo.uid = oldPackage.applicationInfo.uid;
-        res.removedInfo.removedPackage = oldPackage.packageName;
-        res.removedInfo.installerPackageName = ps.installerPackageName;
-        res.removedInfo.isStaticSharedLib = pkg.staticSharedLibName != null;
-        res.removedInfo.isUpdate = true;
-        res.removedInfo.origUsers = installedUsers;
-        res.removedInfo.installReasons = new SparseArray<>(installedUsers.length);
-        for (int i = 0; i < installedUsers.length; i++) {
-            final int userId = installedUsers[i];
-            res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId));
-        }
-
-        final int childCount = (oldPackage.childPackages != null)
-                ? oldPackage.childPackages.size() : 0;
-        for (int i = 0; i < childCount; i++) {
-            boolean childPackageUpdated = false;
-            PackageParser.Package childPkg = oldPackage.childPackages.get(i);
-            final PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
-            if (res.addedChildPackages != null) {
-                PackageInstalledInfo childRes = res.addedChildPackages.get(childPkg.packageName);
-                if (childRes != null) {
-                    childRes.removedInfo.uid = childPkg.applicationInfo.uid;
-                    childRes.removedInfo.removedPackage = childPkg.packageName;
-                    if (childPs != null) {
-                        childRes.removedInfo.installerPackageName = childPs.installerPackageName;
-                    }
-                    childRes.removedInfo.isUpdate = true;
-                    childRes.removedInfo.installReasons = res.removedInfo.installReasons;
-                    childPackageUpdated = true;
-                }
-            }
-            if (!childPackageUpdated) {
-                PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this);
-                childRemovedRes.removedPackage = childPkg.packageName;
-                if (childPs != null) {
-                    childRemovedRes.installerPackageName = childPs.installerPackageName;
-                }
-                childRemovedRes.isUpdate = false;
-                childRemovedRes.dataRemoved = true;
-                synchronized (mPackages) {
-                    if (childPs != null) {
-                        childRemovedRes.origUsers = childPs.queryInstalledUsers(allUsers, true);
-                    }
-                }
-                if (res.removedInfo.removedChildPackages == null) {
-                    res.removedInfo.removedChildPackages = new ArrayMap<>();
-                }
-                res.removedInfo.removedChildPackages.put(childPkg.packageName, childRemovedRes);
-            }
-        }
-
-        boolean sysPkg = (isSystemApp(oldPackage));
-        if (sysPkg) {
-            // Set the system/privileged/oem/vendor/product flags as needed
-            final boolean privileged = isPrivilegedApp(oldPackage);
-            final boolean oem = isOemApp(oldPackage);
-            final boolean vendor = isVendorApp(oldPackage);
-            final boolean product = isProductApp(oldPackage);
-            final boolean productServices = isProductServicesApp(oldPackage);
-
-            final @ParseFlags int systemParseFlags = parseFlags;
-            final @ScanFlags int systemScanFlags = scanFlags
-                    | SCAN_AS_SYSTEM
-                    | (privileged ? SCAN_AS_PRIVILEGED : 0)
-                    | (oem ? SCAN_AS_OEM : 0)
-                    | (vendor ? SCAN_AS_VENDOR : 0)
-                    | (product ? SCAN_AS_PRODUCT : 0)
-                    | (productServices ? SCAN_AS_PRODUCT_SERVICES : 0);
-
-            replaceSystemPackageLIF(oldPackage, pkg, systemParseFlags, systemScanFlags,
-                    user, allUsers, installerPackageName, res, installReason);
-        } else {
-            replaceNonSystemPackageLIF(oldPackage, pkg, parseFlags, scanFlags,
-                    user, allUsers, installerPackageName, res, installReason);
-        }
-    }
-
-    private void replaceNonSystemPackageLIF(PackageParser.Package deletedPackage,
-            PackageParser.Package pkg, final @ParseFlags int parseFlags,
-            final @ScanFlags int scanFlags, UserHandle user, int[] allUsers,
-            String installerPackageName, PackageInstalledInfo res, int installReason) {
-        if (DEBUG_INSTALL) Slog.d(TAG, "replaceNonSystemPackageLI: new=" + pkg + ", old="
-                + deletedPackage);
-
-        String pkgName = deletedPackage.packageName;
-        boolean deletedPkg = true;
-        boolean addedPkg = false;
-        boolean updatedSettings = false;
-        final boolean killApp = (scanFlags & SCAN_DONT_KILL_APP) == 0;
-        final int deleteFlags = PackageManager.DELETE_KEEP_DATA
-                | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
-
-        final long origUpdateTime = (pkg.mExtras != null)
-                ? ((PackageSetting)pkg.mExtras).lastUpdateTime : 0;
-
-        // First delete the existing package while retaining the data directory
-        if (!deletePackageLIF(pkgName, null, true, allUsers, deleteFlags,
-                res.removedInfo, true, pkg)) {
-            // If the existing package wasn't successfully deleted
-            res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE, "replaceNonSystemPackageLI");
-            deletedPkg = false;
-        } else {
-            // Successfully deleted the old package; proceed with replace.
-
-            // If deleted package lived in a container, give users a chance to
-            // relinquish resources before killing.
-            if (deletedPackage.isForwardLocked() || isExternal(deletedPackage)) {
-                if (DEBUG_INSTALL) {
-                    Slog.i(TAG, "upgrading pkg " + deletedPackage + " is ASEC-hosted -> UNAVAILABLE");
-                }
-                final int[] uidArray = new int[] { deletedPackage.applicationInfo.uid };
-                final ArrayList<String> pkgList = new ArrayList<>(1);
-                pkgList.add(deletedPackage.applicationInfo.packageName);
-                sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
-            }
-
-            clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
-                    | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
-
-            try {
-                final List<ScanResult> scanResults = scanPackageTracedLI(pkg, parseFlags,
-                        scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user);
-                commitSuccessfulScanResults(scanResults);
-                final PackageParser.Package newPackage = scanResults.get(0).pkgSetting.pkg;
-                updateSettingsLI(newPackage, installerPackageName, allUsers, res, user,
-                        installReason);
-
-                // Update the in-memory copy of the previous code paths.
-                PackageSetting ps = mSettings.mPackages.get(pkgName);
-                if (!killApp) {
-                    if (ps.oldCodePaths == null) {
-                        ps.oldCodePaths = new ArraySet<>();
-                    }
-                    Collections.addAll(ps.oldCodePaths, deletedPackage.baseCodePath);
-                    if (deletedPackage.splitCodePaths != null) {
-                        Collections.addAll(ps.oldCodePaths, deletedPackage.splitCodePaths);
-                    }
-                } else {
-                    ps.oldCodePaths = null;
-                }
-                if (ps.childPackageNames != null) {
-                    for (int i = ps.childPackageNames.size() - 1; i >= 0; --i) {
-                        final String childPkgName = ps.childPackageNames.get(i);
-                        final PackageSetting childPs = mSettings.mPackages.get(childPkgName);
-                        childPs.oldCodePaths = ps.oldCodePaths;
-                    }
-                }
-                prepareAppDataAfterInstallLIF(newPackage);
-                addedPkg = true;
-                mDexManager.notifyPackageUpdated(newPackage.packageName,
-                        newPackage.baseCodePath, newPackage.splitCodePaths);
-            } catch (PackageManagerException e) {
-                res.setError("Package couldn't be installed in " + pkg.codePath, e);
-            }
-        }
-
-        if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
-            if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, rolling pack: " + pkgName);
-
-            // Revert all internal state mutations and added folders for the failed install
-            if (addedPkg) {
-                deletePackageLIF(pkgName, null, true, allUsers, deleteFlags,
-                        res.removedInfo, true, null);
-            }
-
-            // Restore the old package
-            if (deletedPkg) {
-                if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage);
-                File restoreFile = new File(deletedPackage.codePath);
-                // Parse old package
-                boolean oldExternal = isExternal(deletedPackage);
-                int oldParseFlags  = mDefParseFlags | PackageParser.PARSE_CHATTY |
-                        (deletedPackage.isForwardLocked() ? PackageParser.PARSE_FORWARD_LOCK : 0) |
-                        (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
-                int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
-                try {
-                    scanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags, origUpdateTime,
-                            null);
-                } catch (PackageManagerException e) {
-                    Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade: "
-                            + e.getMessage());
-                    return;
-                }
-
-                synchronized (mPackages) {
-                    // Ensure the installer package name up to date
-                    setInstallerPackageNameLPw(deletedPackage, installerPackageName);
-
-                    // Update permissions for restored package
-                    mPermissionManager.updatePermissions(
-                            deletedPackage.packageName, deletedPackage, false, mPackages.values(),
-                            mPermissionCallback);
-
-                    mSettings.writeLPr();
-                }
-
-                Slog.i(TAG, "Successfully restored package : " + pkgName + " after failed upgrade");
-            }
-        } else {
-            synchronized (mPackages) {
-                PackageSetting ps = mSettings.getPackageLPr(pkg.packageName);
-                if (ps != null) {
-                    res.removedInfo.removedForAllUsers = mPackages.get(ps.name) == null;
-                    if (res.removedInfo.removedChildPackages != null) {
-                        final int childCount = res.removedInfo.removedChildPackages.size();
-                        // Iterate in reverse as we may modify the collection
-                        for (int i = childCount - 1; i >= 0; i--) {
-                            String childPackageName = res.removedInfo.removedChildPackages.keyAt(i);
-                            if (res.addedChildPackages.containsKey(childPackageName)) {
-                                res.removedInfo.removedChildPackages.removeAt(i);
-                            } else {
-                                PackageRemovedInfo childInfo = res.removedInfo
-                                        .removedChildPackages.valueAt(i);
-                                childInfo.removedForAllUsers = mPackages.get(
-                                        childInfo.removedPackage) == null;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    private void replaceSystemPackageLIF(PackageParser.Package deletedPackage,
-            PackageParser.Package pkg, final @ParseFlags int parseFlags,
-            final @ScanFlags int scanFlags, UserHandle user,
-            int[] allUsers, String installerPackageName, PackageInstalledInfo res,
-            int installReason) {
-        if (DEBUG_INSTALL) Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
-                + ", old=" + deletedPackage);
-
-        final boolean disabledSystem;
-
-        // Remove existing system package
-        removePackageLI(deletedPackage, true);
-
-        synchronized (mPackages) {
-            disabledSystem = disableSystemPackageLPw(deletedPackage, pkg);
-        }
-        if (!disabledSystem) {
-            // We didn't need to disable the .apk as a current system package,
-            // which means we are replacing another update that is already
-            // installed.  We need to make sure to delete the older one's .apk.
-            res.removedInfo.args = createInstallArgsForExisting(0,
-                    deletedPackage.applicationInfo.getCodePath(),
-                    deletedPackage.applicationInfo.getResourcePath(),
-                    getAppDexInstructionSets(deletedPackage.applicationInfo));
-        } else {
-            res.removedInfo.args = null;
-        }
-
-        // Successfully disabled the old package. Now proceed with re-installation
-        clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
-                | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
-
-        res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
-        pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
-                ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
-
-        PackageParser.Package newPackage = null;
-        try {
-            final List<ScanResult> scanResults =
-                    scanPackageTracedLI(pkg, parseFlags, scanFlags, 0, user);
-            // Add the package to the internal data structures
-            commitSuccessfulScanResults(scanResults);
-            newPackage = scanResults.get(0).pkgSetting.pkg;
-
-            // Set the update and install times
-            PackageSetting deletedPkgSetting = (PackageSetting) deletedPackage.mExtras;
-            setInstallAndUpdateTime(newPackage, deletedPkgSetting.firstInstallTime,
-                    System.currentTimeMillis());
-
-            // Update the package dynamic state if succeeded
-            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
-                // Now that the install succeeded make sure we remove data
-                // directories for any child package the update removed.
-                final int deletedChildCount = (deletedPackage.childPackages != null)
-                        ? deletedPackage.childPackages.size() : 0;
-                final int newChildCount = (newPackage.childPackages != null)
-                        ? newPackage.childPackages.size() : 0;
-                for (int i = 0; i < deletedChildCount; i++) {
-                    PackageParser.Package deletedChildPkg = deletedPackage.childPackages.get(i);
-                    boolean childPackageDeleted = true;
-                    for (int j = 0; j < newChildCount; j++) {
-                        PackageParser.Package newChildPkg = newPackage.childPackages.get(j);
-                        if (deletedChildPkg.packageName.equals(newChildPkg.packageName)) {
-                            childPackageDeleted = false;
-                            break;
-                        }
-                    }
-                    if (childPackageDeleted) {
-                        PackageSetting ps = mSettings.getDisabledSystemPkgLPr(
-                                deletedChildPkg.packageName);
-                        if (ps != null && res.removedInfo.removedChildPackages != null) {
-                            PackageRemovedInfo removedChildRes = res.removedInfo
-                                    .removedChildPackages.get(deletedChildPkg.packageName);
-                            removePackageDataLIF(ps, allUsers, removedChildRes, 0, false);
-                            removedChildRes.removedForAllUsers = mPackages.get(ps.name) == null;
-                        }
-                    }
-                }
-
-                updateSettingsLI(newPackage, installerPackageName, allUsers, res, user,
-                        installReason);
-                prepareAppDataAfterInstallLIF(newPackage);
-
-                mDexManager.notifyPackageUpdated(newPackage.packageName,
-                            newPackage.baseCodePath, newPackage.splitCodePaths);
-            }
-        } catch (PackageManagerException e) {
-            res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
-            res.setError("Package couldn't be installed in " + pkg.codePath, e);
-        }
-
-        if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
-            // Re installation failed. Restore old information
-            // Remove new pkg information
-            if (newPackage != null) {
-                removeInstalledPackageLI(newPackage, true);
-            }
-            // Add back the old system package
-            try {
-                final List<ScanResult> scanResults = scanPackageTracedLI(
-                        deletedPackage, parseFlags, SCAN_UPDATE_SIGNATURE, 0, user);
-                commitSuccessfulScanResults(scanResults);
-            } catch (PackageManagerException e) {
-                Slog.e(TAG, "Failed to restore original package: " + e.getMessage());
-            }
-
-            synchronized (mPackages) {
-                if (disabledSystem) {
-                    enableSystemPackageLPw(deletedPackage);
-                }
-
-                // Ensure the installer package name up to date
-                setInstallerPackageNameLPw(deletedPackage, installerPackageName);
-
-                // Update permissions for restored package
-                mPermissionManager.updatePermissions(
-                        deletedPackage.packageName, deletedPackage, false, mPackages.values(),
-                        mPermissionCallback);
-
-                mSettings.writeLPr();
-            }
-
-            Slog.i(TAG, "Successfully restored package : " + deletedPackage.packageName
-                    + " after failed upgrade");
-        }
-    }
-
     /**
      * Checks whether the parent or any of the child packages have a change shared
      * user. For a package to be a valid update the shred users of the parent and
@@ -15396,92 +14893,683 @@
 
         Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
     }
+
     private static class InstallRequest {
-        final InstallArgs args;
-        final PackageInstalledInfo res;
+        public final InstallArgs args;
+        public final PackageInstalledInfo installResult;
 
         private InstallRequest(InstallArgs args, PackageInstalledInfo res) {
             this.args = args;
-            this.res = res;
+            this.installResult = res;
         }
     }
 
     @GuardedBy({"mInstallLock", "mPackages"})
-    private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) {
+    private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo installResult) {
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackage");
-            installPackagesLI(Collections.singletonList(new InstallRequest(args, res)));
+            installPackagesLI(Collections.singletonList(new InstallRequest(args, installResult)));
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
     }
 
+    /**
+     * Package state to commit to memory and disk after reconciliation has completed.
+     */
     private static class CommitRequest {
         final Map<String, ReconciledPackage> reconciledPackages;
+        final int[] mAllUsers;
 
-        private CommitRequest(Map<String, ReconciledPackage> reconciledPackages) {
+        private CommitRequest(Map<String, ReconciledPackage> reconciledPackages, int[] allUsers) {
             this.reconciledPackages = reconciledPackages;
+            this.mAllUsers = allUsers;
         }
     }
-    private static class ReconcileRequest {
-        final Map<String, ScanResult> scannedPackages;
 
-        private ReconcileRequest(Map<String, ScanResult> scannedPackages) {
+    /**
+     * Package scan results and related request details used to reconcile the potential addition of
+     * one or more packages to the system.
+     *
+     * Reconcile will take a set of package details that need to be committed to the system and make
+     * sure that they are valid in the context of the system and the other installing apps. Any
+     * invalid state or app will result in a failed reconciliation and thus whatever operation (such
+     * as install) led to the request.
+     */
+    private static class ReconcileRequest {
+        public final Map<String, ScanResult> scannedPackages;
+
+        // TODO: Remove install-specific details from reconcile request; make them generic types
+        //       that can be used for scanDir for example.
+        public final Map<String, InstallArgs> installArgs;
+        public final Map<String, PackageInstalledInfo> installResults;
+        public final Map<String, PrepareResult> preparedPackages;
+
+        private ReconcileRequest(Map<String, ScanResult> scannedPackages,
+                Map<String, InstallArgs> installArgs,
+                Map<String, PackageInstalledInfo> installResults,
+                Map<String, PrepareResult> preparedPackages) {
             this.scannedPackages = scannedPackages;
+            this.installArgs = installArgs;
+            this.installResults = installResults;
+            this.preparedPackages = preparedPackages;
         }
     }
     private static class ReconcileFailure extends PackageManagerException {
         public ReconcileFailure(String message) {
             super("Invalid reconcile request: " + message);
         }
-    };
+    }
 
     /**
      * A container of all data needed to commit a package to in-memory data structures and to disk.
-     * Ideally most of the data contained in this class will move into a PackageSetting it contains.
+     * TODO: move most of the data contained her into a PackageSetting for commit.
      */
-    private static class ReconciledPackage {}
+    private static class ReconciledPackage {
+        public final PackageSetting pkgSetting;
+        public final ScanResult scanResult;
+        public final UserHandle installForUser;
+        public final String volumeUuid;
+        // TODO: Remove install-specific details from the reconcile result
+        public final PackageInstalledInfo installResult;
+        public final PrepareResult prepareResult;
+        @PackageManager.InstallFlags
+        public final int installFlags;
+        public final InstallArgs installArgs;
+
+        private ReconciledPackage(InstallArgs installArgs, PackageSetting pkgSetting,
+                UserHandle installForUser, PackageInstalledInfo installResult, int installFlags,
+                String volumeUuid, PrepareResult prepareResult, ScanResult scanResult) {
+            this.installArgs = installArgs;
+            this.pkgSetting = pkgSetting;
+            this.installForUser = installForUser;
+            this.installResult = installResult;
+            this.installFlags = installFlags;
+            this.volumeUuid = volumeUuid;
+            this.prepareResult = prepareResult;
+            this.scanResult = scanResult;
+        }
+    }
 
     @GuardedBy("mPackages")
     private static Map<String, ReconciledPackage> reconcilePackagesLocked(
-            final ReconcileRequest request) throws ReconcileFailure {
-        return Collections.emptyMap();
+            final ReconcileRequest request)
+            throws ReconcileFailure {
+        Map<String, ReconciledPackage> result = new ArrayMap<>(request.scannedPackages.size());
+        for (String installPackageName : request.installArgs.keySet()) {
+            final ScanResult scanResult = request.scannedPackages.get(installPackageName);
+            final InstallArgs installArgs = request.installArgs.get(installPackageName);
+            final PackageInstalledInfo res = request.installResults.get(installPackageName);
+            if (scanResult == null || installArgs == null || res == null) {
+                throw new ReconcileFailure(
+                        "inputs not balanced; missing argument for " + installPackageName);
+            }
+            result.put(installPackageName,
+                    new ReconciledPackage(installArgs, scanResult.pkgSetting, installArgs.getUser(),
+                            res, installArgs.installFlags, installArgs.volumeUuid,
+                            request.preparedPackages.get(installPackageName), scanResult));
+        }
+        return result;
     }
 
     @GuardedBy("mPackages")
     private boolean commitPackagesLocked(final CommitRequest request) {
+        // TODO: remove any expected failures from this method; this should only be able to fail due
+        //       to unavoidable errors (I/O, etc.)
+        for (ReconciledPackage reconciledPkg : request.reconciledPackages.values()) {
+            final ScanResult scanResult = reconciledPkg.scanResult;
+            final ScanRequest scanRequest = scanResult.request;
+            final PackageParser.Package pkg = scanRequest.pkg;
+            final String packageName = pkg.packageName;
+            final PackageInstalledInfo res = reconciledPkg.installResult;
+
+            if (reconciledPkg.prepareResult.replace) {
+                PackageParser.Package oldPackage = mPackages.get(packageName);
+                if (reconciledPkg.prepareResult.system) {
+                    // Remove existing system package
+                    removePackageLI(oldPackage, true);
+                    if (!disableSystemPackageLPw(oldPackage, pkg)) {
+                        // We didn't need to disable the .apk as a current system package,
+                        // which means we are replacing another update that is already
+                        // installed.  We need to make sure to delete the older one's .apk.
+                        res.removedInfo.args = createInstallArgsForExisting(0,
+                                oldPackage.applicationInfo.getCodePath(),
+                                oldPackage.applicationInfo.getResourcePath(),
+                                getAppDexInstructionSets(oldPackage.applicationInfo));
+                    } else {
+                        res.removedInfo.args = null;
+                    }
+
+                    // Set the update and install times
+                    PackageSetting deletedPkgSetting = (PackageSetting) oldPackage.mExtras;
+                    setInstallAndUpdateTime(pkg, deletedPkgSetting.firstInstallTime,
+                            System.currentTimeMillis());
+
+                    // Update the package dynamic state if succeeded
+                    // Now that the install succeeded make sure we remove data
+                    // directories for any child package the update removed.
+                    final int deletedChildCount = (oldPackage.childPackages != null)
+                            ? oldPackage.childPackages.size() : 0;
+                    final int newChildCount = (pkg.childPackages != null)
+                            ? pkg.childPackages.size() : 0;
+                    for (int i = 0; i < deletedChildCount; i++) {
+                        PackageParser.Package deletedChildPkg = oldPackage.childPackages.get(i);
+                        boolean childPackageDeleted = true;
+                        for (int j = 0; j < newChildCount; j++) {
+                            PackageParser.Package newChildPkg = pkg.childPackages.get(j);
+                            if (deletedChildPkg.packageName.equals(newChildPkg.packageName)) {
+                                childPackageDeleted = false;
+                                break;
+                            }
+                        }
+                        if (childPackageDeleted) {
+                            PackageSetting ps1 = mSettings.getDisabledSystemPkgLPr(
+                                    deletedChildPkg.packageName);
+                            if (ps1 != null && res.removedInfo.removedChildPackages != null) {
+                                PackageRemovedInfo removedChildRes = res.removedInfo
+                                        .removedChildPackages.get(deletedChildPkg.packageName);
+                                removePackageDataLIF(ps1, request.mAllUsers, removedChildRes, 0,
+                                        false);
+                                removedChildRes.removedForAllUsers = mPackages.get(ps1.name)
+                                        == null;
+                            }
+                        }
+                    }
+                } else {
+                    final boolean killApp = (scanRequest.scanFlags & SCAN_DONT_KILL_APP) == 0;
+                    final int deleteFlags = PackageManager.DELETE_KEEP_DATA
+                            | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
+                    // First delete the existing package while retaining the data directory
+                    if (!deletePackageLIF(packageName, null, true, request.mAllUsers, deleteFlags,
+                            res.removedInfo, true, pkg)) {
+                        // If the existing package wasn't successfully deleted
+                        res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE,
+                                "replaceNonSystemPackageLI");
+                        return false;
+                    } else {
+                        // Successfully deleted the old package; proceed with replace.
+
+                        // If deleted package lived in a container, give users a chance to
+                        // relinquish resources before killing.
+                        if (oldPackage.isForwardLocked() || isExternal(oldPackage)) {
+                            if (DEBUG_INSTALL) {
+                                Slog.i(TAG, "upgrading pkg " + oldPackage
+                                        + " is ASEC-hosted -> UNAVAILABLE");
+                            }
+                            final int[] uidArray = new int[]{oldPackage.applicationInfo.uid};
+                            final ArrayList<String> pkgList = new ArrayList<>(1);
+                            pkgList.add(oldPackage.applicationInfo.packageName);
+                            sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
+                        }
+
+                        clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
+                                | StorageManager.FLAG_STORAGE_CE
+                                | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+                    }
+
+
+
+                    // Update the in-memory copy of the previous code paths.
+                    PackageSetting ps1 = mSettings.mPackages.get(
+                            reconciledPkg.prepareResult.existingPackage.packageName);
+                    if ((reconciledPkg.installArgs.installFlags & PackageManager.DONT_KILL_APP)
+                            == 0) {
+                        if (ps1.mOldCodePaths == null) {
+                            ps1.mOldCodePaths = new ArraySet<>();
+                        }
+                        Collections.addAll(ps1.mOldCodePaths, oldPackage.baseCodePath);
+                        if (oldPackage.splitCodePaths != null) {
+                            Collections.addAll(ps1.mOldCodePaths, oldPackage.splitCodePaths);
+                        }
+                    } else {
+                        ps1.mOldCodePaths = null;
+                    }
+                    if (ps1.childPackageNames != null) {
+                        for (int i = ps1.childPackageNames.size() - 1; i >= 0; --i) {
+                            final String childPkgName = ps1.childPackageNames.get(i);
+                            final PackageSetting childPs = mSettings.mPackages.get(childPkgName);
+                            childPs.mOldCodePaths = ps1.mOldCodePaths;
+                        }
+                    }
+
+                    if (reconciledPkg.installResult.returnCode
+                            == PackageManager.INSTALL_SUCCEEDED) {
+                        PackageSetting ps2 = mSettings.getPackageLPr(pkg.packageName);
+                        if (ps2 != null) {
+                            res.removedInfo.removedForAllUsers = mPackages.get(ps2.name) == null;
+                            if (res.removedInfo.removedChildPackages != null) {
+                                final int childCount1 = res.removedInfo.removedChildPackages.size();
+                                // Iterate in reverse as we may modify the collection
+                                for (int i = childCount1 - 1; i >= 0; i--) {
+                                    String childPackageName =
+                                            res.removedInfo.removedChildPackages.keyAt(i);
+                                    if (res.addedChildPackages.containsKey(childPackageName)) {
+                                        res.removedInfo.removedChildPackages.removeAt(i);
+                                    } else {
+                                        PackageRemovedInfo childInfo = res.removedInfo
+                                                .removedChildPackages.valueAt(i);
+                                        childInfo.removedForAllUsers = mPackages.get(
+                                                childInfo.removedPackage) == null;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+
+
+            try {
+                commitScanResultLocked(scanResult);
+            } catch (PackageManagerException e) {
+                res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
+                res.setError("Package couldn't be installed in " + pkg.codePath, e);
+                return false;
+            }
+            updateSettingsLI(pkg, reconciledPkg.installArgs.installerPackageName, request.mAllUsers,
+                    res, reconciledPkg.installForUser, reconciledPkg.installArgs.installReason);
+
+            final PackageSetting ps = mSettings.mPackages.get(packageName);
+            if (ps != null) {
+                res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
+                ps.setUpdateAvailable(false /*updateAvailable*/);
+            }
+            final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+            for (int i = 0; i < childCount; i++) {
+                PackageParser.Package childPkg = pkg.childPackages.get(i);
+                PackageInstalledInfo childRes = res.addedChildPackages.get(
+                        childPkg.packageName);
+                PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
+                if (childPs != null) {
+                    childRes.newUsers = childPs.queryInstalledUsers(
+                            sUserManager.getUserIds(), true);
+                }
+            }
+            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+                updateSequenceNumberLP(ps, res.newUsers);
+                updateInstantAppInstallerLocked(packageName);
+            }
+        }
         return true;
     }
 
-    @GuardedBy({"mInstallLock", "mPackages", "PackageManagerService.mPackages"})
+    /**
+     * Installs one or more packages atomically. This operation is broken up into four phases:
+     * <ul>
+     *     <li><b>Prepare</b>
+     *         <br/>Analyzes any current install state, parses the package and does initial
+     *         validation on it.</li>
+     *     <li><b>Scan</b>
+     *         <br/>Interrogates the parsed packages given the context collected in prepare.</li>
+     *     <li><b>Reconcile</b>
+     *         <br/>Validates scanned packages in the context of each other and the current system
+     *         state to ensure that the install will be successful.
+     *     <li><b>Commit</b>
+     *         <br/>Commits all scanned packages and updates system state. This is the only place
+     *         that system state may be modified in the install flow and all predictable errors
+     *         must be determined before this phase.</li>
+     * </ul>
+     *
+     * Failure at any phase will result in a full failure to install all packages.
+     */
+    @GuardedBy({"mInstallLock", "mPackages"})
     private void installPackagesLI(List<InstallRequest> requests) {
-        Map<String, ScanResult> scans = new ArrayMap<>(requests.size());
-        for (InstallRequest request : requests) {
-            // TODO(b/109941548): remove this once we've pulled everything from it and into scan,
-            // reconcile or commit.
-            preparePackageLI(request.args, request.res);
-
-            // TODO(b/109941548): scan package and get result
-        }
-        ReconcileRequest reconcileRequest = new ReconcileRequest(scans);
-        Map<String, ReconciledPackage> reconciledPackages;
+        final Map<String, ScanResult> scans = new ArrayMap<>(requests.size());
+        final Map<String, InstallArgs> installArgs = new ArrayMap<>(requests.size());
+        final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
+        final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
         try {
-            reconciledPackages = reconcilePackagesLocked(reconcileRequest);
-        } catch (ReconcileFailure e) {
-            // TODO(b/109941548): set install args error
-            return;
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
+            for (InstallRequest request : requests) {
+                // TODO(b/109941548): remove this once we've pulled everything from it and into
+                //                    scan, reconcile or commit.
+                final PrepareResult prepareResult;
+                try {
+                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
+                    prepareResult = preparePackageLI(request.args, request.installResult);
+                } catch (PrepareFailure prepareFailure) {
+                    request.installResult.setError(prepareFailure.error,
+                            prepareFailure.getMessage());
+                    request.installResult.origPackage = prepareFailure.conflictingPackage;
+                    request.installResult.origPermission = prepareFailure.conflictingPermission;
+                    return;
+                } finally {
+                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+                }
+                request.installResult.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
+                request.installResult.installerPackageName = request.args.installerPackageName;
+
+                final String packageName = prepareResult.packageToScan.packageName;
+                prepareResults.put(packageName, prepareResult);
+                installResults.put(packageName, request.installResult);
+                installArgs.put(packageName, request.args);
+                try {
+                    final List<ScanResult> scanResults = scanPackageTracedLI(
+                            prepareResult.packageToScan, prepareResult.parseFlags,
+                            prepareResult.scanFlags, System.currentTimeMillis(),
+                            request.args.user);
+                    for (ScanResult result : scanResults) {
+                        if (null != scans.put(result.pkgSetting.pkg.packageName, result)) {
+                            request.installResult.setError(
+                                    PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
+                                    "Duplicate package " + result.pkgSetting.pkg.packageName
+                                            + " in atomic install request.");
+                            return;
+                        }
+                    }
+                } catch (PackageManagerException e) {
+                    request.installResult.setError("Scanning Failed.", e);
+                    return;
+                }
+            }
+            ReconcileRequest reconcileRequest = new ReconcileRequest(scans, installArgs,
+                    installResults,
+                    prepareResults);
+            CommitRequest commitRequest = null;
+            synchronized (mPackages) {
+                Map<String, ReconciledPackage> reconciledPackages;
+                try {
+                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
+                    reconciledPackages = reconcilePackagesLocked(reconcileRequest);
+                } catch (ReconcileFailure e) {
+                    for (InstallRequest request : requests) {
+                        // TODO(b/109941548): add more concrete failure reasons
+                        request.installResult.setError("Reconciliation failed...", e);
+                        // TODO: return any used system resources
+                    }
+                    return;
+                } finally {
+                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+                }
+                try {
+                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
+                    commitRequest = new CommitRequest(reconciledPackages,
+                            sUserManager.getUserIds());
+                    if (!commitPackagesLocked(commitRequest)) {
+                        cleanUpCommitFailuresLocked(commitRequest);
+                        return;
+                    }
+                } finally {
+                    for (PrepareResult result : prepareResults.values()) {
+                        if (result.freezer != null) {
+                            result.freezer.close();
+                        }
+                    }
+                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+                }
+            }
+            executePostCommitSteps(commitRequest);
+        } finally {
+            for (PrepareResult result : prepareResults.values()) {
+                if (result.freezer != null) {
+                    result.freezer.close();
+                }
+            }
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
-        CommitRequest request = new CommitRequest(reconciledPackages);
-        if (!commitPackagesLocked(request)) {
-            // TODO(b/109941548): set install args error
-            return;
-        }
-        // TODO(b/109941548) post-commit actions (dex-opt, etc.)
     }
 
-    @Deprecated
+    /**
+     * On successful install, executes remaining steps after commit completes and the package lock
+     * is released.
+     */
+    private void executePostCommitSteps(CommitRequest commitRequest) {
+        for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
+            final boolean forwardLocked =
+                    ((reconciledPkg.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
+            final boolean instantApp =
+                    ((reconciledPkg.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
+            final PackageParser.Package pkg = reconciledPkg.pkgSetting.pkg;
+            final String packageName = pkg.packageName;
+            prepareAppDataAfterInstallLIF(pkg);
+            if (reconciledPkg.prepareResult.clearCodeCache) {
+                clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
+                        | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+            }
+            if (reconciledPkg.prepareResult.replace) {
+                mDexManager.notifyPackageUpdated(pkg.packageName,
+                        pkg.baseCodePath, pkg.splitCodePaths);
+            }
+
+            // Prepare the application profiles for the new code paths.
+            // This needs to be done before invoking dexopt so that any install-time profile
+            // can be used for optimizations.
+            mArtManagerService.prepareAppProfiles(
+                    pkg, resolveUserIds(reconciledPkg.installForUser.getIdentifier()));
+
+            // Check whether we need to dexopt the app.
+            //
+            // NOTE: it is IMPORTANT to call dexopt:
+            //   - after doRename which will sync the package data from PackageParser.Package and
+            //     its corresponding ApplicationInfo.
+            //   - after installNewPackageLIF or replacePackageLIF which will update result with the
+            //     uid of the application (pkg.applicationInfo.uid).
+            //     This update happens in place!
+            //
+            // We only need to dexopt if the package meets ALL of the following conditions:
+            //   1) it is not forward locked.
+            //   2) it is not on on an external ASEC container.
+            //   3) it is not an instant app or if it is then dexopt is enabled via gservices.
+            //   4) it is not debuggable.
+            //
+            // Note that we do not dexopt instant apps by default. dexopt can take some time to
+            // complete, so we skip this step during installation. Instead, we'll take extra time
+            // the first time the instant app starts. It's preferred to do it this way to provide
+            // continuous progress to the useur instead of mysteriously blocking somewhere in the
+            // middle of running an instant app. The default behaviour can be overridden
+            // via gservices.
+            final boolean performDexopt = !forwardLocked
+                    && !pkg.applicationInfo.isExternalAsec()
+                    && (!instantApp || Global.getInt(mContext.getContentResolver(),
+                    Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
+                    && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
+
+            if (performDexopt) {
+                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+                // Do not run PackageDexOptimizer through the local performDexOpt
+                // method because `pkg` may not be in `mPackages` yet.
+                //
+                // Also, don't fail application installs if the dexopt step fails.
+                DexoptOptions dexoptOptions = new DexoptOptions(packageName,
+                        REASON_INSTALL,
+                        DexoptOptions.DEXOPT_BOOT_COMPLETE
+                                | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
+                mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
+                        null /* instructionSets */,
+                        getOrCreateCompilerPackageStats(pkg),
+                        mDexManager.getPackageUseInfoOrDefault(packageName),
+                        dexoptOptions);
+                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+            }
+
+            // Notify BackgroundDexOptService that the package has been changed.
+            // If this is an update of a package which used to fail to compile,
+            // BackgroundDexOptService will remove it from its blacklist.
+            // TODO: Layering violation
+            BackgroundDexOptService.notifyPackageChanged(packageName);
+        }
+
+    }
+
+    private void cleanUpCommitFailuresLocked(CommitRequest request) {
+        final Map<String, ReconciledPackage> reconciledPackages = request.reconciledPackages;
+        final int[] allUsers = request.mAllUsers;
+        for (ReconciledPackage reconciledPackage : reconciledPackages.values()) {
+            final String pkgName1 = reconciledPackage.pkgSetting.pkg.packageName;
+            if (reconciledPackage.installResult.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+                reconciledPackage.installResult.setError(
+                        PackageManager.INSTALL_FAILED_INTERNAL_ERROR, "Commit failed...");
+            }
+            final PackageParser.Package oldPackage =
+                    reconciledPackage.prepareResult.existingPackage;
+            final PackageParser.Package newPackage = reconciledPackage.pkgSetting.pkg;
+            if (reconciledPackage.prepareResult.system) {
+                // Re installation failed. Restore old information
+                // Remove new pkg information
+                if (newPackage != null) {
+                    removeInstalledPackageLI(newPackage, true);
+                }
+                // Add back the old system package
+                PackageParser.Package restoredPkg = null;
+                try {
+                    final List<ScanResult> restoreResults = scanPackageTracedLI(oldPackage,
+                            reconciledPackage.prepareResult.parseFlags, SCAN_UPDATE_SIGNATURE, 0,
+                            reconciledPackage.installForUser);
+                    commitSuccessfulScanResults(restoreResults);
+                    restoredPkg = restoreResults.get(0).pkgSetting.pkg;
+                } catch (PackageManagerException e) {
+                    Slog.e(TAG, "Failed to restore original package: " + e.getMessage());
+                }
+                synchronized (mPackages) {
+                    final boolean disabledSystem;
+                    // Remove existing system package
+                    removePackageLI(reconciledPackage.scanResult.pkgSetting.pkg, true);
+                    disabledSystem = disableSystemPackageLPw(
+                            reconciledPackage.scanResult.pkgSetting.pkg, restoredPkg);
+                    if (disabledSystem) {
+                        enableSystemPackageLPw(restoredPkg);
+                    }
+                    // Ensure the installer package name up to date
+                    setInstallerPackageNameLPw(reconciledPackage.scanResult.pkgSetting.pkg,
+                            reconciledPackage.installArgs.installerPackageName);
+                    // Update permissions for restored package
+                    mPermissionManager.updatePermissions(
+                            restoredPkg.packageName, restoredPkg, false, mPackages.values(),
+                            mPermissionCallback);
+                    mSettings.writeLPr();
+                }
+                Slog.i(TAG, "Successfully restored package : " + restoredPkg.packageName
+                        + " after failed upgrade");
+            } else if (reconciledPackage.prepareResult.replace) {
+                if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, rolling pack: " + pkgName1);
+
+                // Revert all internal state mutations and added folders for the failed install
+                boolean deletedPkg = deletePackageLIF(pkgName1, null, true,
+                        allUsers, /*TODO: deleteFlags*/ 0,
+                        reconciledPackage.installResult.removedInfo, true, null);
+
+                // Restore the old package
+                if (deletedPkg) {
+                    if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + oldPackage);
+                    File restoreFile = new File(oldPackage.codePath);
+                    // Parse old package
+                    boolean oldExternal = isExternal(oldPackage);
+                    int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
+                            | (oldPackage.isForwardLocked() ? PackageParser.PARSE_FORWARD_LOCK : 0)
+                            | (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
+                    int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
+                    try {
+                        scanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags,
+                                /* origUpdateTime */ System.currentTimeMillis(), null);
+                    } catch (PackageManagerException e) {
+                        Slog.e(TAG, "Failed to restore package : " + pkgName1
+                                + " after failed upgrade: "
+                                + e.getMessage());
+                        return;
+                    }
+
+                    synchronized (mPackages) {
+                        // Ensure the installer package name up to date
+                        setInstallerPackageNameLPw(oldPackage,
+                                reconciledPackage.installArgs.installerPackageName);
+
+                        // Update permissions for restored package
+                        mPermissionManager.updatePermissions(
+                                oldPackage.packageName, oldPackage, false, mPackages.values(),
+                                mPermissionCallback);
+
+                        mSettings.writeLPr();
+                    }
+
+                    Slog.i(TAG, "Successfully restored package : " + pkgName1
+                            + " after failed upgrade");
+                }
+            } else {
+                // Remove package from internal structures, but keep around any
+                // data that might have already existed
+                deletePackageLIF(pkgName1, UserHandle.ALL, false, null,
+                        PackageManager.DELETE_KEEP_DATA,
+                        reconciledPackage.installResult.removedInfo, true, null);
+            }
+        }
+    }
+
+    /**
+     * The set of data needed to successfully install the prepared package. This includes data that
+     * will be used to scan and reconcile the package.
+     */
+    private static class PrepareResult {
+        public final int installReason;
+        public final String volumeUuid;
+        public final String installerPackageName;
+        public final UserHandle user;
+        public final boolean replace;
+        public final int scanFlags;
+        public final int parseFlags;
+        @Nullable /* The original Package if it is being replaced, otherwise {@code null} */
+        public final PackageParser.Package existingPackage;
+        public final PackageParser.Package packageToScan;
+        public final boolean clearCodeCache;
+        public final boolean system;
+        /* The original package name if it was changed during an update, otherwise {@code null}. */
+        @Nullable
+        public final String renamedPackage;
+        public final PackageFreezer freezer;
+
+        private PrepareResult(int installReason, String volumeUuid,
+                String installerPackageName, UserHandle user, boolean replace, int scanFlags,
+                int parseFlags, PackageParser.Package existingPackage,
+                PackageParser.Package packageToScan, boolean clearCodeCache, boolean system,
+                String renamedPackage,
+                PackageFreezer freezer) {
+            this.installReason = installReason;
+            this.volumeUuid = volumeUuid;
+            this.installerPackageName = installerPackageName;
+            this.user = user;
+            this.replace = replace;
+            this.scanFlags = scanFlags;
+            this.parseFlags = parseFlags;
+            this.existingPackage = existingPackage;
+            this.packageToScan = packageToScan;
+            this.clearCodeCache = clearCodeCache;
+            this.system = system;
+            this.renamedPackage = renamedPackage;
+            this.freezer = freezer;
+        }
+    }
+
+    private static class PrepareFailure extends PackageManagerException {
+
+        public String conflictingPackage;
+        public String conflictingPermission;
+
+        PrepareFailure(int error) {
+            super(error, "Failed to prepare for install.");
+        }
+
+        PrepareFailure(int error, String detailMessage) {
+            super(error, detailMessage);
+        }
+
+        PrepareFailure(String message, Exception e) {
+            super(e instanceof PackageParserException
+                    ? ((PackageParserException) e).error
+                    : ((PackageManagerException) e).error,
+                    ExceptionUtils.getCompleteMessage(message, e));
+        }
+
+        PrepareFailure conflictsWithExistingPermission(String conflictingPermission,
+                String conflictingPackage) {
+            this.conflictingPermission = conflictingPermission;
+            this.conflictingPackage = conflictingPackage;
+            return this;
+        }
+    }
+
     @GuardedBy("mInstallLock")
-    private void preparePackageLI(InstallArgs args, PackageInstalledInfo res) {
+    private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
+            throws PrepareFailure {
         final int installFlags = args.installFlags;
         final String installerPackageName = args.installerPackageName;
         final String volumeUuid = args.volumeUuid;
@@ -15494,7 +15582,6 @@
         final boolean forceSdk = ((installFlags & PackageManager.INSTALL_FORCE_SDK) != 0);
         final boolean virtualPreload =
                 ((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
-        boolean replace = false;
         @ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
         if (args.move != null) {
             // moving a complete application; perform an initial scan on the new install location
@@ -15513,18 +15600,13 @@
             scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
         }
 
-        // Result object to be returned
-        res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
-        res.installerPackageName = installerPackageName;
-
         if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
 
         // Sanity check
         if (instantApp && (forwardLocked || onExternal)) {
             Slog.i(TAG, "Incompatible ephemeral install; fwdLocked=" + forwardLocked
                     + " external=" + onExternal);
-            res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
-            return;
+            throw new PrepareFailure(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
         }
 
         // Retrieve PackageSettings and parse package
@@ -15533,6 +15615,7 @@
                 | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
                 | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)
                 | (forceSdk ? PackageParser.PARSE_FORCE_SDK : 0);
+
         PackageParser pp = new PackageParser();
         pp.setSeparateProcesses(mSeparateProcesses);
         pp.setDisplayMetrics(mMetrics);
@@ -15544,8 +15627,7 @@
             pkg = pp.parsePackage(tmpPackageFile, parseFlags);
             DexMetadataHelper.validatePackageDexMetadata(pkg);
         } catch (PackageParserException e) {
-            res.setError("Failed parse during installPackageLI", e);
-            return;
+            throw new PrepareFailure("Failed parse during installPackageLI", e);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
@@ -15555,16 +15637,14 @@
             if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
                 Slog.w(TAG,
                         "Instant app package " + pkg.packageName + " does not target at least O");
-                res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
+                throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
                         "Instant app package must target at least O");
-                return;
             }
             if (pkg.mSharedUserId != null) {
                 Slog.w(TAG, "Instant app package " + pkg.packageName
                         + " may not declare sharedUserId.");
-                res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
+                throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
                         "Instant app package may not declare a sharedUserId");
-                return;
             }
         }
 
@@ -15575,9 +15655,8 @@
             // No static shared libs on external storage
             if (onExternal) {
                 Slog.i(TAG, "Static shared libs can only be installed on internal storage.");
-                res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+                throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
                         "Packages declaring static-shared libs cannot be updated");
-                return;
             }
         }
 
@@ -15616,10 +15695,9 @@
         }
 
         String pkgName = res.name = pkg.packageName;
-        if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
+        if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0) {
             if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {
-                res.setError(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
-                return;
+                throw new PrepareFailure(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
             }
         }
 
@@ -15631,22 +15709,21 @@
                 PackageParser.collectCertificates(pkg, false /* skipVerify */);
             }
         } catch (PackageParserException e) {
-            res.setError("Failed collect during installPackageLI", e);
-            return;
+            throw new PrepareFailure("Failed collect during installPackageLI", e);
         }
 
         if (instantApp && pkg.mSigningDetails.signatureSchemeVersion
                 < SignatureSchemeVersion.SIGNING_BLOCK_V2) {
             Slog.w(TAG, "Instant app package " + pkg.packageName
                     + " is not signed with at least APK Signature Scheme v2");
-            res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
+            throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
                     "Instant app package must be signed with APK Signature Scheme v2 or greater");
-            return;
         }
 
         // Get rid of all references to package scan path via parser.
         pp = null;
         boolean systemApp = false;
+        boolean replace = false;
         synchronized (mPackages) {
             // Check if installing already existing package
             if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
@@ -15661,8 +15738,10 @@
                     pkg.setPackageName(oldName);
                     pkgName = pkg.packageName;
                     replace = true;
-                    if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName="
-                            + oldName + " pkgName=" + pkgName);
+                    if (DEBUG_INSTALL) {
+                        Slog.d(TAG, "Replacing existing renamed package: oldName="
+                                + oldName + " pkgName=" + pkgName);
+                    }
                 } else if (mPackages.containsKey(pkgName)) {
                     // This package, under its official name, already exists
                     // on the device; we should replace it.
@@ -15672,11 +15751,11 @@
 
                 // Child packages are installed through the parent package
                 if (pkg.parentPackage != null) {
-                    res.setError(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+                    throw new PrepareFailure(
+                            PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
                             "Package " + pkg.packageName + " is child of package "
                                     + pkg.parentPackage.parentPackage + ". Child packages "
                                     + "can be updated only through the parent package.");
-                    return;
                 }
 
                 if (replace) {
@@ -15686,26 +15765,25 @@
                     final int newTargetSdk = pkg.applicationInfo.targetSdkVersion;
                     if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1
                             && newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) {
-                        res.setError(PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,
+                        throw new PrepareFailure(
+                                PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,
                                 "Package " + pkg.packageName + " new target SDK " + newTargetSdk
                                         + " doesn't support runtime permissions but the old"
                                         + " target SDK " + oldTargetSdk + " does.");
-                        return;
                     }
                     // Prevent persistent apps from being updated
                     if ((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0) {
-                        res.setError(PackageManager.INSTALL_FAILED_INVALID_APK,
+                        throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK,
                                 "Package " + oldPackage.packageName + " is a persistent app. "
                                         + "Persistent apps are not updateable.");
-                        return;
                     }
                     // Prevent installing of child packages
                     if (oldPackage.parentPackage != null) {
-                        res.setError(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+                        throw new PrepareFailure(
+                                PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
                                 "Package " + pkg.packageName + " is child of package "
                                         + oldPackage.parentPackage + ". Child packages "
                                         + "can be updated only through the parent package.");
-                        return;
                     }
                 }
             }
@@ -15732,10 +15810,9 @@
                 final KeySetManagerService ksms = mSettings.mKeySetManagerService;
                 if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
                     if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
-                        res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+                        throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
                                 + pkg.packageName + " upgrade keys do not match the "
                                 + "previously installed version");
-                        return;
                     }
                 } else {
                     try {
@@ -15752,8 +15829,7 @@
                             }
                         }
                     } catch (PackageManagerException e) {
-                        res.setError(e.error, e.getMessage());
-                        return;
+                        throw new PrepareFailure(e.error, e.getMessage());
                     }
                 }
 
@@ -15764,8 +15840,9 @@
                 res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
             }
 
+
             int N = pkg.permissions.size();
-            for (int i = N-1; i >= 0; i--) {
+            for (int i = N - 1; i >= 0; i--) {
                 final PackageParser.Permission perm = pkg.permissions.get(i);
                 final BasePermission bp =
                         (BasePermission) mPermissionManager.getPermissionTEMP(perm.info.name);
@@ -15790,7 +15867,7 @@
                     final KeySetManagerService ksms = mSettings.mKeySetManagerService;
                     if (sourcePackageName.equals(pkg.packageName)
                             && (ksms.shouldCheckUpgradeKeySetLocked(
-                                    sourcePackageSetting, scanFlags))) {
+                            sourcePackageSetting, scanFlags))) {
                         sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, pkg);
                     } else {
 
@@ -15798,12 +15875,12 @@
                         // package's certificate has rotated from the current one, or if it is an
                         // older certificate with which the current is ok with sharing permissions
                         if (sourcePackageSetting.signatures.mSigningDetails.checkCapability(
-                                        pkg.mSigningDetails,
-                                        PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+                                pkg.mSigningDetails,
+                                PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
                             sigsOk = true;
                         } else if (pkg.mSigningDetails.checkCapability(
-                                        sourcePackageSetting.signatures.mSigningDetails,
-                                        PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+                                sourcePackageSetting.signatures.mSigningDetails,
+                                PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
 
                             // the scanned package checks out, has signing certificate rotation
                             // history, and is newer; bring it over
@@ -15818,12 +15895,13 @@
                         // install to proceed; we fail the install on all other permission
                         // redefinitions.
                         if (!sourcePackageName.equals("android")) {
-                            res.setError(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "
-                                    + pkg.packageName + " attempting to redeclare permission "
-                                    + perm.info.name + " already owned by " + sourcePackageName);
-                            res.origPermission = perm.info.name;
-                            res.origPackage = sourcePackageName;
-                            return;
+                            throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "
+                                    + pkg.packageName
+                                    + " attempting to redeclare permission "
+                                    + perm.info.name + " already owned by "
+                                    + sourcePackageName)
+                                    .conflictsWithExistingPermission(perm.info.name,
+                                            sourcePackageName);
                         } else {
                             Slog.w(TAG, "Package " + pkg.packageName
                                     + " attempting to redeclare system permission "
@@ -15852,14 +15930,12 @@
         if (systemApp) {
             if (onExternal) {
                 // Abort update; system app can't be replaced with app on sdcard
-                res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+                throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
                         "Cannot install updates to system apps on sdcard");
-                return;
             } else if (instantApp) {
                 // Abort update; system app can't be replaced with an instant app
-                res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
+                throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
                         "Cannot update a system app with an instant app");
-                return;
             }
         }
 
@@ -15887,13 +15963,13 @@
 
             try {
                 String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ?
-                    args.abiOverride : pkg.cpuAbiOverride);
+                        args.abiOverride : pkg.cpuAbiOverride);
                 final boolean extractNativeLibs = !pkg.isLibrary();
                 derivePackageAbi(pkg, abiOverride, extractNativeLibs);
             } catch (PackageManagerException pme) {
                 Slog.e(TAG, "Error deriving application ABI", pme);
-                res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
-                return;
+                throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
+                        "Error deriving application ABI");
             }
 
             // Shared libraries for the package need to be updated.
@@ -15907,8 +15983,7 @@
         }
 
         if (!args.doRename(res.returnCode, pkg)) {
-            res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
-            return;
+            throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
         }
 
         if (PackageManagerServiceUtils.isApkVerityEnabled()) {
@@ -15922,7 +15997,6 @@
                     apkPath = pkg.baseCodePath;
                 }
             }
-
             if (apkPath != null) {
                 final VerityUtils.SetupResult result =
                         VerityUtils.generateApkVeritySetupData(apkPath);
@@ -15934,16 +16008,15 @@
                         mInstaller.installApkVerity(apkPath, fd, result.getContentSize());
                         mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash);
                     } catch (InstallerException | IOException | DigestException |
-                             NoSuchAlgorithmException e) {
-                        res.setError(INSTALL_FAILED_INTERNAL_ERROR,
+                            NoSuchAlgorithmException e) {
+                        throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
                                 "Failed to set up verity: " + e);
-                        return;
                     } finally {
                         IoUtils.closeQuietly(fd);
                     }
                 } else if (result.isFailed()) {
-                    res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Failed to generate verity");
-                    return;
+                    throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
+                            "Failed to generate verity");
                 } else {
                     // Do nothing if verity is skipped. Will fall back to full apk verification on
                     // reboot.
@@ -15958,109 +16031,293 @@
                 Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName);
             }
         }
+        final PackageFreezer freezer =
+                freezePackageForInstall(pkgName, installFlags, "installPackageLI");
+        boolean shouldCloseFreezerBeforeReturn = true;
+        try {
+            final PackageParser.Package existingPackage;
+            String renamedPackage = null;
+            boolean clearCodeCache = false;
+            boolean sysPkg = false;
+            String targetVolumeUuid = volumeUuid;
+            int targetScanFlags = scanFlags;
+            int targetParseFlags = parseFlags;
 
-        try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
-                "installPackageLI")) {
             if (replace) {
+                targetVolumeUuid = null;
                 if (pkg.applicationInfo.isStaticSharedLibrary()) {
                     // Static libs have a synthetic package name containing the version
                     // and cannot be updated as an update would get a new package name,
                     // unless this is the exact same version code which is useful for
                     // development.
                     PackageParser.Package existingPkg = mPackages.get(pkg.packageName);
-                    if (existingPkg != null &&
-                            existingPkg.getLongVersionCode() != pkg.getLongVersionCode()) {
-                        res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring "
-                                + "static-shared libs cannot be updated");
-                        return;
+                    if (existingPkg != null
+                            && existingPkg.getLongVersionCode() != pkg.getLongVersionCode()) {
+                        throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PACKAGE,
+                                "Packages declaring "
+                                        + "static-shared libs cannot be updated");
                     }
                 }
-                replacePackageLIF(pkg, parseFlags, scanFlags, args.user,
-                        installerPackageName, res, args.installReason);
-            } else {
-                installNewPackageLIF(pkg, parseFlags, scanFlags,
-                        args.user, installerPackageName, volumeUuid, res, args.installReason);
-            }
-        }
 
-        // Prepare the application profiles for the new code paths.
-        // This needs to be done before invoking dexopt so that any install-time profile
-        // can be used for optimizations.
-        mArtManagerService.prepareAppProfiles(pkg, resolveUserIds(args.user.getIdentifier()));
+                final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
 
-        // Check whether we need to dexopt the app.
-        //
-        // NOTE: it is IMPORTANT to call dexopt:
-        //   - after doRename which will sync the package data from PackageParser.Package and its
-        //     corresponding ApplicationInfo.
-        //   - after installNewPackageLIF or replacePackageLIF which will update result with the
-        //     uid of the application (pkg.applicationInfo.uid).
-        //     This update happens in place!
-        //
-        // We only need to dexopt if the package meets ALL of the following conditions:
-        //   1) it is not forward locked.
-        //   2) it is not on on an external ASEC container.
-        //   3) it is not an instant app or if it is then dexopt is enabled via gservices.
-        //   4) it is not debuggable.
-        //
-        // Note that we do not dexopt instant apps by default. dexopt can take some time to
-        // complete, so we skip this step during installation. Instead, we'll take extra time
-        // the first time the instant app starts. It's preferred to do it this way to provide
-        // continuous progress to the useur instead of mysteriously blocking somewhere in the
-        // middle of running an instant app. The default behaviour can be overridden
-        // via gservices.
-        final boolean performDexopt = (res.returnCode == PackageManager.INSTALL_SUCCEEDED)
-                && !forwardLocked
-                && !pkg.applicationInfo.isExternalAsec()
-                && (!instantApp || Global.getInt(mContext.getContentResolver(),
-                Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
-                && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
+                final PackageParser.Package oldPackage;
+                final PackageSetting ps;
+                final String pkgName11 = pkg.packageName;
+                final int[] allUsers;
+                final int[] installedUsers;
 
-        if (performDexopt) {
-            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
-            // Do not run PackageDexOptimizer through the local performDexOpt
-            // method because `pkg` may not be in `mPackages` yet.
-            //
-            // Also, don't fail application installs if the dexopt step fails.
-            DexoptOptions dexoptOptions = new DexoptOptions(pkg.packageName,
-                    REASON_INSTALL,
-                    DexoptOptions.DEXOPT_BOOT_COMPLETE |
-                    DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
-            mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
-                    null /* instructionSets */,
-                    getOrCreateCompilerPackageStats(pkg),
-                    mDexManager.getPackageUseInfoOrDefault(pkg.packageName),
-                    dexoptOptions);
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-        }
+                synchronized (mPackages) {
+                    oldPackage = mPackages.get(pkgName11);
+                    existingPackage = oldPackage;
+                    if (DEBUG_INSTALL) {
+                        Slog.d(TAG,
+                                "replacePackageLI: new=" + pkg + ", old=" + oldPackage);
+                    }
 
-        // Notify BackgroundDexOptService that the package has been changed.
-        // If this is an update of a package which used to fail to compile,
-        // BackgroundDexOptService will remove it from its blacklist.
-        // TODO: Layering violation
-        BackgroundDexOptService.notifyPackageChanged(pkg.packageName);
+                    // don't allow upgrade to target a release SDK from a pre-release SDK
+                    final boolean oldTargetsPreRelease = oldPackage.applicationInfo.targetSdkVersion
+                            == Build.VERSION_CODES.CUR_DEVELOPMENT;
+                    final boolean newTargetsPreRelease = pkg.applicationInfo.targetSdkVersion
+                            == Build.VERSION_CODES.CUR_DEVELOPMENT;
+                    if (oldTargetsPreRelease
+                            && !newTargetsPreRelease
+                            && ((parseFlags & PackageParser.PARSE_FORCE_SDK) == 0)) {
+                        Slog.w(TAG, "Can't install package targeting released sdk");
+                        throw new PrepareFailure(
+                                PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE);
+                    }
 
-        synchronized (mPackages) {
-            final PackageSetting ps = mSettings.mPackages.get(pkgName);
-            if (ps != null) {
-                res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
-                ps.setUpdateAvailable(false /*updateAvailable*/);
-            }
+                    ps = mSettings.mPackages.get(pkgName11);
 
-            final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
-            for (int i = 0; i < childCount; i++) {
-                PackageParser.Package childPkg = pkg.childPackages.get(i);
-                PackageInstalledInfo childRes = res.addedChildPackages.get(childPkg.packageName);
-                PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
-                if (childPs != null) {
-                    childRes.newUsers = childPs.queryInstalledUsers(
-                            sUserManager.getUserIds(), true);
+                    // verify signatures are valid
+                    final KeySetManagerService ksms = mSettings.mKeySetManagerService;
+                    if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) {
+                        if (!ksms.checkUpgradeKeySetLocked(ps, pkg)) {
+                            throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+                                    "New package not signed by keys specified by upgrade-keysets: "
+                                            + pkgName11);
+                        }
+                    } else {
+                        // default to original signature matching
+                        if (!pkg.mSigningDetails.checkCapability(oldPackage.mSigningDetails,
+                                SigningDetails.CertCapabilities.INSTALLED_DATA)
+                                && !oldPackage.mSigningDetails.checkCapability(
+                                pkg.mSigningDetails,
+                                SigningDetails.CertCapabilities.ROLLBACK)) {
+                            throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+                                    "New package has a different signature: " + pkgName11);
+                        }
+                    }
+
+                    // don't allow a system upgrade unless the upgrade hash matches
+                    if (oldPackage.restrictUpdateHash != null && oldPackage.isSystem()) {
+                        final byte[] digestBytes;
+                        try {
+                            final MessageDigest digest = MessageDigest.getInstance("SHA-512");
+                            updateDigest(digest, new File(pkg.baseCodePath));
+                            if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
+                                for (String path : pkg.splitCodePaths) {
+                                    updateDigest(digest, new File(path));
+                                }
+                            }
+                            digestBytes = digest.digest();
+                        } catch (NoSuchAlgorithmException | IOException e) {
+                            throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
+                                    "Could not compute hash: " + pkgName11);
+                        }
+                        if (!Arrays.equals(oldPackage.restrictUpdateHash, digestBytes)) {
+                            throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
+                                    "New package fails restrict-update check: " + pkgName11);
+                        }
+                        // retain upgrade restriction
+                        pkg.restrictUpdateHash = oldPackage.restrictUpdateHash;
+                    }
+
+                    // Check for shared user id changes
+                    String invalidPackageName =
+                            getParentOrChildPackageChangedSharedUser(oldPackage, pkg);
+                    if (invalidPackageName != null) {
+                        throw new PrepareFailure(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
+                                "Package " + invalidPackageName + " tried to change user "
+                                        + oldPackage.mSharedUserId);
+                    }
+
+                    // check if the new package supports all of the abis which the old package
+                    // supports
+                    boolean oldPkgSupportMultiArch =
+                            oldPackage.applicationInfo.secondaryCpuAbi != null;
+                    boolean newPkgSupportMultiArch = pkg.applicationInfo.secondaryCpuAbi != null;
+                    if (isSystemApp(oldPackage) && oldPkgSupportMultiArch
+                            && !newPkgSupportMultiArch) {
+                        throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+                                "Update to package " + pkgName11 + " doesn't support multi arch");
+                    }
+
+                    // In case of rollback, remember per-user/profile install state
+                    allUsers = sUserManager.getUserIds();
+                    installedUsers = ps.queryInstalledUsers(allUsers, true);
+
+
+                    // don't allow an upgrade from full to ephemeral
+                    if (isInstantApp) {
+                        if (args.user == null || args.user.getIdentifier() == UserHandle.USER_ALL) {
+                            for (int currentUser : allUsers) {
+                                if (!ps.getInstantApp(currentUser)) {
+                                    // can't downgrade from full to instant
+                                    Slog.w(TAG,
+                                            "Can't replace full app with instant app: " + pkgName11
+                                                    + " for user: " + currentUser);
+                                    throw new PrepareFailure(
+                                            PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
+                                }
+                            }
+                        } else if (!ps.getInstantApp(args.user.getIdentifier())) {
+                            // can't downgrade from full to instant
+                            Slog.w(TAG, "Can't replace full app with instant app: " + pkgName11
+                                    + " for user: " + args.user.getIdentifier());
+                            throw new PrepareFailure(
+                                    PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
+                        }
+                    }
+                }
+
+                // Update what is removed
+                res.removedInfo = new PackageRemovedInfo(this);
+                res.removedInfo.uid = oldPackage.applicationInfo.uid;
+                res.removedInfo.removedPackage = oldPackage.packageName;
+                res.removedInfo.installerPackageName = ps.installerPackageName;
+                res.removedInfo.isStaticSharedLib = pkg.staticSharedLibName != null;
+                res.removedInfo.isUpdate = true;
+                res.removedInfo.origUsers = installedUsers;
+                res.removedInfo.installReasons = new SparseArray<>(installedUsers.length);
+                for (int i = 0; i < installedUsers.length; i++) {
+                    final int userId = installedUsers[i];
+                    res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId));
+                }
+
+                final int childCount = (oldPackage.childPackages != null)
+                        ? oldPackage.childPackages.size() : 0;
+                for (int i = 0; i < childCount; i++) {
+                    boolean childPackageUpdated = false;
+                    PackageParser.Package childPkg = oldPackage.childPackages.get(i);
+                    final PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
+                    if (res.addedChildPackages != null) {
+                        PackageInstalledInfo childRes = res.addedChildPackages.get(
+                                childPkg.packageName);
+                        if (childRes != null) {
+                            childRes.removedInfo.uid = childPkg.applicationInfo.uid;
+                            childRes.removedInfo.removedPackage = childPkg.packageName;
+                            if (childPs != null) {
+                                childRes.removedInfo.installerPackageName =
+                                        childPs.installerPackageName;
+                            }
+                            childRes.removedInfo.isUpdate = true;
+                            childRes.removedInfo.installReasons = res.removedInfo.installReasons;
+                            childPackageUpdated = true;
+                        }
+                    }
+                    if (!childPackageUpdated) {
+                        PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this);
+                        childRemovedRes.removedPackage = childPkg.packageName;
+                        if (childPs != null) {
+                            childRemovedRes.installerPackageName = childPs.installerPackageName;
+                        }
+                        childRemovedRes.isUpdate = false;
+                        childRemovedRes.dataRemoved = true;
+                        synchronized (mPackages) {
+                            if (childPs != null) {
+                                childRemovedRes.origUsers = childPs.queryInstalledUsers(allUsers,
+                                        true);
+                            }
+                        }
+                        if (res.removedInfo.removedChildPackages == null) {
+                            res.removedInfo.removedChildPackages = new ArrayMap<>();
+                        }
+                        res.removedInfo.removedChildPackages.put(childPkg.packageName,
+                                childRemovedRes);
+                    }
+                }
+
+                sysPkg = (isSystemApp(oldPackage));
+                if (sysPkg) {
+                    // Set the system/privileged/oem/vendor/product flags as needed
+                    final boolean privileged = isPrivilegedApp(oldPackage);
+                    final boolean oem = isOemApp(oldPackage);
+                    final boolean vendor = isVendorApp(oldPackage);
+                    final boolean product = isProductApp(oldPackage);
+                    final @ParseFlags int systemParseFlags = parseFlags;
+                    final @ScanFlags int systemScanFlags = scanFlags
+                            | SCAN_AS_SYSTEM
+                            | (privileged ? SCAN_AS_PRIVILEGED : 0)
+                            | (oem ? SCAN_AS_OEM : 0)
+                            | (vendor ? SCAN_AS_VENDOR : 0)
+                            | (product ? SCAN_AS_PRODUCT : 0);
+
+                    if (DEBUG_INSTALL) {
+                        Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
+                                + ", old=" + oldPackage);
+                    }
+                    clearCodeCache = true;
+                    res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
+                    pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
+                            ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
+                    targetParseFlags = systemParseFlags;
+                    targetScanFlags = systemScanFlags;
+                } else { // non system replace
+                    replace = true;
+                    if (DEBUG_INSTALL) {
+                        Slog.d(TAG,
+                                "replaceNonSystemPackageLI: new=" + pkg + ", old="
+                                        + oldPackage);
+                    }
+
+                    String pkgName1 = oldPackage.packageName;
+                    boolean deletedPkg = true;
+                    boolean addedPkg = false;
+                    boolean updatedSettings = false;
+
+                    final long origUpdateTime = (pkg.mExtras != null)
+                            ? ((PackageSetting) pkg.mExtras).lastUpdateTime : 0;
+
+                }
+            } else { // new package install
+                replace = false;
+                existingPackage = null;
+                // Remember this for later, in case we need to rollback this install
+                String pkgName1 = pkg.packageName;
+
+                if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
+
+                // TODO(patb): MOVE TO RECONCILE
+                synchronized (mPackages) {
+                    renamedPackage = mSettings.getRenamedPackageLPr(pkgName1);
+                    if (renamedPackage != null) {
+                        // A package with the same name is already installed, though
+                        // it has been renamed to an older name.  The package we
+                        // are trying to install should be installed as an update to
+                        // the existing one, but that has not been requested, so bail.
+                        throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS,
+                                "Attempt to re-install " + pkgName1
+                                        + " without first uninstalling package running as "
+                                        + renamedPackage);
+                    }
+                    if (mPackages.containsKey(pkgName1)) {
+                        // Don't allow installation over an existing package with the same name.
+                        throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS,
+                                "Attempt to re-install " + pkgName1
+                                        + " without first uninstalling.");
+                    }
                 }
             }
-
-            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
-                updateSequenceNumberLP(ps, res.newUsers);
-                updateInstantAppInstallerLocked(pkgName);
+            // we're passing the freezer back to be closed in a later phase of install
+            shouldCloseFreezerBeforeReturn = false;
+            return new PrepareResult(args.installReason, targetVolumeUuid, installerPackageName,
+                    args.user, replace, targetScanFlags, targetParseFlags, existingPackage, pkg,
+                    clearCodeCache, sysPkg, renamedPackage, freezer);
+        } finally {
+            if (shouldCloseFreezerBeforeReturn) {
+                freezer.close();
             }
         }
     }
@@ -16870,7 +17127,7 @@
             }
         }
 
-        removePackageLI(ps, (flags & PackageManager.DELETE_CHATTY) != 0);
+        removePackageLI(ps.name, (flags & PackageManager.DELETE_CHATTY) != 0);
 
         if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
             final PackageParser.Package resolvedPkg;
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 727fb15..b850613 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -207,4 +207,13 @@
         writeUsersInfoToProto(proto, PackageProto.USERS);
         proto.end(packageToken);
     }
+
+    /** Updates all fields in the current setting from another. */
+    public void updateFrom(PackageSetting other) {
+        super.updateFrom(other);
+        appId = other.appId;
+        pkg = other.pkg;
+        sharedUserId = other.sharedUserId;
+        sharedUser = other.sharedUser;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 138594c..fd6aceb 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -26,7 +26,6 @@
 import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
 import android.content.pm.Signature;
-import android.os.BaseBundle;
 import android.os.PersistableBundle;
 import android.service.pm.PackageProto;
 import android.util.ArraySet;
@@ -109,7 +108,7 @@
 
     // Whether this package is currently stopped, thus can not be
     // started until explicitly launched by the user.
-    private final SparseArray<PackageUserState> userState = new SparseArray<PackageUserState>();
+    private final SparseArray<PackageUserState> mUserState = new SparseArray<>();
 
     /**
      * Non-persisted value. During an "upgrade without restart", we need the set
@@ -118,7 +117,7 @@
      * restart, this field will be cleared since the classloader would be created
      * using the full set of code paths when the package's process is started.
      */
-    Set<String> oldCodePaths;
+    Set<String> mOldCodePaths;
 
     /** Package name of the app that installed this package */
     String installerPackageName;
@@ -223,7 +222,7 @@
     /**
      * Makes a shallow copy of the given package settings.
      *
-     * NOTE: For some fields [such as keySetData, signatures, userState, verificationInfo, etc...],
+     * NOTE: For some fields [such as keySetData, signatures, mUserState, verificationInfo, etc...],
      * the original object is copied and a new one is not created.
      */
     public void copyFrom(PackageSettingBase orig) {
@@ -244,7 +243,7 @@
         keySetData = orig.keySetData;
         lastUpdateTime = orig.lastUpdateTime;
         legacyNativeLibraryPathString = orig.legacyNativeLibraryPathString;
-        // Intentionally skip oldCodePaths; it's not relevant for copies
+        // Intentionally skip mOldCodePaths; it's not relevant for copies
         parentPackageName = orig.parentPackageName;
         primaryCpuAbiString = orig.primaryCpuAbiString;
         resourcePath = orig.resourcePath;
@@ -253,9 +252,9 @@
         signatures = orig.signatures;
         timeStamp = orig.timeStamp;
         uidError = orig.uidError;
-        userState.clear();
-        for (int i=0; i<orig.userState.size(); i++) {
-            userState.put(orig.userState.keyAt(i), orig.userState.valueAt(i));
+        mUserState.clear();
+        for (int i = 0; i < orig.mUserState.size(); i++) {
+            mUserState.put(orig.mUserState.keyAt(i), orig.mUserState.valueAt(i));
         }
         verificationInfo = orig.verificationInfo;
         versionCode = orig.versionCode;
@@ -271,16 +270,16 @@
     }
 
     private PackageUserState modifyUserState(int userId) {
-        PackageUserState state = userState.get(userId);
+        PackageUserState state = mUserState.get(userId);
         if (state == null) {
             state = new PackageUserState();
-            userState.put(userId, state);
+            mUserState.put(userId, state);
         }
         return state;
     }
 
     public PackageUserState readUserState(int userId) {
-        PackageUserState state = userState.get(userId);
+        PackageUserState state = mUserState.get(userId);
         if (state == null) {
             return DEFAULT_USER_STATE;
         }
@@ -330,7 +329,7 @@
     /** Only use for testing. Do NOT use in production code. */
     @VisibleForTesting
     SparseArray<PackageUserState> getUserState() {
-        return userState;
+        return mUserState;
     }
 
     boolean isAnyInstalled(int[] users) {
@@ -536,14 +535,14 @@
     }
 
     void removeUser(int userId) {
-        userState.delete(userId);
+        mUserState.delete(userId);
     }
 
     public int[] getNotInstalledUserIds() {
         int count = 0;
-        int userStateCount = userState.size();
+        int userStateCount = mUserState.size();
         for (int i = 0; i < userStateCount; i++) {
-            if (userState.valueAt(i).installed == false) {
+            if (!mUserState.valueAt(i).installed) {
                 count++;
             }
         }
@@ -551,8 +550,8 @@
         int[] excludedUserIds = new int[count];
         int idx = 0;
         for (int i = 0; i < userStateCount; i++) {
-            if (userState.valueAt(i).installed == false) {
-                excludedUserIds[idx++] = userState.keyAt(i);
+            if (!mUserState.valueAt(i).installed) {
+                excludedUserIds[idx++] = mUserState.keyAt(i);
             }
         }
         return excludedUserIds;
@@ -591,11 +590,11 @@
     }
 
     protected void writeUsersInfoToProto(ProtoOutputStream proto, long fieldId) {
-        int count = userState.size();
+        int count = mUserState.size();
         for (int i = 0; i < count; i++) {
             final long userToken = proto.start(fieldId);
-            final int userId = userState.keyAt(i);
-            final PackageUserState state = userState.valueAt(i);
+            final int userId = mUserState.keyAt(i);
+            final PackageUserState state = mUserState.valueAt(i);
             proto.write(PackageProto.UserInfoProto.ID, userId);
             final int installType;
             if (state.instantApp) {
@@ -630,4 +629,48 @@
         PackageUserState userState = readUserState(userId);
         return userState.harmfulAppWarning;
     }
+
+    protected PackageSettingBase updateFrom(PackageSettingBase other) {
+        super.copyFrom(other);
+        this.parentPackageName = other.parentPackageName;
+        this.childPackageNames = other.childPackageNames;
+        this.codePath = other.codePath;
+        this.codePathString = other.codePathString;
+        this.resourcePath = other.resourcePath;
+        this.resourcePathString = other.resourcePathString;
+        this.usesStaticLibraries = other.usesStaticLibraries;
+        this.usesStaticLibrariesVersions = other.usesStaticLibrariesVersions;
+        this.legacyNativeLibraryPathString = other.legacyNativeLibraryPathString;
+        this.primaryCpuAbiString = other.primaryCpuAbiString;
+        this.secondaryCpuAbiString = other.secondaryCpuAbiString;
+        this.cpuAbiOverrideString = other.cpuAbiOverrideString;
+        this.timeStamp = other.timeStamp;
+        this.firstInstallTime = other.firstInstallTime;
+        this.lastUpdateTime = other.lastUpdateTime;
+        this.versionCode = other.versionCode;
+        this.uidError = other.uidError;
+        this.signatures = other.signatures;
+        this.installPermissionsFixed = other.installPermissionsFixed;
+        this.keySetData = other.keySetData;
+        this.installerPackageName = other.installerPackageName;
+        this.isOrphaned = other.isOrphaned;
+        this.volumeUuid = other.volumeUuid;
+        this.categoryHint = other.categoryHint;
+        this.updateAvailable = other.updateAvailable;
+        this.verificationInfo = other.verificationInfo;
+
+        if (mOldCodePaths != null) {
+            if (other.mOldCodePaths != null) {
+                mOldCodePaths.clear();
+                mOldCodePaths.addAll(other.mOldCodePaths);
+            } else {
+                mOldCodePaths = null;
+            }
+        }
+        mUserState.clear();
+        for (int i = 0; i < other.mUserState.size(); i++) {
+            mUserState.put(other.mUserState.keyAt(i), other.mUserState.valueAt(i));
+        }
+        return this;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6d242f4..5c88e06 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -539,16 +539,18 @@
             if((p.pkg != null) && (p.pkg.applicationInfo != null)) {
                 p.pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
             }
-            mDisabledSysPackages.put(name, p);
-
+            final PackageSetting disabled;
             if (replaced) {
                 // a little trick...  when we install the new package, we don't
                 // want to modify the existing PackageSetting for the built-in
-                // version.  so at this point we need a new PackageSetting that
-                // is okay to muck with.
-                PackageSetting newp = new PackageSetting(p);
-                replacePackageLPw(name, newp);
+                // version.  so at this point we make a copy to place into the
+                // disabled set.
+                disabled = new PackageSetting(p);
+            } else {
+                disabled = p;
             }
+            mDisabledSysPackages.put(name, disabled);
+
             return true;
         }
         return false;
@@ -1105,19 +1107,6 @@
         mInstallerPackages.remove(packageName);
     }
 
-    private void replacePackageLPw(String name, PackageSetting newp) {
-        final PackageSetting p = mPackages.get(name);
-        if (p != null) {
-            if (p.sharedUser != null) {
-                p.sharedUser.removePackage(p);
-                p.sharedUser.addPackage(newp);
-            } else {
-                replaceUserIdLPw(p.appId, newp);
-            }
-        }
-        mPackages.put(name, newp);
-    }
-
     private boolean addUserIdLPw(int uid, Object obj, Object name) {
         if (uid > Process.LAST_APPLICATION_UID) {
             return false;
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index c94d209..1a8b2af 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -24,7 +24,6 @@
 import android.util.proto.ProtoOutputStream;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 
 /**
@@ -69,24 +68,26 @@
         proto.end(token);
     }
 
-    void removePackage(PackageSetting packageSetting) {
-        if (packages.remove(packageSetting)) {
-            // recalculate the pkgFlags for this shared user if needed
-            if ((this.pkgFlags & packageSetting.pkgFlags) != 0) {
-                int aggregatedFlags = uidFlags;
-                for (PackageSetting ps : packages) {
-                    aggregatedFlags |= ps.pkgFlags;
-                }
-                setFlags(aggregatedFlags);
-            }
-            if ((this.pkgPrivateFlags & packageSetting.pkgPrivateFlags) != 0) {
-                int aggregatedPrivateFlags = uidPrivateFlags;
-                for (PackageSetting ps : packages) {
-                    aggregatedPrivateFlags |= ps.pkgPrivateFlags;
-                }
-                setPrivateFlags(aggregatedPrivateFlags);
-            }
+    boolean removePackage(PackageSetting packageSetting) {
+        if (!packages.remove(packageSetting)) {
+            return false;
         }
+        // recalculate the pkgFlags for this shared user if needed
+        if ((this.pkgFlags & packageSetting.pkgFlags) != 0) {
+            int aggregatedFlags = uidFlags;
+            for (PackageSetting ps : packages) {
+                aggregatedFlags |= ps.pkgFlags;
+            }
+            setFlags(aggregatedFlags);
+        }
+        if ((this.pkgPrivateFlags & packageSetting.pkgPrivateFlags) != 0) {
+            int aggregatedPrivateFlags = uidPrivateFlags;
+            for (PackageSetting ps : packages) {
+                aggregatedPrivateFlags |= ps.pkgPrivateFlags;
+            }
+            setPrivateFlags(aggregatedPrivateFlags);
+        }
+        return true;
     }
 
     void addPackage(PackageSetting packageSetting) {
@@ -143,4 +144,16 @@
         }
     }
 
+    /** Updates all fields in this shared user setting from another. */
+    public SharedUserSetting updateFrom(SharedUserSetting sharedUser) {
+        copyFrom(sharedUser);
+        this.userId = sharedUser.userId;
+        this.uidFlags = sharedUser.uidFlags;
+        this.uidPrivateFlags = sharedUser.uidPrivateFlags;
+        this.seInfoTargetSdkVersion = sharedUser.seInfoTargetSdkVersion;
+        this.packages.clear();
+        this.packages.addAll(sharedUser.packages);
+        this.signaturesChanged = sharedUser.signaturesChanged;
+        return this;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index ee41c0b..c3c0788 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -716,9 +716,9 @@
         assertNotSame(origPkgSetting.mPermissionsState, testPkgSetting.mPermissionsState);
         assertThat(origPkgSetting.mPermissionsState, is(testPkgSetting.mPermissionsState));
         assertThat(origPkgSetting.name, is(testPkgSetting.name));
-        // oldCodePaths is _not_ copied
-        // assertNotSame(origPkgSetting.oldCodePaths, testPkgSetting.oldCodePaths);
-        // assertThat(origPkgSetting.oldCodePaths, is(not(testPkgSetting.oldCodePaths)));
+        // mOldCodePaths is _not_ copied
+        // assertNotSame(origPkgSetting.mOldCodePaths, testPkgSetting.mOldCodePaths);
+        // assertThat(origPkgSetting.mOldCodePaths, is(not(testPkgSetting.mOldCodePaths)));
         assertSame(origPkgSetting.parentPackageName, testPkgSetting.parentPackageName);
         assertThat(origPkgSetting.parentPackageName, is(testPkgSetting.parentPackageName));
         assertSame(origPkgSetting.pkg, testPkgSetting.pkg);