Merge "Adding reboot throttling" into udc-dev

This commit is contained in:
Alexei Nicoara
2023-03-15 11:32:24 +00:00
committed by Android (Google) Code Review
2 changed files with 81 additions and 12 deletions

View File

@@ -79,6 +79,7 @@ public class RescueParty {
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";
static final String PROP_LAST_FACTORY_RESET_TIME_MS = "persist.sys.last_factory_reset";
@VisibleForTesting
static final int LEVEL_NONE = 0;
@VisibleForTesting
@@ -105,10 +106,11 @@ public class RescueParty {
@VisibleForTesting
static final String NAMESPACE_TO_PACKAGE_MAPPING_FLAG =
"namespace_to_package_mapping";
@VisibleForTesting
static final long FACTORY_RESET_THROTTLE_DURATION_MS = TimeUnit.MINUTES.toMillis(10);
private static final String NAME = "rescue-party-observer";
private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
private static final String PROP_VIRTUAL_DEVICE = "ro.hardware.virtual_device";
private static final String PROP_DEVICE_CONFIG_DISABLE_FLAG =
@@ -327,8 +329,8 @@ public class RescueParty {
}
}
private static int getMaxRescueLevel(boolean mayPerformFactoryReset) {
if (!mayPerformFactoryReset
private static int getMaxRescueLevel(boolean mayPerformReboot) {
if (!mayPerformReboot
|| SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)) {
return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS;
}
@@ -339,11 +341,11 @@ public class RescueParty {
* Get the rescue level to perform if this is the n-th attempt at mitigating failure.
*
* @param mitigationCount: the mitigation attempt number (1 = first attempt etc.)
* @param mayPerformFactoryReset: whether or not a factory reset may be performed for the given
* failure.
* @param mayPerformReboot: whether or not a reboot and factory reset may be performed
* for the given failure.
* @return the rescue level for the n-th mitigation attempt.
*/
private static int getRescueLevel(int mitigationCount, boolean mayPerformFactoryReset) {
private static int getRescueLevel(int mitigationCount, boolean mayPerformReboot) {
if (mitigationCount == 1) {
return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS;
} else if (mitigationCount == 2) {
@@ -351,9 +353,9 @@ public class RescueParty {
} else if (mitigationCount == 3) {
return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS;
} else if (mitigationCount == 4) {
return Math.min(getMaxRescueLevel(mayPerformFactoryReset), LEVEL_WARM_REBOOT);
return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_WARM_REBOOT);
} else if (mitigationCount >= 5) {
return Math.min(getMaxRescueLevel(mayPerformFactoryReset), LEVEL_FACTORY_RESET);
return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_FACTORY_RESET);
} else {
Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount);
return LEVEL_NONE;
@@ -450,6 +452,8 @@ public class RescueParty {
break;
}
SystemProperties.set(PROP_ATTEMPTING_FACTORY_RESET, "true");
long now = System.currentTimeMillis();
SystemProperties.set(PROP_LAST_FACTORY_RESET_TIME_MS, Long.toString(now));
runnable = new Runnable() {
@Override
public void run() {
@@ -627,7 +631,7 @@ public class RescueParty {
if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
|| failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) {
return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount,
mayPerformFactoryReset(failedPackage)));
mayPerformReboot(failedPackage)));
} else {
return PackageHealthObserverImpact.USER_IMPACT_NONE;
}
@@ -642,7 +646,7 @@ public class RescueParty {
if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
|| failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
final int level = getRescueLevel(mitigationCount,
mayPerformFactoryReset(failedPackage));
mayPerformReboot(failedPackage));
executeRescueLevel(mContext,
failedPackage == null ? null : failedPackage.getPackageName(), level);
return true;
@@ -683,8 +687,9 @@ public class RescueParty {
if (isDisabled()) {
return false;
}
boolean mayPerformReboot = !shouldThrottleReboot();
executeRescueLevel(mContext, /*failedPackage=*/ null,
getRescueLevel(mitigationCount, true));
getRescueLevel(mitigationCount, mayPerformReboot));
return true;
}
@@ -698,14 +703,27 @@ public class RescueParty {
* prompting a factory reset is an acceptable mitigation strategy for the package's
* failure, {@code false} otherwise.
*/
private boolean mayPerformFactoryReset(@Nullable VersionedPackage failingPackage) {
private boolean mayPerformReboot(@Nullable VersionedPackage failingPackage) {
if (failingPackage == null) {
return false;
}
if (shouldThrottleReboot()) {
return false;
}
return isPersistentSystemApp(failingPackage.getPackageName());
}
/**
* Returns {@code true} if Rescue Party is allowed to attempt a reboot or factory reset.
* Will return {@code false} if a factory reset was already offered recently.
*/
private boolean shouldThrottleReboot() {
Long lastResetTime = SystemProperties.getLong(PROP_LAST_FACTORY_RESET_TIME_MS, 0);
long now = System.currentTimeMillis();
return now < lastResetTime + FACTORY_RESET_THROTTLE_DURATION_MS;
}
private boolean isPersistentSystemApp(@NonNull String packageName) {
PackageManager pm = mContext.getPackageManager();
try {

View File

@@ -68,6 +68,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
/**
* Test RescueParty.
@@ -94,6 +95,9 @@ public class RescuePartyTest {
"persist.device_config.configuration.disable_rescue_party";
private static final String PROP_DISABLE_FACTORY_RESET_FLAG =
"persist.device_config.configuration.disable_rescue_party_factory_reset";
private static final String PROP_LAST_FACTORY_RESET_TIME_MS = "persist.sys.last_factory_reset";
private static final int THROTTLING_DURATION_MIN = 10;
private MockitoSession mSession;
private HashMap<String, String> mSystemSettingsMap;
@@ -458,6 +462,53 @@ public class RescuePartyTest {
assertTrue(RescueParty.isFactoryResetPropertySet());
}
@Test
public void testThrottlingOnBootFailures() {
SystemProperties.set(RescueParty.PROP_ATTEMPTING_REBOOT, Boolean.toString(false));
long now = System.currentTimeMillis();
long beforeTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN - 1);
SystemProperties.set(PROP_LAST_FACTORY_RESET_TIME_MS, Long.toString(beforeTimeout));
for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) {
noteBoot(i);
}
assertFalse(RescueParty.isAttemptingFactoryReset());
}
@Test
public void testThrottlingOnAppCrash() {
SystemProperties.set(RescueParty.PROP_ATTEMPTING_REBOOT, Boolean.toString(false));
long now = System.currentTimeMillis();
long beforeTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN - 1);
SystemProperties.set(PROP_LAST_FACTORY_RESET_TIME_MS, Long.toString(beforeTimeout));
for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) {
noteAppCrash(i + 1, true);
}
assertFalse(RescueParty.isAttemptingFactoryReset());
}
@Test
public void testNotThrottlingAfterTimeoutOnBootFailures() {
SystemProperties.set(RescueParty.PROP_ATTEMPTING_REBOOT, Boolean.toString(false));
long now = System.currentTimeMillis();
long afterTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN + 1);
SystemProperties.set(PROP_LAST_FACTORY_RESET_TIME_MS, Long.toString(afterTimeout));
for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) {
noteBoot(i);
}
assertTrue(RescueParty.isAttemptingFactoryReset());
}
@Test
public void testNotThrottlingAfterTimeoutOnAppCrash() {
SystemProperties.set(RescueParty.PROP_ATTEMPTING_REBOOT, Boolean.toString(false));
long now = System.currentTimeMillis();
long afterTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN + 1);
SystemProperties.set(PROP_LAST_FACTORY_RESET_TIME_MS, Long.toString(afterTimeout));
for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) {
noteAppCrash(i + 1, true);
}
assertTrue(RescueParty.isAttemptingFactoryReset());
}
@Test
public void testNativeRescuePartyResets() {
doReturn(true).when(() -> SettingsToPropertiesMapper.isNativeFlagsResetPerformed());