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

Commit 8f050435 authored by Mohammad Samiul Islam's avatar Mohammad Samiul Islam
Browse files

Fail blocking staged session when rollback is committed

Bug: 162294757
Test: atest RollbackManagerHostTest#testRollbackFailsBlockingSessions
Test: atest StagedInstallTest
Change-Id: Ia496e4ceb3db61e08875b2b6b060c91a966a5d53
parent f772d29b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -11900,6 +11900,7 @@ package android.content.pm {
    field public static final int INVALID_ID = -1; // 0xffffffff
    field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2
    field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0
    field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4
    field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3
    field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1
  }
+8 −1
Original line number Diff line number Diff line
@@ -2075,7 +2075,8 @@ public class PackageInstaller {
                STAGED_SESSION_NO_ERROR,
                STAGED_SESSION_VERIFICATION_FAILED,
                STAGED_SESSION_ACTIVATION_FAILED,
                STAGED_SESSION_UNKNOWN})
                STAGED_SESSION_UNKNOWN,
                STAGED_SESSION_OTHER_ERROR})
        @Retention(RetentionPolicy.SOURCE)
        public @interface StagedSessionErrorCode{}
        /**
@@ -2101,6 +2102,12 @@ public class PackageInstaller {
         */
        public static final int STAGED_SESSION_UNKNOWN = 3;

        /**
         * Constant indicating that a known error occurred while processing this staged session, but
         * the error could not be matched to other categories.
         */
        public static final int STAGED_SESSION_OTHER_ERROR = 4;

        /** {@hide} */
        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
        public int sessionId;
+1 −0
Original line number Diff line number Diff line
@@ -11900,6 +11900,7 @@ package android.content.pm {
    field public static final int INVALID_ID = -1; // 0xffffffff
    field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2
    field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0
    field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4
    field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3
    field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1
  }
+7 −5
Original line number Diff line number Diff line
@@ -3181,7 +3181,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
    /** {@hide} */
    void setStagedSessionReady() {
        synchronized (mLock) {
            if (mDestroyed) return; // Do not allow destroyed staged session to change state
            // Do not allow destroyed/failed staged session to change state
            if (mDestroyed || mStagedSessionFailed) return;
            mStagedSessionReady = true;
            mStagedSessionApplied = false;
            mStagedSessionFailed = false;
@@ -3192,10 +3193,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
    }

    /** {@hide} */
    void setStagedSessionFailed(@StagedSessionErrorCode int errorCode,
                                String errorMessage) {
    void setStagedSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage) {
        synchronized (mLock) {
            if (mDestroyed) return; // Do not allow destroyed staged session to change state
            // Do not allow destroyed/failed staged session to change state
            if (mDestroyed || mStagedSessionFailed) return;
            mStagedSessionReady = false;
            mStagedSessionApplied = false;
            mStagedSessionFailed = true;
@@ -3210,7 +3211,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
    /** {@hide} */
    void setStagedSessionApplied() {
        synchronized (mLock) {
            if (mDestroyed) return; // Do not allow destroyed staged session to change state
            // Do not allow destroyed/failed staged session to change state
            if (mDestroyed || mStagedSessionFailed) return;
            mStagedSessionReady = false;
            mStagedSessionApplied = true;
            mStagedSessionFailed = false;
+43 −14
Original line number Diff line number Diff line
@@ -870,10 +870,21 @@ public class StagingManager {
        mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
    }

    private int parentOrOwnSessionId(PackageInstallerSession session) {
    private int getSessionIdForParentOrSelf(PackageInstallerSession session) {
        return session.hasParentSessionId() ? session.getParentSessionId() : session.sessionId;
    }

    private PackageInstallerSession getParentSessionOrSelf(PackageInstallerSession session) {
        return session.hasParentSessionId()
                ? getStagedSession(session.getParentSessionId())
                : session;
    }

    private boolean isRollback(PackageInstallerSession session) {
        final PackageInstallerSession root = getParentSessionOrSelf(session);
        return root.params.installReason == PackageManager.INSTALL_REASON_ROLLBACK;
    }

    /**
     * <p> Check if the session provided is non-overlapping with the active staged sessions.
     *
@@ -899,6 +910,8 @@ public class StagingManager {
        boolean supportsCheckpoint = ((StorageManager) mContext.getSystemService(
                Context.STORAGE_SERVICE)).isCheckpointSupported();

        final boolean isRollback = isRollback(session);

        synchronized (mStagedSessions) {
            for (int i = 0; i < mStagedSessions.size(); i++) {
                final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
@@ -913,8 +926,8 @@ public class StagingManager {
                }
                // Check if stagedSession has an active parent session or not
                if (stagedSession.hasParentSessionId()) {
                    int parentId = stagedSession.getParentSessionId();
                    PackageInstallerSession parentSession = mStagedSessions.get(parentId);
                    final int parentId = stagedSession.getParentSessionId();
                    final PackageInstallerSession parentSession = mStagedSessions.get(parentId);
                    if (parentSession == null || parentSession.isStagedAndInTerminalState()
                            || parentSession.isDestroyed()) {
                        // Parent session has been abandoned or terminated already
@@ -930,21 +943,37 @@ public class StagingManager {
                    continue;
                }

                // If session is not among the active sessions, then it cannot have same package
                // name as any of the active sessions.
                // New session cannot have same package name as one of the active sessions
                if (session.getPackageName().equals(stagedSession.getPackageName())) {
                    if (isRollback) {
                        // If the new session is a rollback, then it gets priority. The existing
                        // session is failed to unblock rollback.
                        final PackageInstallerSession root = getParentSessionOrSelf(stagedSession);
                        if (!ensureActiveApexSessionIsAborted(root)) {
                            Slog.e(TAG, "Failed to abort apex session " + root.sessionId);
                            // Safe to ignore active apex session abort failure since session
                            // will be marked failed on next step and staging directory for session
                            // will be deleted.
                        }
                        root.setStagedSessionFailed(
                                SessionInfo.STAGED_SESSION_OTHER_ERROR,
                                "Session was blocking rollback session: " + session.sessionId);
                        Slog.i(TAG, "Session " + root.sessionId + " is marked failed due to "
                                + "blocking rollback session: " + session.sessionId);
                    } else {
                        throw new PackageManagerException(
                                PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
                                "Package: " + session.getPackageName() + " in session: "
                                        + session.sessionId + " has been staged already by session:"
                                    + stagedSession.sessionId, null);
                                        + " " + stagedSession.sessionId, null);
                    }
                }

                // Staging multiple root sessions is not allowed if device doesn't support
                // checkpoint. If session and stagedSession do not have common ancestor, they are
                // from two different root sessions.
                if (!supportsCheckpoint
                        && parentOrOwnSessionId(session) != parentOrOwnSessionId(stagedSession)) {
                if (!supportsCheckpoint && getSessionIdForParentOrSelf(session)
                        != getSessionIdForParentOrSelf(stagedSession)) {
                    throw new PackageManagerException(
                            PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
                            "Cannot stage multiple sessions without checkpoint support", null);
@@ -1260,8 +1289,8 @@ public class StagingManager {
                        + sessionId);
                return;
            }
            if (session.isDestroyed()) {
                // No point in running verification on a destroyed session
            if (session.isDestroyed() || session.isStagedSessionFailed()) {
                // No point in running verification on a destroyed/failed session
                onPreRebootVerificationComplete(sessionId);
                return;
            }