diff options
Diffstat (limited to 'services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java')
-rw-r--r-- | services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java | 324 |
1 files changed, 312 insertions, 12 deletions
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java index 211a83d8588e..682569f1d9ab 100644 --- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java @@ -26,6 +26,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static com.android.server.RescueParty.LEVEL_FACTORY_RESET; +import static com.android.server.RescueParty.RESCUE_LEVEL_FACTORY_RESET; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -41,9 +42,11 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; +import android.crashrecovery.flags.Flags; import android.os.RecoverySystem; import android.os.SystemProperties; import android.os.UserHandle; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.DeviceConfig; import android.provider.Settings; import android.util.ArraySet; @@ -55,6 +58,7 @@ import com.android.server.am.SettingsToPropertiesMapper; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.mockito.Answers; import org.mockito.ArgumentCaptor; @@ -69,6 +73,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; @@ -100,6 +105,9 @@ public class RescuePartyTest { private static final int THROTTLING_DURATION_MIN = 10; + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private MockitoSession mSession; private HashMap<String, String> mSystemSettingsMap; private HashMap<String, String> mCrashRecoveryPropertiesMap; @@ -267,6 +275,42 @@ public class RescuePartyTest { } @Test + public void testBootLoopDetectionWithExecutionForAllRescueLevelsRecoverabilityDetection() { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + RescueParty.onSettingsProviderPublished(mMockContext); + verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), + any(Executor.class), + mMonitorCallbackCaptor.capture())); + HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>(); + + // Record DeviceConfig accesses + DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue(); + monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1); + monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE2); + + final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2}; + + noteBoot(1); + verifyDeviceConfigReset(expectedAllResetNamespaces, verifiedTimesMap); + + noteBoot(2); + assertTrue(RescueParty.isRebootPropertySet()); + + noteBoot(3); + verifyOnlySettingsReset(Settings.RESET_MODE_UNTRUSTED_DEFAULTS); + + noteBoot(4); + verifyOnlySettingsReset(Settings.RESET_MODE_UNTRUSTED_CHANGES); + + noteBoot(5); + verifyOnlySettingsReset(Settings.RESET_MODE_TRUSTED_DEFAULTS); + + setCrashRecoveryPropAttemptingReboot(false); + noteBoot(6); + assertTrue(RescueParty.isFactoryResetPropertySet()); + } + + @Test public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevels() { noteAppCrash(1, true); @@ -292,6 +336,47 @@ public class RescuePartyTest { } @Test + public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevelsRecoverability() { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + RescueParty.onSettingsProviderPublished(mMockContext); + verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), + any(Executor.class), + mMonitorCallbackCaptor.capture())); + HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>(); + + // Record DeviceConfig accesses + DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue(); + monitorCallback.onDeviceConfigAccess(PERSISTENT_PACKAGE, NAMESPACE1); + monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1); + monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE2); + + final String[] expectedResetNamespaces = new String[]{NAMESPACE1}; + final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2}; + + noteAppCrash(1, true); + verifyDeviceConfigReset(expectedResetNamespaces, verifiedTimesMap); + + noteAppCrash(2, true); + verifyDeviceConfigReset(expectedAllResetNamespaces, verifiedTimesMap); + + noteAppCrash(3, true); + assertTrue(RescueParty.isRebootPropertySet()); + + noteAppCrash(4, true); + verifyOnlySettingsReset(Settings.RESET_MODE_UNTRUSTED_DEFAULTS); + + noteAppCrash(5, true); + verifyOnlySettingsReset(Settings.RESET_MODE_UNTRUSTED_CHANGES); + + noteAppCrash(6, true); + verifyOnlySettingsReset(Settings.RESET_MODE_TRUSTED_DEFAULTS); + + setCrashRecoveryPropAttemptingReboot(false); + noteAppCrash(7, true); + assertTrue(RescueParty.isFactoryResetPropertySet()); + } + + @Test public void testNonPersistentAppOnlyPerformsFlagResets() { noteAppCrash(1, false); @@ -316,6 +401,45 @@ public class RescuePartyTest { } @Test + public void testNonPersistentAppOnlyPerformsFlagResetsRecoverabilityDetection() { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + RescueParty.onSettingsProviderPublished(mMockContext); + verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), + any(Executor.class), + mMonitorCallbackCaptor.capture())); + HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>(); + + // Record DeviceConfig accesses + DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue(); + monitorCallback.onDeviceConfigAccess(NON_PERSISTENT_PACKAGE, NAMESPACE1); + monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1); + monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE2); + + final String[] expectedResetNamespaces = new String[]{NAMESPACE1}; + final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2}; + + noteAppCrash(1, false); + verifyDeviceConfigReset(expectedResetNamespaces, verifiedTimesMap); + + noteAppCrash(2, false); + verifyDeviceConfigReset(expectedAllResetNamespaces, verifiedTimesMap); + + noteAppCrash(3, false); + assertFalse(RescueParty.isRebootPropertySet()); + + noteAppCrash(4, false); + verifyNoSettingsReset(Settings.RESET_MODE_UNTRUSTED_DEFAULTS); + noteAppCrash(5, false); + verifyNoSettingsReset(Settings.RESET_MODE_UNTRUSTED_CHANGES); + noteAppCrash(6, false); + verifyNoSettingsReset(Settings.RESET_MODE_TRUSTED_DEFAULTS); + + setCrashRecoveryPropAttemptingReboot(false); + noteAppCrash(7, false); + assertFalse(RescueParty.isFactoryResetPropertySet()); + } + + @Test public void testNonPersistentAppCrashDetectionWithScopedResets() { RescueParty.onSettingsProviderPublished(mMockContext); verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), @@ -439,19 +563,32 @@ public class RescuePartyTest { } @Test - public void testIsAttemptingFactoryReset() { + public void testIsRecoveryTriggeredReboot() { for (int i = 0; i < LEVEL_FACTORY_RESET; i++) { noteBoot(i + 1); } assertFalse(RescueParty.isFactoryResetPropertySet()); setCrashRecoveryPropAttemptingReboot(false); noteBoot(LEVEL_FACTORY_RESET + 1); - assertTrue(RescueParty.isAttemptingFactoryReset()); + assertTrue(RescueParty.isRecoveryTriggeredReboot()); + assertTrue(RescueParty.isFactoryResetPropertySet()); + } + + @Test + public void testIsRecoveryTriggeredRebootRecoverabilityDetection() { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + for (int i = 0; i < RESCUE_LEVEL_FACTORY_RESET; i++) { + noteBoot(i + 1); + } + assertFalse(RescueParty.isFactoryResetPropertySet()); + setCrashRecoveryPropAttemptingReboot(false); + noteBoot(RESCUE_LEVEL_FACTORY_RESET + 1); + assertTrue(RescueParty.isRecoveryTriggeredReboot()); assertTrue(RescueParty.isFactoryResetPropertySet()); } @Test - public void testIsAttemptingFactoryResetOnlyAfterRebootCompleted() { + public void testIsRecoveryTriggeredRebootOnlyAfterRebootCompleted() { for (int i = 0; i < LEVEL_FACTORY_RESET; i++) { noteBoot(i + 1); } @@ -464,7 +601,26 @@ public class RescuePartyTest { noteBoot(mitigationCount++); setCrashRecoveryPropAttemptingReboot(false); noteBoot(mitigationCount + 1); - assertTrue(RescueParty.isAttemptingFactoryReset()); + assertTrue(RescueParty.isRecoveryTriggeredReboot()); + assertTrue(RescueParty.isFactoryResetPropertySet()); + } + + @Test + public void testIsRecoveryTriggeredRebootOnlyAfterRebootCompletedRecoverabilityDetection() { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + for (int i = 0; i < RESCUE_LEVEL_FACTORY_RESET; i++) { + noteBoot(i + 1); + } + int mitigationCount = RESCUE_LEVEL_FACTORY_RESET + 1; + assertFalse(RescueParty.isFactoryResetPropertySet()); + noteBoot(mitigationCount++); + assertFalse(RescueParty.isFactoryResetPropertySet()); + noteBoot(mitigationCount++); + assertFalse(RescueParty.isFactoryResetPropertySet()); + noteBoot(mitigationCount++); + setCrashRecoveryPropAttemptingReboot(false); + noteBoot(mitigationCount + 1); + assertTrue(RescueParty.isRecoveryTriggeredReboot()); assertTrue(RescueParty.isFactoryResetPropertySet()); } @@ -477,7 +633,20 @@ public class RescuePartyTest { for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) { noteBoot(i); } - assertFalse(RescueParty.isAttemptingFactoryReset()); + assertFalse(RescueParty.isRecoveryTriggeredReboot()); + } + + @Test + public void testThrottlingOnBootFailuresRecoverabilityDetection() { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + setCrashRecoveryPropAttemptingReboot(false); + long now = System.currentTimeMillis(); + long beforeTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN - 1); + setCrashRecoveryPropLastFactoryReset(beforeTimeout); + for (int i = 1; i <= RESCUE_LEVEL_FACTORY_RESET; i++) { + noteBoot(i); + } + assertFalse(RescueParty.isRecoveryTriggeredReboot()); } @Test @@ -489,7 +658,20 @@ public class RescuePartyTest { for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) { noteAppCrash(i + 1, true); } - assertFalse(RescueParty.isAttemptingFactoryReset()); + assertFalse(RescueParty.isRecoveryTriggeredReboot()); + } + + @Test + public void testThrottlingOnAppCrashRecoverabilityDetection() { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + setCrashRecoveryPropAttemptingReboot(false); + long now = System.currentTimeMillis(); + long beforeTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN - 1); + setCrashRecoveryPropLastFactoryReset(beforeTimeout); + for (int i = 0; i <= RESCUE_LEVEL_FACTORY_RESET; i++) { + noteAppCrash(i + 1, true); + } + assertFalse(RescueParty.isRecoveryTriggeredReboot()); } @Test @@ -501,8 +683,22 @@ public class RescuePartyTest { for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) { noteBoot(i); } - assertTrue(RescueParty.isAttemptingFactoryReset()); + assertTrue(RescueParty.isRecoveryTriggeredReboot()); + } + + @Test + public void testNotThrottlingAfterTimeoutOnBootFailuresRecoverabilityDetection() { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + setCrashRecoveryPropAttemptingReboot(false); + long now = System.currentTimeMillis(); + long afterTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN + 1); + setCrashRecoveryPropLastFactoryReset(afterTimeout); + for (int i = 1; i <= RESCUE_LEVEL_FACTORY_RESET; i++) { + noteBoot(i); + } + assertTrue(RescueParty.isRecoveryTriggeredReboot()); } + @Test public void testNotThrottlingAfterTimeoutOnAppCrash() { setCrashRecoveryPropAttemptingReboot(false); @@ -512,7 +708,20 @@ public class RescuePartyTest { for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) { noteAppCrash(i + 1, true); } - assertTrue(RescueParty.isAttemptingFactoryReset()); + assertTrue(RescueParty.isRecoveryTriggeredReboot()); + } + + @Test + public void testNotThrottlingAfterTimeoutOnAppCrashRecoverabilityDetection() { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + setCrashRecoveryPropAttemptingReboot(false); + long now = System.currentTimeMillis(); + long afterTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN + 1); + setCrashRecoveryPropLastFactoryReset(afterTimeout); + for (int i = 0; i <= RESCUE_LEVEL_FACTORY_RESET; i++) { + noteAppCrash(i + 1, true); + } + assertTrue(RescueParty.isRecoveryTriggeredReboot()); } @Test @@ -531,6 +740,7 @@ public class RescuePartyTest { @Test public void testExplicitlyEnablingAndDisablingRescue() { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false)); SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true)); assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, @@ -543,6 +753,7 @@ public class RescuePartyTest { @Test public void testDisablingRescueByDeviceConfigFlag() { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false)); SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(true)); @@ -568,6 +779,20 @@ public class RescuePartyTest { } @Test + public void testDisablingFactoryResetByDeviceConfigFlagRecoverabilityDetection() { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, Boolean.toString(true)); + + for (int i = 0; i < RESCUE_LEVEL_FACTORY_RESET; i++) { + noteBoot(i + 1); + } + assertFalse(RescueParty.isFactoryResetPropertySet()); + + // Restore the property value initialized in SetUp() + SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, ""); + } + + @Test public void testHealthCheckLevels() { RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); @@ -594,6 +819,46 @@ public class RescuePartyTest { } @Test + public void testHealthCheckLevelsRecoverabilityDetection() { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); + + // Ensure that no action is taken for cases where the failure reason is unknown + assertEquals(observer.onHealthCheckFailed(sFailingPackage, + PackageWatchdog.FAILURE_REASON_UNKNOWN, 1), + PackageHealthObserverImpact.USER_IMPACT_LEVEL_0); + + // Ensure the correct user impact is returned for each mitigation count. + assertEquals(observer.onHealthCheckFailed(sFailingPackage, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), + PackageHealthObserverImpact.USER_IMPACT_LEVEL_10); + + assertEquals(observer.onHealthCheckFailed(sFailingPackage, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2), + PackageHealthObserverImpact.USER_IMPACT_LEVEL_20); + + assertEquals(observer.onHealthCheckFailed(sFailingPackage, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 3), + PackageHealthObserverImpact.USER_IMPACT_LEVEL_20); + + assertEquals(observer.onHealthCheckFailed(sFailingPackage, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4), + PackageHealthObserverImpact.USER_IMPACT_LEVEL_20); + + assertEquals(observer.onHealthCheckFailed(sFailingPackage, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5), + PackageHealthObserverImpact.USER_IMPACT_LEVEL_20); + + assertEquals(observer.onHealthCheckFailed(sFailingPackage, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 6), + PackageHealthObserverImpact.USER_IMPACT_LEVEL_20); + + assertEquals(observer.onHealthCheckFailed(sFailingPackage, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 7), + PackageHealthObserverImpact.USER_IMPACT_LEVEL_20); + } + + @Test public void testBootLoopLevels() { RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); @@ -606,6 +871,19 @@ public class RescuePartyTest { } @Test + public void testBootLoopLevelsRecoverabilityDetection() { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); + + assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LEVEL_20); + assertEquals(observer.onBootLoop(2), PackageHealthObserverImpact.USER_IMPACT_LEVEL_50); + assertEquals(observer.onBootLoop(3), PackageHealthObserverImpact.USER_IMPACT_LEVEL_71); + assertEquals(observer.onBootLoop(4), PackageHealthObserverImpact.USER_IMPACT_LEVEL_75); + assertEquals(observer.onBootLoop(5), PackageHealthObserverImpact.USER_IMPACT_LEVEL_80); + assertEquals(observer.onBootLoop(6), PackageHealthObserverImpact.USER_IMPACT_LEVEL_100); + } + + @Test public void testResetDeviceConfigForPackagesOnlyRuntimeMap() { RescueParty.onSettingsProviderPublished(mMockContext); verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver), @@ -727,11 +1005,26 @@ public class RescuePartyTest { private void verifySettingsResets(int resetMode, String[] resetNamespaces, HashMap<String, Integer> configResetVerifiedTimesMap) { + verifyOnlySettingsReset(resetMode); + verifyDeviceConfigReset(resetNamespaces, configResetVerifiedTimesMap); + } + + private void verifyOnlySettingsReset(int resetMode) { verify(() -> Settings.Global.resetToDefaultsAsUser(mMockContentResolver, null, resetMode, UserHandle.USER_SYSTEM)); verify(() -> Settings.Secure.resetToDefaultsAsUser(eq(mMockContentResolver), isNull(), eq(resetMode), anyInt())); - // Verify DeviceConfig resets + } + + private void verifyNoSettingsReset(int resetMode) { + verify(() -> Settings.Global.resetToDefaultsAsUser(mMockContentResolver, null, + resetMode, UserHandle.USER_SYSTEM), never()); + verify(() -> Settings.Secure.resetToDefaultsAsUser(eq(mMockContentResolver), isNull(), + eq(resetMode), anyInt()), never()); + } + + private void verifyDeviceConfigReset(String[] resetNamespaces, + Map<String, Integer> configResetVerifiedTimesMap) { if (resetNamespaces == null) { verify(() -> DeviceConfig.resetToDefaults(anyInt(), anyString()), never()); } else { @@ -818,9 +1111,16 @@ public class RescuePartyTest { // mock properties in BootThreshold try { - mSpyBootThreshold = spy(watchdog.new BootThreshold( - PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT, - PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS)); + if (Flags.recoverabilityDetection()) { + mSpyBootThreshold = spy(watchdog.new BootThreshold( + PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT, + PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS, + PackageWatchdog.DEFAULT_BOOT_LOOP_MITIGATION_INCREMENT)); + } else { + mSpyBootThreshold = spy(watchdog.new BootThreshold( + PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT, + PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS)); + } mCrashRecoveryPropertiesMap = new HashMap<>(); doAnswer((Answer<Integer>) invocationOnMock -> { |