Merge "SystemUI as an exception to user impact level limit" into main
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index eb03709..d9926a4 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -150,6 +150,15 @@
private static final int DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD =
PackageHealthObserverImpact.USER_IMPACT_LEVEL_71;
+ // Comma separated list of all packages exempt from user impact level threshold. If a package
+ // in the list is crash looping, all the mitigations including factory reset will be performed.
+ private static final String PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD =
+ "persist.device_config.configuration.packages_exempt_from_impact_level_threshold";
+
+ // Comma separated list of default packages exempt from user impact level threshold.
+ private static final String DEFAULT_PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD =
+ "com.android.systemui";
+
private long mNumberOfNativeCrashPollsRemaining;
private static final int DB_VERSION = 1;
@@ -196,6 +205,8 @@
private final DeviceConfig.OnPropertiesChangedListener
mOnPropertyChangedListener = this::onPropertyChanged;
+ private final Set<String> mPackagesExemptFromImpactLevelThreshold = new ArraySet<>();
+
// The set of packages that have been synced with the ExplicitHealthCheckController
@GuardedBy("mLock")
private Set<String> mRequestedHealthCheckPackages = new ArraySet<>();
@@ -518,7 +529,7 @@
@FailureReasons int failureReason,
int currentObserverImpact,
int mitigationCount) {
- if (currentObserverImpact < getUserImpactLevelLimit()) {
+ if (allowMitigations(currentObserverImpact, versionedPackage)) {
synchronized (mLock) {
mLastMitigation = mSystemClock.uptimeMillis();
}
@@ -526,6 +537,13 @@
}
}
+ private boolean allowMitigations(int currentObserverImpact,
+ VersionedPackage versionedPackage) {
+ return currentObserverImpact < getUserImpactLevelLimit()
+ || getPackagesExemptFromImpactLevelThreshold().contains(
+ versionedPackage.getPackageName());
+ }
+
private long getMitigationWindowMs() {
return SystemProperties.getLong(MITIGATION_WINDOW_MS, DEFAULT_MITIGATION_WINDOW_MS);
}
@@ -657,6 +675,15 @@
DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD);
}
+ private Set<String> getPackagesExemptFromImpactLevelThreshold() {
+ if (mPackagesExemptFromImpactLevelThreshold.isEmpty()) {
+ String packageNames = SystemProperties.get(PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD,
+ DEFAULT_PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD);
+ return Set.of(packageNames.split("\\s*,\\s*"));
+ }
+ return mPackagesExemptFromImpactLevelThreshold;
+ }
+
/** Possible severity values of the user impact of a {@link PackageHealthObserver#execute}. */
@Retention(SOURCE)
@IntDef(value = {PackageHealthObserverImpact.USER_IMPACT_LEVEL_0,
diff --git a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
index 3722fef..c0e90f9 100644
--- a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
@@ -35,6 +35,7 @@
import android.Manifest;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
@@ -77,6 +78,7 @@
import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -413,6 +415,311 @@
verify(rescuePartyObserver, never()).executeBootLoopMitigation(2);
}
+ @Test
+ @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+ public void testCrashLoopWithRescuePartyAndRollbackObserver() throws Exception {
+ PackageWatchdog watchdog = createWatchdog();
+ RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog);
+ RollbackPackageHealthObserver rollbackObserver =
+ setUpRollbackPackageHealthObserver(watchdog);
+ VersionedPackage versionedPackageA = new VersionedPackage(APP_A, VERSION_CODE);
+
+ when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).then(inv -> {
+ ApplicationInfo info = new ApplicationInfo();
+ info.flags |= ApplicationInfo.FLAG_PERSISTENT
+ | ApplicationInfo.FLAG_SYSTEM;
+ return info;
+ });
+
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+ // Mitigation: SCOPED_DEVICE_CONFIG_RESET
+ verify(rescuePartyObserver).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ verify(rescuePartyObserver, never()).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+ verify(rollbackObserver, never()).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+ // Mitigation: ALL_DEVICE_CONFIG_RESET
+ verify(rescuePartyObserver).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+ verify(rescuePartyObserver, never()).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
+ verify(rollbackObserver, never()).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+ // Mitigation: WARM_REBOOT
+ verify(rescuePartyObserver).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
+ verify(rescuePartyObserver, never()).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
+ verify(rollbackObserver, never()).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+ // Mitigation: Low impact rollback
+ verify(rollbackObserver).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ verify(rescuePartyObserver, never()).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
+
+ // update available rollbacks to mock rollbacks being applied after the call to
+ // rollbackObserver.execute
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+ List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL));
+
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+ // DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD reached. No more mitigations applied
+ verify(rescuePartyObserver, never()).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
+ verify(rollbackObserver, never()).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+ public void testCrashLoopWithRescuePartyAndRollbackObserverEnableDeprecateFlagReset()
+ throws Exception {
+ PackageWatchdog watchdog = createWatchdog();
+ RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog);
+ RollbackPackageHealthObserver rollbackObserver =
+ setUpRollbackPackageHealthObserver(watchdog);
+ VersionedPackage versionedPackageA = new VersionedPackage(APP_A, VERSION_CODE);
+
+ when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).then(inv -> {
+ ApplicationInfo info = new ApplicationInfo();
+ info.flags |= ApplicationInfo.FLAG_PERSISTENT
+ | ApplicationInfo.FLAG_SYSTEM;
+ return info;
+ });
+
+
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+ // Mitigation: WARM_REBOOT
+ verify(rescuePartyObserver).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ verify(rescuePartyObserver, never()).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+ verify(rollbackObserver, never()).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+ // Mitigation: Low impact rollback
+ verify(rollbackObserver).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ verify(rescuePartyObserver, never()).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+
+ // update available rollbacks to mock rollbacks being applied after the call to
+ // rollbackObserver.execute
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+ List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL));
+
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+ // DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD reached. No more mitigations applied
+ verify(rescuePartyObserver, never()).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+ verify(rollbackObserver, never()).execute(versionedPackageA,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+ public void testCrashLoopSystemUIWithRescuePartyAndRollbackObserver() throws Exception {
+ PackageWatchdog watchdog = createWatchdog();
+ RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog);
+ RollbackPackageHealthObserver rollbackObserver =
+ setUpRollbackPackageHealthObserver(watchdog);
+ String systemUi = "com.android.systemui";
+ VersionedPackage versionedPackageUi = new VersionedPackage(
+ systemUi, VERSION_CODE);
+ RollbackInfo rollbackInfoUi = getRollbackInfo(systemUi, VERSION_CODE, 1,
+ PackageManager.ROLLBACK_USER_IMPACT_LOW);
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_LOW,
+ ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL, rollbackInfoUi));
+
+ when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).then(inv -> {
+ ApplicationInfo info = new ApplicationInfo();
+ info.flags |= ApplicationInfo.FLAG_PERSISTENT
+ | ApplicationInfo.FLAG_SYSTEM;
+ return info;
+ });
+
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+ // Mitigation: SCOPED_DEVICE_CONFIG_RESET
+ verify(rescuePartyObserver).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+ verify(rollbackObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+ // Mitigation: ALL_DEVICE_CONFIG_RESET
+ verify(rescuePartyObserver).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+ verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
+ verify(rollbackObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+ // Mitigation: WARM_REBOOT
+ verify(rescuePartyObserver).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
+ verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
+ verify(rollbackObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+ // Mitigation: Low impact rollback
+ verify(rollbackObserver).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
+ verify(rollbackObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+
+ // update available rollbacks to mock rollbacks being applied after the call to
+ // rollbackObserver.execute
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+ List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL));
+
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+ // Mitigation: RESET_SETTINGS_UNTRUSTED_DEFAULTS
+ verify(rescuePartyObserver).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
+ verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 5);
+ verify(rollbackObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+ // Mitigation: RESET_SETTINGS_UNTRUSTED_CHANGES
+ verify(rescuePartyObserver).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 5);
+ verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 6);
+ verify(rollbackObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+ // Mitigation: RESET_SETTINGS_TRUSTED_DEFAULTS
+ verify(rescuePartyObserver).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 6);
+ verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 7);
+ verify(rollbackObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+ // Mitigation: Factory reset. High impact rollbacks are performed only for boot loops.
+ verify(rescuePartyObserver).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 7);
+ verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 8);
+ verify(rollbackObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+ public void testCrashLoopSystemUIWithRescuePartyAndRollbackObserverEnableDeprecateFlagReset()
+ throws Exception {
+ PackageWatchdog watchdog = createWatchdog();
+ RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog);
+ RollbackPackageHealthObserver rollbackObserver =
+ setUpRollbackPackageHealthObserver(watchdog);
+ String systemUi = "com.android.systemui";
+ VersionedPackage versionedPackageUi = new VersionedPackage(
+ systemUi, VERSION_CODE);
+ RollbackInfo rollbackInfoUi = getRollbackInfo(systemUi, VERSION_CODE, 1,
+ PackageManager.ROLLBACK_USER_IMPACT_LOW);
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_LOW,
+ ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL, rollbackInfoUi));
+
+ when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).then(inv -> {
+ ApplicationInfo info = new ApplicationInfo();
+ info.flags |= ApplicationInfo.FLAG_PERSISTENT
+ | ApplicationInfo.FLAG_SYSTEM;
+ return info;
+ });
+
+
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+ // Mitigation: WARM_REBOOT
+ verify(rescuePartyObserver).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+ verify(rollbackObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+ // Mitigation: Low impact rollback
+ verify(rollbackObserver).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+ verify(rollbackObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+
+ // update available rollbacks to mock rollbacks being applied after the call to
+ // rollbackObserver.execute
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+ List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL));
+
+ raiseFatalFailureAndDispatch(watchdog,
+ Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+ // Mitigation: Factory reset. High impact rollbacks are performed only for boot loops.
+ verify(rescuePartyObserver).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+ verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
+ verify(rollbackObserver, never()).execute(versionedPackageUi,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+ }
+
RollbackPackageHealthObserver setUpRollbackPackageHealthObserver(PackageWatchdog watchdog) {
RollbackPackageHealthObserver rollbackObserver =
spy(new RollbackPackageHealthObserver(mSpyContext, mApexManager));
@@ -424,7 +731,6 @@
watchdog.registerHealthObserver(rollbackObserver);
return rollbackObserver;
}
-
RescuePartyObserver setUpRescuePartyObserver(PackageWatchdog watchdog) {
setCrashRecoveryPropRescueBootCount(0);
RescuePartyObserver rescuePartyObserver = spy(RescuePartyObserver.getInstance(mSpyContext));
@@ -686,4 +992,20 @@
mTestLooper.moveTimeForward(milliSeconds);
mTestLooper.dispatchAll();
}
+
+ private void raiseFatalFailureAndDispatch(PackageWatchdog watchdog,
+ List<VersionedPackage> packages, int failureReason) {
+ long triggerFailureCount = watchdog.getTriggerFailureCount();
+ if (failureReason == PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK
+ || failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
+ triggerFailureCount = 1;
+ }
+ for (int i = 0; i < triggerFailureCount; i++) {
+ watchdog.onPackageFailure(packages, failureReason);
+ }
+ mTestLooper.dispatchAll();
+ if (Flags.recoverabilityDetection()) {
+ moveTimeForwardAndDispatch(watchdog.DEFAULT_MITIGATION_WINDOW_MS);
+ }
+ }
}