Merge "Add reboot step to Rescue Party"
This commit is contained in:
committed by
Android (Google) Code Review
commit
3e99a4a68a
@@ -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.");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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, "");
|
||||
|
||||
Reference in New Issue
Block a user