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

Commit a8bc614b authored by JW Wang's avatar JW Wang
Browse files

Fix up the threading model (1/n)

1. add thread annotations
   - @WorkerThread: this method should run on the handler thread only
   - @AnyThread: this method is thread-safe
2. assert @WorkerThread methods really run on the handler thread
3. dispatch tasks to the handler thread where all state changes happen

TODO: remove locks in the next CL.

Bug: 158974169
Test: atest StagedRollbackTest
Change-Id: I64a5d494075daddc9a4b4fc330605987723f733d
parent 15b6c503
Loading
Loading
Loading
Loading
+42 −8
Original line number Diff line number Diff line
@@ -18,7 +18,9 @@ package com.android.server.rollback;

import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN;

import android.annotation.AnyThread;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -42,6 +44,7 @@ import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.PackageWatchdog;
import com.android.server.PackageWatchdog.FailureReasons;
@@ -59,6 +62,7 @@ import java.io.PrintWriter;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;

/**
 * {@link PackageHealthObserver} for {@link RollbackManagerService}.
@@ -113,7 +117,7 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
    public boolean execute(@Nullable VersionedPackage failedPackage,
            @FailureReasons int rollbackReason) {
        if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
            rollbackAll();
            mHandler.post(() -> rollbackAll());
            return true;
        }

@@ -122,7 +126,7 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
            Slog.w(TAG, "Expected rollback but no valid rollback found for " + failedPackage);
            return false;
        }
        rollbackPackage(rollback, failedPackage, rollbackReason);
        mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason));
        // Assume rollback executed successfully
        return true;
    }
@@ -132,10 +136,15 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
        return NAME;
    }

    private void assertInWorkerThread() {
        Preconditions.checkState(mHandler.getLooper().isCurrentThread());
    }

    /**
     * Start observing health of {@code packages} for {@code durationMs}.
     * This may cause {@code packages} to be rolled back if they crash too freqeuntly.
     */
    @AnyThread
    void startObservingHealth(List<String> packages, long durationMs) {
        PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs);
    }
@@ -143,11 +152,14 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
    /** Verifies the rollback state after a reboot and schedules polling for sometime after reboot
     * to check for native crashes and mitigate them if needed.
     */
    @AnyThread
    void onBootCompletedAsync() {
        mHandler.post(()->onBootCompleted());
    }

    @WorkerThread
    private void onBootCompleted() {
        assertInWorkerThread();
        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
        if (!rollbackManager.getAvailableRollbacks().isEmpty()) {
            // TODO(gavincorkery): Call into Package Watchdog from outside the observer
@@ -162,6 +174,7 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
        }
    }

    @AnyThread
    private RollbackInfo getAvailableRollback(VersionedPackage failedPackage) {
        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
        for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) {
@@ -191,8 +204,10 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
        return null;
    }

    @WorkerThread
    private BroadcastReceiver listenForStagedSessionReady(RollbackManager rollbackManager,
            int rollbackId, @Nullable VersionedPackage logPackage) {
        assertInWorkerThread();
        BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
@@ -202,12 +217,14 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
        };
        IntentFilter sessionUpdatedFilter =
                new IntentFilter(PackageInstaller.ACTION_SESSION_UPDATED);
        mContext.registerReceiver(sessionUpdatedReceiver, sessionUpdatedFilter);
        mContext.registerReceiver(sessionUpdatedReceiver, sessionUpdatedFilter, null, mHandler);
        return sessionUpdatedReceiver;
    }

    @WorkerThread
    private void handleStagedSessionChange(RollbackManager rollbackManager, int rollbackId,
            BroadcastReceiver listener, @Nullable VersionedPackage logPackage) {
        assertInWorkerThread();
        PackageInstaller packageInstaller =
                mContext.getPackageManager().getPackageInstaller();
        List<RollbackInfo> recentRollbacks =
@@ -249,7 +266,9 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
     * Returns {@code true} if staged session associated with {@code rollbackId} was marked
     * as handled, {@code false} if already handled.
     */
    @WorkerThread
    private boolean markStagedSessionHandled(int rollbackId) {
        assertInWorkerThread();
        synchronized (mPendingStagedRollbackIds) {
            return mPendingStagedRollbackIds.remove(rollbackId);
        }
@@ -259,13 +278,17 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
     * Returns {@code true} if all pending staged rollback sessions were marked as handled,
     * {@code false} if there is any left.
     */
    @WorkerThread
    private boolean isPendingStagedSessionsEmpty() {
        assertInWorkerThread();
        synchronized (mPendingStagedRollbackIds) {
            return mPendingStagedRollbackIds.isEmpty();
        }
    }

    @WorkerThread
    private void saveStagedRollbackId(int stagedRollbackId, @Nullable VersionedPackage logPackage) {
        assertInWorkerThread();
        writeStagedRollbackId(mLastStagedRollbackIdsFile, stagedRollbackId, logPackage);
    }

@@ -286,7 +309,9 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
        }
    }

    @WorkerThread
    private SparseArray<String> popLastStagedRollbackIds() {
        assertInWorkerThread();
        try {
            return readStagedRollbackIds(mLastStagedRollbackIdsFile);
        } finally {
@@ -319,6 +344,7 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
    /**
     * Returns true if the package name is the name of a module.
     */
    @AnyThread
    private boolean isModule(String packageName) {
        // Check if the package is an APK inside an APEX. If it is, use the parent APEX package when
        // querying PackageManager.
@@ -346,8 +372,10 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
     * @param rollback {@code rollbackInfo} of the {@code failedPackage}
     * @param failedPackage the package that needs to be rolled back
     */
    @WorkerThread
    private void rollbackPackage(RollbackInfo rollback, VersionedPackage failedPackage,
            @FailureReasons int rollbackReason) {
        assertInWorkerThread();
        final RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
        int reasonToLog = WatchdogRollbackLogger.mapFailureReasonToMetric(rollbackReason);
        final String failedPackageToLog;
@@ -366,7 +394,9 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
        WatchdogRollbackLogger.logEvent(logPackage,
                FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
                reasonToLog, failedPackageToLog);
        final LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {

        Consumer<Intent> onResult = result -> {
            assertInWorkerThread();
            int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
                    RollbackManager.STATUS_FAILURE);
            if (status == RollbackManager.STATUS_SUCCESS) {
@@ -395,15 +425,19 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
                                .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
                        reasonToLog, failedPackageToLog);
            }
        };

        final LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver(result -> {
            mHandler.post(() -> onResult.accept(result));
        });

        mHandler.post(() ->
        rollbackManager.commitRollback(rollback.getRollbackId(),
                        Collections.singletonList(failedPackage),
                        rollbackReceiver.getIntentSender()));
                Collections.singletonList(failedPackage), rollbackReceiver.getIntentSender());
    }

    @WorkerThread
    private void rollbackAll() {
        assertInWorkerThread();
        Slog.i(TAG, "Rolling back all available rollbacks");
        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
        List<RollbackInfo> rollbacks = rollbackManager.getAvailableRollbacks();