Merge "Add reboot step to Rescue Party"

This commit is contained in:
Gavin Corkery
2021-01-12 09:52:14 +00:00
committed by Android (Google) Code Review
3 changed files with 95 additions and 12 deletions

View File

@@ -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.");

View File

@@ -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);
}

View File

@@ -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, "");