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

Commit 7cbf063f authored by JW Wang's avatar JW Wang
Browse files

Extract staged session code to a separate class (1/n)

Now StagingManager manages a collection of StagedSessions instead of
PackageInstallerSessions which might expose info not directly related to
staged sessions and not useful to StagingManager.

In next CL, we will move fields/methods that are related to staged
sessions into PackageInstallerSession#StagedSession to improve the
cohesion.

Bug: 166694095
Test: atest StagedInstallTest
Test: atest StagingManagerTest
Test: atest PackageInstallerSessionTest

Change-Id: I4eeeea280d10fc8f49a3aa053fd907be5c2e3706
parent e86a9431
Loading
Loading
Loading
Loading
+7 −7
Original line number Diff line number Diff line
@@ -291,23 +291,23 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
    }

    void restoreAndApplyStagedSessionIfNeeded() {
        List<PackageInstallerSession> stagedSessionsToRestore = new ArrayList<>();
        List<StagingManager.StagedSession> stagedSessionsToRestore = new ArrayList<>();
        synchronized (mSessions) {
            for (int i = 0; i < mSessions.size(); i++) {
                final PackageInstallerSession session = mSessions.valueAt(i);
                if (session.isStaged()) {
                    stagedSessionsToRestore.add(session);
                    stagedSessionsToRestore.add(session.mStagedSession);
                }
            }
        }
        // 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 (PackageInstallerSession session : stagedSessionsToRestore) {
            if (!session.isStagedAndInTerminalState() && session.hasParentSessionId()
        for (StagingManager.StagedSession session : stagedSessionsToRestore) {
            if (!session.isInTerminalState() && session.hasParentSessionId()
                    && getSession(session.getParentSessionId()) == null) {
                session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                        "An orphan staged session " + session.sessionId + " is found, "
                session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                        "An orphan staged session " + session.sessionId() + " is found, "
                                + "parent " + session.getParentSessionId() + " is missing");
            }
            mStagingManager.restoreSession(session, isDeviceUpgrading);
@@ -1399,7 +1399,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
                @Override
                public void run() {
                    if (session.isStaged() && !success) {
                        mStagingManager.abortSession(session);
                        mStagingManager.abortSession(session.mStagedSession);
                    }
                    synchronized (mSessions) {
                        if (!session.isStaged() || !success) {
+152 −4
Original line number Diff line number Diff line
@@ -440,6 +440,153 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
    @GuardedBy("mLock")
    private String mStagedSessionErrorMessage;

    @Nullable
    final StagedSession mStagedSession;

    @VisibleForTesting
    public class StagedSession implements StagingManager.StagedSession {
        @Override
        public List<StagingManager.StagedSession> getChildSessions() {
            if (!params.isMultiPackage) {
                return Collections.EMPTY_LIST;
            }
            synchronized (mLock) {
                int size = mChildSessions.size();
                List<StagingManager.StagedSession> childSessions = new ArrayList<>(size);
                for (int i = 0; i < size; ++i) {
                    childSessions.add(mChildSessions.valueAt(i).mStagedSession);
                }
                return childSessions;
            }
        }

        @Override
        public SessionParams sessionParams() {
            return params;
        }

        @Override
        public boolean isMultiPackage() {
            return params.isMultiPackage;
        }

        @Override
        public boolean isApexSession() {
            return (params.installFlags & PackageManager.INSTALL_APEX) != 0;
        }

        @Override
        public int sessionId() {
            return sessionId;
        }

        @Override
        public boolean containsApexSession() {
            return PackageInstallerSession.this.containsApexSession();
        }

        @Override
        public String getPackageName() {
            return PackageInstallerSession.this.getPackageName();
        }

        @Override
        public void setSessionReady() {
            setStagedSessionReady();
        }

        @Override
        public void setSessionFailed(int errorCode, String errorMessage) {
            setStagedSessionFailed(errorCode, errorMessage);
        }

        @Override
        public void setSessionApplied() {
            setStagedSessionApplied();
        }

        @Override
        public boolean containsApkSession() {
            return PackageInstallerSession.this.containsApkSession();
        }

        @Override
        public void installSession(IntentSender statusReceiver) {
            installStagedSession(statusReceiver);

        }

        @Override
        public boolean hasParentSessionId() {
            return PackageInstallerSession.this.hasParentSessionId();
        }

        @Override
        public int getParentSessionId() {
            return PackageInstallerSession.this.getParentSessionId();
        }

        @Override
        public boolean isCommitted() {
            return PackageInstallerSession.this.isCommitted();
        }

        @Override
        public boolean isInTerminalState() {
            return isStagedAndInTerminalState();
        }

        @Override
        public boolean isDestroyed() {
            return PackageInstallerSession.this.isDestroyed();
        }

        @Override
        public long getCommittedMillis() {
            return PackageInstallerSession.this.getCommittedMillis();
        }

        @Override
        public boolean sessionContains(Predicate<StagingManager.StagedSession> filter) {
            return PackageInstallerSession.this.sessionContains(s -> filter.test(s.mStagedSession));
        }

        @Override
        public boolean isSessionReady() {
            return isStagedSessionReady();
        }

        @Override
        public boolean isSessionApplied() {
            return isStagedSessionApplied();
        }

        @Override
        public boolean isSessionFailed() {
            return isStagedSessionFailed();
        }

        @Override
        public void abandon() {
            PackageInstallerSession.this.abandon();
        }

        @Override
        public boolean notifyStartPreRebootVerification() {
            return notifyStagedStartPreRebootVerification();
        }

        @Override
        public void notifyEndPreRebootVerification() {
            notifyStagedEndPreRebootVerification();
        }

        @Override
        public void verifySession() {
            verifyStagedSession();
        }
    }

    /**
     * The callback to run when pre-reboot verification has ended. Used by {@link #abandonStaged()}
     * to delay session clean-up until it is safe to do so.
@@ -697,6 +844,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        mStagedSessionErrorCode = stagedSessionErrorCode;
        mStagedSessionErrorMessage =
                stagedSessionErrorMessage != null ? stagedSessionErrorMessage : "";
        mStagedSession = params.isStaged ? new StagedSession() : null;

        if (isDataLoaderInstallation()) {
            if (isApexSession()) {
@@ -1728,7 +1876,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                    SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, msgWithErrorCode);
            // TODO(b/136257624): Remove this once all verification logic has been transferred out
            //  of StagingManager.
            mStagingManager.notifyVerificationComplete(this);
            mStagingManager.notifyVerificationComplete(mStagedSession);
        } else {
            // Dispatch message to remove session from PackageInstallerService.
            dispatchSessionFinished(error, msg, null);
@@ -1847,7 +1995,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                    .write();
        }
        if (params.isStaged) {
            mStagingManager.commitSession(this);
            mStagingManager.commitSession(mStagedSession);
            // TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even
            //  though ideally, we just need to send session committed broadcast.
            dispatchSessionFinished(INSTALL_SUCCEEDED, "Session staged", null);
@@ -2188,7 +2336,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        if (isStaged()) {
            // TODO(b/136257624): Remove this once all verification logic has been transferred out
            //  of StagingManager.
            mStagingManager.notifyPreRebootVerification_Apk_Complete(this);
            mStagingManager.notifyPreRebootVerification_Apk_Complete(mStagedSession);
            // TODO(b/136257624): We also need to destroy internals for verified staged session,
            //  otherwise file descriptors are never closed for verified staged session until reboot
            return;
@@ -3223,7 +3371,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
            r = () -> {
                assertNotLocked("abandonStaged");
                if (isCommitted) {
                    mStagingManager.abortCommittedSession(this);
                    mStagingManager.abortCommittedSession(mStagedSession);
                }
                cleanStageDir(childSessions);
                destroyInternal();
+166 −131

File changed.

Preview size limit exceeded, changes collapsed.

+13 −6
Original line number Diff line number Diff line
@@ -33,8 +33,11 @@ import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

import java.io.File;
import java.util.function.Predicate;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -70,8 +73,8 @@ public class StagingManagerTest {
    public void checkNonOverlappingWithStagedSessions_laterSessionShouldNotFailEarlierOnes()
            throws Exception {
        // Create 2 sessions with overlapping packages
        PackageInstallerSession session1 = createSession(111, "com.foo", 1);
        PackageInstallerSession session2 = createSession(222, "com.foo", 2);
        StagingManager.StagedSession session1 = createSession(111, "com.foo", 1);
        StagingManager.StagedSession session2 = createSession(222, "com.foo", 2);

        mStagingManager.createSession(session1);
        mStagingManager.createSession(session2);
@@ -82,7 +85,7 @@ public class StagingManagerTest {
                () -> mStagingManager.checkNonOverlappingWithStagedSessions(session2));
    }

    private PackageInstallerSession createSession(int sessionId, String packageName,
    private StagingManager.StagedSession createSession(int sessionId, String packageName,
            long committedMillis) {
        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
@@ -121,8 +124,12 @@ public class StagingManagerTest {
                /* stagedSessionErrorCode */ PackageInstaller.SessionInfo.STAGED_SESSION_NO_ERROR,
                /* stagedSessionErrorMessage */ "no error");

        session = spy(session);
        doReturn(packageName).when(session).getPackageName();
        return session;
        StagingManager.StagedSession stagedSession = spy(session.mStagedSession);
        doReturn(packageName).when(stagedSession).getPackageName();
        doAnswer(invocation -> {
            Predicate<StagingManager.StagedSession> filter = invocation.getArgument(0);
            return filter.test(stagedSession);
        }).when(stagedSession).sessionContains(any());
        return stagedSession;
    }
}