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

Commit da998cfa authored by Nikita Ioffe's avatar Nikita Ioffe
Browse files

Fail committing new staged session if another one is already in progress

This ensures that there might be at most one committed non-finalized
(i.e. either applied or failed) session.

Note that from StagingManager POV atomic staged install is considered to
be a single session that is represented by the parent session of an
install.

Test: run adb install --staged foo.apk twice.
Test: StagedRollbackTest
Bug: 127296534
Change-Id: I497723173745d4f6eb64b7d22fa3adb0f9e8f6c0
parent 83234be1
Loading
Loading
Loading
Loading
+8 −0
Original line number Original line Diff line number Diff line
@@ -1389,6 +1389,14 @@ public abstract class PackageManager {
     */
     */
    public static final int INSTALL_FAILED_BAD_SIGNATURE = -118;
    public static final int INSTALL_FAILED_BAD_SIGNATURE = -118;


    /**
     * Installation failed return code: a new staged session was attempted to be committed while
     * there is already one in-progress.
     *
     * @hide
     */
    public static final int INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS = -119;

    /** @hide */
    /** @hide */
    @IntDef(flag = true, prefix = { "DELETE_" }, value = {
    @IntDef(flag = true, prefix = { "DELETE_" }, value = {
            DELETE_KEEP_DATA,
            DELETE_KEEP_DATA,
+14 −1
Original line number Original line Diff line number Diff line
@@ -510,6 +510,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        }
        }
    }
    }


    /** {@hide} */
    boolean isCommitted() {
        synchronized (mLock) {
            return mCommitted;
        }
    }

    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private void assertPreparedAndNotSealedLocked(String cookie) {
    private void assertPreparedAndNotSealedLocked(String cookie) {
        assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
        assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
@@ -1064,7 +1071,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {


    private void handleCommit() {
    private void handleCommit() {
        if (params.isStaged) {
        if (params.isStaged) {
            try {
                mStagingManager.commitSession(this);
                mStagingManager.commitSession(this);
            } catch (StagingManager.AlreadyInProgressStagedSessionException e) {
                dispatchSessionFinished(
                        PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
                        e.getMessage(), null);
            }
            destroyInternal();
            destroyInternal();
            dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
            dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
            return;
            return;
+43 −3
Original line number Original line Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.pm;
package com.android.server.pm;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.apex.ApexInfo;
import android.apex.ApexInfo;
import android.apex.ApexInfoList;
import android.apex.ApexInfoList;
import android.apex.ApexSessionInfo;
import android.apex.ApexSessionInfo;
@@ -479,11 +480,42 @@ public class StagingManager {
        return true;
        return true;
    }
    }


    void commitSession(@NonNull PackageInstallerSession session) {
    void commitSession(@NonNull PackageInstallerSession session)
            throws AlreadyInProgressStagedSessionException {
        PackageInstallerSession activeSession = getActiveSession();
        boolean anotherSessionAlreadyInProgress =
                activeSession != null && session.sessionId != activeSession.sessionId;
        updateStoredSession(session);
        updateStoredSession(session);
        if (anotherSessionAlreadyInProgress) {
            session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                    "There is already in-progress committed staged session "
                            + activeSession.sessionId);
            throw new AlreadyInProgressStagedSessionException(activeSession.sessionId);
        }
        mBgHandler.post(() -> preRebootVerification(session));
        mBgHandler.post(() -> preRebootVerification(session));
    }
    }


    @Nullable
    private PackageInstallerSession getActiveSession() {
        synchronized (mStagedSessions) {
            for (int i = 0; i < mStagedSessions.size(); i++) {
                final PackageInstallerSession session = mStagedSessions.valueAt(i);
                if (!session.isCommitted()) {
                    continue;
                }
                if (session.hasParentSessionId()) {
                    // Staging manager will finalize only parent session. Ignore child sessions
                    // picking the active.
                    continue;
                }
                if (!session.isStagedSessionApplied() && !session.isStagedSessionFailed()) {
                    return session;
                }
            }
        }
        return null;
    }

    void createSession(@NonNull PackageInstallerSession sessionInfo) {
    void createSession(@NonNull PackageInstallerSession sessionInfo) {
        synchronized (mStagedSessions) {
        synchronized (mStagedSessions) {
            mStagedSessions.append(sessionInfo.sessionId, sessionInfo);
            mStagedSessions.append(sessionInfo.sessionId, sessionInfo);
@@ -497,8 +529,8 @@ public class StagingManager {
    }
    }


    void abortCommittedSession(@NonNull PackageInstallerSession session) {
    void abortCommittedSession(@NonNull PackageInstallerSession session) {
        if (session.isStagedSessionApplied()) {
        if (session.isStagedSessionApplied() || session.isStagedSessionFailed()) {
            Slog.w(TAG, "Cannot abort applied session!");
            Slog.w(TAG, "Cannot abort already finalized session : " + session.sessionId);
            return;
            return;
        }
        }
        abortSession(session);
        abortSession(session);
@@ -618,4 +650,12 @@ public class StagingManager {
            }
            }
        }
        }
    }
    }

    static final class AlreadyInProgressStagedSessionException extends Exception {

        AlreadyInProgressStagedSessionException(int sessionId) {
            super("There is already in-progress committed staged session "
                    + sessionId);
        }
    }
}
}