Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2124d4b3 authored by Richard Uhler's avatar Richard Uhler
Browse files

Ensure race between rollback and roll forward is properly handled

If rollback for a package is committed at the same time the package is
updated, it's possible we will incorrectly roll back the newly updated
version of the application.

Add a hidden API to the package installer that lets you set a required
existing version of a package to be updated. If the expected package
version is not installed at the time of commit, the update install
fails.

The RollbackManager uses this new API to ensure that rollback will fail
if the package in question was just updated.

Test: atest RollbackTest, with new test added and manual confirmation
      that the race condition was exercised by the new test.
Bug: 128831080

Change-Id: Ifa5627e257d2ef13e2b213ef0dbc93932797ce0d
parent 7abba0a4
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -1318,6 +1318,8 @@ public class PackageInstaller {
        public boolean isMultiPackage;
        /** {@hide} */
        public boolean isStaged;
        /** {@hide} */
        public long requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;

        /**
         * Construct parameters for a new package install session.
@@ -1350,6 +1352,7 @@ public class PackageInstaller {
            installerPackageName = source.readString();
            isMultiPackage = source.readBoolean();
            isStaged = source.readBoolean();
            requiredInstalledVersionCode = source.readLong();
        }

        /** {@hide} */
@@ -1372,6 +1375,7 @@ public class PackageInstaller {
            ret.installerPackageName = installerPackageName;
            ret.isMultiPackage = isMultiPackage;
            ret.isStaged = isStaged;
            ret.requiredInstalledVersionCode = requiredInstalledVersionCode;
            return ret;
        }

@@ -1567,6 +1571,19 @@ public class PackageInstaller {
            }
        }

        /**
         * Require the given version of the package be installed.
         * The install will only be allowed if the existing version code of
         * the package installed on the device matches the given version code.
         * Use {@link * PackageManager#VERSION_CODE_HIGHEST} to allow
         * installation regardless of the currently installed package version.
         *
         * @hide
         */
        public void setRequiredInstalledVersionCode(long versionCode) {
            requiredInstalledVersionCode = versionCode;
        }

        /** {@hide} */
        public void setInstallFlagsForcePermissionPrompt() {
            installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT;
@@ -1708,6 +1725,7 @@ public class PackageInstaller {
            pw.printPair("installerPackageName", installerPackageName);
            pw.printPair("isMultiPackage", isMultiPackage);
            pw.printPair("isStaged", isStaged);
            pw.printPair("requiredInstalledVersionCode", requiredInstalledVersionCode);
            pw.println();
        }

@@ -1736,6 +1754,7 @@ public class PackageInstaller {
            dest.writeString(installerPackageName);
            dest.writeBoolean(isMultiPackage);
            dest.writeBoolean(isStaged);
            dest.writeLong(requiredInstalledVersionCode);
        }

        public static final Parcelable.Creator<SessionParams>
+9 −0
Original line number Diff line number Diff line
@@ -1423,6 +1423,14 @@ public abstract class PackageManager {
     */
    public static final int INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY = -120;

    /**
     * Installation failed return code: the required installed version code
     * does not match the currently installed package version code.
     *
     * @hide
     */
    public static final int INSTALL_FAILED_WRONG_INSTALLED_VERSION = -121;

    /** @hide */
    @IntDef(flag = true, prefix = { "DELETE_" }, value = {
            DELETE_KEEP_DATA,
@@ -6926,6 +6934,7 @@ public abstract class PackageManager {
            case INSTALL_FAILED_BAD_DEX_METADATA: return "INSTALL_FAILED_BAD_DEX_METADATA";
            case INSTALL_FAILED_MISSING_SPLIT: return "INSTALL_FAILED_MISSING_SPLIT";
            case INSTALL_FAILED_BAD_SIGNATURE: return "INSTALL_FAILED_BAD_SIGNATURE";
            case INSTALL_FAILED_WRONG_INSTALLED_VERSION: return "INSTALL_FAILED_WRONG_INSTALLED_VERSION";
            default: return Integer.toString(status);
        }
    }
+2 −0
Original line number Diff line number Diff line
@@ -64,6 +64,8 @@ public class PackageHelper {
    public static final int RECOMMEND_MEDIA_UNAVAILABLE = -5;
    public static final int RECOMMEND_FAILED_INVALID_URI = -6;
    public static final int RECOMMEND_FAILED_VERSION_DOWNGRADE = -7;
    /** {@hide} */
    public static final int RECOMMEND_FAILED_WRONG_INSTALLED_VERSION = -8;

    private static final String TAG = "PackageHelper";
    // App installation location settings values
+26 −2
Original line number Diff line number Diff line
@@ -14974,12 +14974,14 @@ public class PackageManagerService extends IPackageManager.Stub
        final int installReason;
        @Nullable
        MultiPackageInstallParams mParentInstallParams;
        final long requiredInstalledVersionCode;
        InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
                int installFlags, String installerPackageName, String volumeUuid,
                VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
                String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
                PackageParser.SigningDetails signingDetails, int installReason) {
                PackageParser.SigningDetails signingDetails, int installReason,
                long requiredInstalledVersionCode) {
            super(user);
            this.origin = origin;
            this.move = move;
@@ -14993,6 +14995,7 @@ public class PackageManagerService extends IPackageManager.Stub
            this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
            this.signingDetails = signingDetails;
            this.installReason = installReason;
            this.requiredInstalledVersionCode = requiredInstalledVersionCode;
        }
        InstallParams(ActiveInstallSession activeInstallSession) {
@@ -15023,6 +15026,8 @@ public class PackageManagerService extends IPackageManager.Stub
            whitelistedRestrictedPermissions = activeInstallSession.getSessionParams()
                    .whitelistedRestrictedPermissions;
            signingDetails = activeInstallSession.getSigningDetails();
            requiredInstalledVersionCode = activeInstallSession.getSessionParams()
                    .requiredInstalledVersionCode;
        }
        @Override
@@ -15051,6 +15056,23 @@ public class PackageManagerService extends IPackageManager.Stub
                    }
                }
                if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) {
                    if (dataOwnerPkg == null) {
                        Slog.w(TAG, "Required installed version code was "
                                + requiredInstalledVersionCode
                                + " but package is not installed");
                        return PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION;
                    }
                    if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) {
                        Slog.w(TAG, "Required installed version code was "
                                + requiredInstalledVersionCode
                                + " but actual installed version is "
                                + dataOwnerPkg.getLongVersionCode());
                        return PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION;
                    }
                }
                if (dataOwnerPkg != null) {
                    if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
                            dataOwnerPkg.applicationInfo.flags)) {
@@ -15177,6 +15199,8 @@ public class PackageManagerService extends IPackageManager.Stub
                    loc = installLocationPolicy(pkgLite);
                    if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
                        ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
                    } else if (loc == PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION) {
                        ret = PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
                    } else if (!onInt) {
                        // Override install location with flags
                        if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
@@ -23289,7 +23313,7 @@ public class PackageManagerService extends IPackageManager.Stub
                installerPackageName, volumeUuid, null /*verificationInfo*/, user,
                packageAbiOverride, null /*grantedPermissions*/,
                null /*whitelistedRestrictedPermissions*/, PackageParser.SigningDetails.UNKNOWN,
                PackageManager.INSTALL_REASON_UNKNOWN);
                PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.VERSION_CODE_HIGHEST);
        params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params));
        msg.obj = params;
+16 −0
Original line number Diff line number Diff line
@@ -165,6 +165,22 @@ public class StagingManager {
                continue;
            }
            long activeVersion = activePackage.applicationInfo.longVersionCode;
            if (session.params.requiredInstalledVersionCode
                    != PackageManager.VERSION_CODE_HIGHEST) {
                if (activeVersion != session.params.requiredInstalledVersionCode) {
                    session.setStagedSessionFailed(
                            SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                            "Installed version of APEX package " + newPackage.packageName
                            + " does not match required. Active version: " + activeVersion
                            + " required: " + session.params.requiredInstalledVersionCode);

                    if (!mApexManager.abortActiveSession()) {
                        Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
                    }
                    return false;
                }
            }

            boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
                    session.params.installFlags, activePackage.applicationInfo.flags);
            if (activeVersion > newPackage.versionCode && !allowsDowngrade) {
Loading