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

Commit 24b4ed56 authored by Gavin Corkery's avatar Gavin Corkery
Browse files

Mark apex sessions as successful upon boot completion

Instead of marking apex sessions as successful immediately, wait
until the boot has completed. This ensures that the checkpoint
has been committed. This is necessary since the apex session
state lives in /metadata, so any changes to the session will
persist regardless of the checkpoint state. This change ensures
that APK and APEX sessions behave the same way if the device
reboots after activation, but before boot completion.

Create a new lifecycle class within Staging Manager to receive
calls for boot completion.

Test: atest ApexRollbackTests
Test: Manual test of rebooting device after activation and before
     boot completion, and ensuring session is not marked as successful
Bug: 154705130
Change-Id: I822e46123d3f4d3d84e0c50c9aa791c1a81e8f50
parent ff779f6d
Loading
Loading
Loading
Loading
+58 −1
Original line number Diff line number Diff line
@@ -69,6 +69,8 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageHelper;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
@@ -110,6 +112,9 @@ public class StagingManager {
    private final List<String> mFailedPackageNames = new ArrayList<>();
    private String mNativeFailureReason;

    @GuardedBy("mSuccessfulStagedSessionIds")
    private final List<Integer> mSuccessfulStagedSessionIds = new ArrayList<>();

    StagingManager(PackageInstallerService pi, Context context,
            Supplier<PackageParser2> packageParserSupplier) {
        mPi = pi;
@@ -122,6 +127,34 @@ public class StagingManager {
                BackgroundThread.get().getLooper());
    }

    /**
     This class manages lifecycle events for StagingManager.
     */
    public static final class Lifecycle extends SystemService {
        private static StagingManager sStagingManager;

        public Lifecycle(Context context) {
            super(context);
        }

        void startService(StagingManager stagingManager) {
            sStagingManager = stagingManager;
            LocalServices.getService(SystemServiceManager.class).startService(this);
        }

        @Override
        public void onStart() {
            // no-op
        }

        @Override
        public void onBootPhase(int phase) {
            if (phase == SystemService.PHASE_BOOT_COMPLETED && sStagingManager != null) {
                sStagingManager.markStagedSessionsAsSuccessful();
            }
        }
    }

    private void updateStoredSession(@NonNull PackageInstallerSession sessionInfo) {
        synchronized (mStagedSessions) {
            PackageInstallerSession storedSession = mStagedSessions.get(sessionInfo.sessionId);
@@ -652,8 +685,23 @@ public class StagingManager {
        Slog.d(TAG, "Marking session " + session.sessionId + " as applied");
        session.setStagedSessionApplied();
        if (hasApex) {
            try {
                if (supportsCheckpoint()) {
                    // Store the session ID, which will be marked as successful by ApexManager
                    // upon boot completion.
                    synchronized (mSuccessfulStagedSessionIds) {
                        mSuccessfulStagedSessionIds.add(session.sessionId);
                    }
                } else {
                    // Mark sessions as successful immediately on non-checkpointing devices.
                    mApexManager.markStagedSessionSuccessful(session.sessionId);
                }
            } catch (RemoteException e) {
                Slog.w(TAG, "Checkpoint support unknown, marking session as successful "
                        + "immediately.");
                mApexManager.markStagedSessionSuccessful(session.sessionId);
            }
        }
    }

    private List<String> findAPKsInDir(File stageDir) {
@@ -1121,7 +1169,16 @@ public class StagingManager {
        }
    }

    void markStagedSessionsAsSuccessful() {
        synchronized (mSuccessfulStagedSessionIds) {
            for (int i = 0; i < mSuccessfulStagedSessionIds.size(); i++) {
                mApexManager.markStagedSessionSuccessful(mSuccessfulStagedSessionIds.get(i));
            }
        }
    }

    void systemReady() {
        new Lifecycle(mContext).startService(this);
        // Register the receiver of boot completed intent for staging manager.
        mContext.registerReceiver(new BroadcastReceiver() {
            @Override