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

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

Simplify staged install resume flow.

In short, new flow looks like this:

1. Check state of apex sessions.
2. If it's inconsistent (e.g has activated and failed sessions) -> abort
3. If there is a failed apex session, fail all remaining sessions.
4. Otherwise proceed with resuming sessions one-by-one.

Test: atest CtsStagedInstallHostTestCases
Test: atest ApexManagerTest
Test: atest StagingManagerTest
Bug: 176452293
Change-Id: Ide45ee9977b291a4ce2ca3d1cdebf53f464891f8
parent 06bdb473
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Singleton;
import android.util.Slog;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -225,6 +226,12 @@ public abstract class ApexManager {
    @Nullable
    abstract ApexSessionInfo getStagedSessionInfo(int sessionId);

    /**
     * Returns array of all staged sessions known to apexd.
     */
    @NonNull
    abstract SparseArray<ApexSessionInfo> getSessions();

    /**
     * Submit a staged session to apex service. This causes the apex service to perform some initial
     * verification and accept or reject the session. Submitting a session successfully is not
@@ -690,6 +697,21 @@ public abstract class ApexManager {
            }
        }

        @Override
        SparseArray<ApexSessionInfo> getSessions() {
            try {
                final ApexSessionInfo[] sessions = waitForApexService().getSessions();
                final SparseArray<ApexSessionInfo> result = new SparseArray<>(sessions.length);
                for (int i = 0; i < sessions.length; i++) {
                    result.put(sessions[i].sessionId, sessions[i]);
                }
                return result;
            } catch (RemoteException re) {
                Slog.e(TAG, "Unable to contact apexservice", re);
                throw new RuntimeException(re);
            }
        }

        @Override
        ApexInfoList submitStagedSession(ApexSessionParams params) throws PackageManagerException {
            try {
@@ -1082,6 +1104,11 @@ public abstract class ApexManager {
            throw new UnsupportedOperationException();
        }

        @Override
        SparseArray<ApexSessionInfo> getSessions() {
            return new SparseArray<>(0);
        }

        @Override
        ApexInfoList submitStagedSession(ApexSessionParams params)
                throws PackageManagerException {
+19 −13
Original line number Diff line number Diff line
@@ -295,23 +295,29 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
        synchronized (mSessions) {
            for (int i = 0; i < mSessions.size(); i++) {
                final PackageInstallerSession session = mSessions.valueAt(i);
                if (session.isStaged()) {
                    stagedSessionsToRestore.add(session.mStagedSession);
                if (!session.isStaged()) {
                    continue;
                }
                StagingManager.StagedSession stagedSession = session.mStagedSession;
                if (!stagedSession.isInTerminalState() && stagedSession.hasParentSessionId()
                        && getSession(stagedSession.getParentSessionId()) == null) {
                    stagedSession.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                            "An orphan staged session " + stagedSession.sessionId() + " is found, "
                                + "parent " + stagedSession.getParentSessionId() + " is missing");
                    continue;
                }
                if (!stagedSession.hasParentSessionId() && stagedSession.isCommitted()
                        && !stagedSession.isInTerminalState()) {
                    // StagingManager.restoreSessions expects a list of committed, non-finalized
                    // parent staged sessions.
                    stagedSessionsToRestore.add(stagedSession);
                }
        // Don't hold mSessions lock when calling restoreSession, since it might trigger an APK
        // atomic install which needs to query sessions, which requires lock on mSessions.
        boolean isDeviceUpgrading = mPm.isDeviceUpgrading();
        for (StagingManager.StagedSession session : stagedSessionsToRestore) {
            if (!session.isInTerminalState() && session.hasParentSessionId()
                    && getSession(session.getParentSessionId()) == null) {
                session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                        "An orphan staged session " + session.sessionId() + " is found, "
                                + "parent " + session.getParentSessionId() + " is missing");
            }
            mStagingManager.restoreSession(session, isDeviceUpgrading);
        }
        // Don't hold mSessions lock when calling restoreSessions, since it might trigger an APK
        // atomic install which needs to query sessions, which requires lock on mSessions.
        // Note: restoreSessions mutates content of stagedSessionsToRestore.
        mStagingManager.restoreSessions(stagedSessionsToRestore, mPm.isDeviceUpgrading());
    }

    @GuardedBy("mSessions")