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

Commit 3e99a4a6 authored by Gavin Corkery's avatar Gavin Corkery Committed by Android (Google) Code Review
Browse files

Merge "Add reboot step to Rescue Party"

parents e2cdacf4 44dfb6e7
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
@@ -54,9 +54,13 @@ import libcore.io.IoUtils;

import org.xmlpull.v1.XmlPullParserException;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Retention;
@@ -149,6 +153,11 @@ public class PackageWatchdog {
    private static final String ATTR_PASSED_HEALTH_CHECK = "passed-health-check";
    private static final String ATTR_MITIGATION_CALLS = "mitigation-calls";

    // A file containing information about the current mitigation count in the case of a boot loop.
    // This allows boot loop information to persist in the case of an fs-checkpoint being
    // aborted.
    private static final String METADATA_FILE = "/metadata/watchdog/mitigation_count.txt";

    @GuardedBy("PackageWatchdog.class")
    private static PackageWatchdog sPackageWatchdog;

@@ -492,6 +501,7 @@ public class PackageWatchdog {
                }
                if (currentObserverToNotify != null) {
                    mBootThreshold.setMitigationCount(mitigationCount);
                    mBootThreshold.saveMitigationCountToMetadata();
                    currentObserverToNotify.executeBootLoopMitigation(mitigationCount);
                }
            }
@@ -1700,9 +1710,31 @@ public class PackageWatchdog {
            SystemProperties.set(property, Long.toString(newStart));
        }

        public void saveMitigationCountToMetadata() {
            try (BufferedWriter writer = new BufferedWriter(new FileWriter(METADATA_FILE))) {
                writer.write(String.valueOf(getMitigationCount()));
            } catch (Exception e) {
                Slog.e(TAG, "Could not save metadata to file: " + e);
            }
        }

        public void readMitigationCountFromMetadataIfNecessary() {
            File bootPropsFile = new File(METADATA_FILE);
            if (bootPropsFile.exists()) {
                try (BufferedReader reader = new BufferedReader(new FileReader(METADATA_FILE))) {
                    String mitigationCount = reader.readLine();
                    setMitigationCount(Integer.parseInt(mitigationCount));
                    bootPropsFile.delete();
                } catch (Exception e) {
                    Slog.i(TAG, "Could not read metadata file: " + e);
                }
            }
        }


        /** Increments the boot counter, and returns whether the device is bootlooping. */
        public boolean incrementAndTest() {
            readMitigationCountFromMetadataIfNecessary();
            final long now = mSystemClock.uptimeMillis();
            if (now - getStart() < 0) {
                Slog.e(TAG, "Window was less than zero. Resetting start to current time.");
+44 −7
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
import android.os.PowerManager;
import android.os.RecoverySystem;
import android.os.RemoteCallback;
import android.os.SystemClock;
@@ -77,6 +78,7 @@ public class RescueParty {
    @VisibleForTesting
    static final String PROP_ENABLE_RESCUE = "persist.sys.enable_rescue";
    static final String PROP_ATTEMPTING_FACTORY_RESET = "sys.attempting_factory_reset";
    static final String PROP_ATTEMPTING_REBOOT = "sys.attempting_reboot";
    static final String PROP_MAX_RESCUE_LEVEL_ATTEMPTED = "sys.max_rescue_level_attempted";
    @VisibleForTesting
    static final int LEVEL_NONE = 0;
@@ -87,7 +89,9 @@ public class RescueParty {
    @VisibleForTesting
    static final int LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS = 3;
    @VisibleForTesting
    static final int LEVEL_FACTORY_RESET = 4;
    static final int LEVEL_WARM_REBOOT = 4;
    @VisibleForTesting
    static final int LEVEL_FACTORY_RESET = 5;
    @VisibleForTesting
    static final String PROP_RESCUE_BOOT_COUNT = "sys.rescue_boot_count";
    @VisibleForTesting
@@ -159,12 +163,24 @@ public class RescueParty {
    }

    /**
     * Check if we're currently attempting to reboot for a factory reset.
     * Check if we're currently attempting to reboot for a factory reset. This method must
     * return true if RescueParty tries to reboot early during a boot loop, since the device
     * will not be fully booted at this time.
     *
     * TODO(gavincorkery): Rename method since its scope has expanded.
     */
    public static boolean isAttemptingFactoryReset() {
        return isFactoryResetPropertySet() || isRebootPropertySet();
    }

    static boolean isFactoryResetPropertySet() {
        return SystemProperties.getBoolean(PROP_ATTEMPTING_FACTORY_RESET, false);
    }

    static boolean isRebootPropertySet() {
        return SystemProperties.getBoolean(PROP_ATTEMPTING_REBOOT, false);
    }

    /**
     * Called when {@code SettingsProvider} has been published, which is a good
     * opportunity to reset any settings depending on our rescue level.
@@ -329,8 +345,10 @@ public class RescueParty {
            return LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES;
        } else if (mitigationCount == 3) {
            return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS;
        } else if (mitigationCount >= 4) {
            return getMaxRescueLevel();
        } else if (mitigationCount == 4) {
            return Math.min(getMaxRescueLevel(), LEVEL_WARM_REBOOT);
        } else if (mitigationCount >= 5) {
            return Math.min(getMaxRescueLevel(), LEVEL_FACTORY_RESET);
        } else {
            Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount);
            return LEVEL_NONE;
@@ -356,6 +374,8 @@ public class RescueParty {
        // Try our best to reset all settings possible, and once finished
        // rethrow any exception that we encountered
        Exception res = null;
        Runnable runnable;
        Thread thread;
        switch (level) {
            case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
                try {
@@ -396,11 +416,26 @@ public class RescueParty {
                    res = e;
                }
                break;
            case LEVEL_FACTORY_RESET:
            case LEVEL_WARM_REBOOT:
                // Request the reboot from a separate thread to avoid deadlock on PackageWatchdog
                // when device shutting down.
                SystemProperties.set(PROP_ATTEMPTING_REBOOT, "true");
                runnable = () -> {
                    try {
                        PowerManager pm = context.getSystemService(PowerManager.class);
                        if (pm != null) {
                            pm.reboot(TAG);
                        }
                    } catch (Throwable t) {
                        logRescueException(level, t);
                    }
                };
                thread = new Thread(runnable);
                thread.start();
                break;
            case LEVEL_FACTORY_RESET:
                SystemProperties.set(PROP_ATTEMPTING_FACTORY_RESET, "true");
                Runnable runnable = new Runnable() {
                runnable = new Runnable() {
                    @Override
                    public void run() {
                        try {
@@ -410,7 +445,7 @@ public class RescueParty {
                        }
                    }
                };
                Thread thread = new Thread(runnable);
                thread = new Thread(runnable);
                thread.start();
                break;
        }
@@ -433,6 +468,7 @@ public class RescueParty {
            case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
                return PackageHealthObserverImpact.USER_IMPACT_LOW;
            case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
            case LEVEL_WARM_REBOOT:
            case LEVEL_FACTORY_RESET:
                return PackageHealthObserverImpact.USER_IMPACT_HIGH;
            default:
@@ -714,6 +750,7 @@ public class RescueParty {
            case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: return "RESET_SETTINGS_UNTRUSTED_DEFAULTS";
            case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: return "RESET_SETTINGS_UNTRUSTED_CHANGES";
            case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: return "RESET_SETTINGS_TRUSTED_DEFAULTS";
            case LEVEL_WARM_REBOOT: return "WARM_REBOOT";
            case LEVEL_FACTORY_RESET: return "FACTORY_RESET";
            default: return Integer.toString(level);
        }
+19 −5
Original line number Diff line number Diff line
@@ -233,8 +233,10 @@ public class RescuePartyTest {
                verifiedTimesMap);

        noteBoot(4);
        assertTrue(RescueParty.isRebootPropertySet());

        assertTrue(RescueParty.isAttemptingFactoryReset());
        noteBoot(5);
        assertTrue(RescueParty.isFactoryResetPropertySet());
    }

    @Test
@@ -255,7 +257,10 @@ public class RescuePartyTest {
                /*configResetVerifiedTimesMap=*/ null);

        notePersistentAppCrash(4);
        assertTrue(RescueParty.isAttemptingFactoryReset());
        assertTrue(RescueParty.isRebootPropertySet());

        notePersistentAppCrash(5);
        assertTrue(RescueParty.isFactoryResetPropertySet());
    }

    @Test
@@ -306,7 +311,11 @@ public class RescuePartyTest {

        observer.execute(new VersionedPackage(
                CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4);
        assertTrue(RescueParty.isAttemptingFactoryReset());
        assertTrue(RescueParty.isRebootPropertySet());

        observer.execute(new VersionedPackage(
                CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5);
        assertTrue(RescueParty.isFactoryResetPropertySet());
    }

    @Test
@@ -367,7 +376,11 @@ public class RescuePartyTest {

        observer.execute(new VersionedPackage(
                CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4);
        assertTrue(RescueParty.isAttemptingFactoryReset());
        assertTrue(RescueParty.isRebootPropertySet());

        observer.execute(new VersionedPackage(
                CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5);
        assertTrue(RescueParty.isFactoryResetPropertySet());
    }

    @Test
@@ -376,6 +389,7 @@ public class RescuePartyTest {
            noteBoot(i + 1);
        }
        assertTrue(RescueParty.isAttemptingFactoryReset());
        assertTrue(RescueParty.isFactoryResetPropertySet());
    }

    @Test
@@ -424,7 +438,7 @@ public class RescuePartyTest {
        for (int i = 0; i < LEVEL_FACTORY_RESET; i++) {
            noteBoot(i + 1);
        }
        assertFalse(RescueParty.isAttemptingFactoryReset());
        assertFalse(RescueParty.isFactoryResetPropertySet());

        // Restore the property value initialized in SetUp()
        SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, "");