Loading packages/CrashRecovery/services/java/com/android/server/RescueParty.java +32 −7 Original line number Diff line number Diff line Loading @@ -74,6 +74,8 @@ import java.util.concurrent.TimeUnit; * @hide */ public class RescueParty { @VisibleForTesting static final String PROP_ENABLE_RESCUE = "persist.sys.enable_rescue"; @VisibleForTesting static final int LEVEL_NONE = 0; @VisibleForTesting Loading Loading @@ -123,7 +125,7 @@ public class RescueParty { private static boolean isDisabled() { // Check if we're explicitly enabled for testing if (CrashRecoveryProperties.enableRescueParty().orElse(false)) { if (SystemProperties.getBoolean(PROP_ENABLE_RESCUE, false)) { return false; } Loading Loading @@ -176,6 +178,29 @@ public class RescueParty { return CrashRecoveryProperties.attemptingReboot().orElse(false); } protected static long getLastFactoryResetTimeMs() { return CrashRecoveryProperties.lastFactoryResetTimeMs().orElse(0L); } protected static int getMaxRescueLevelAttempted() { return CrashRecoveryProperties.maxRescueLevelAttempted().orElse(LEVEL_NONE); } protected static void setFactoryResetProperty(boolean value) { CrashRecoveryProperties.attemptingFactoryReset(value); } protected static void setRebootProperty(boolean value) { CrashRecoveryProperties.attemptingReboot(value); } protected static void setLastFactoryResetTimeMs(long value) { CrashRecoveryProperties.lastFactoryResetTimeMs(value); } protected static void setMaxRescueLevelAttempted(int level) { CrashRecoveryProperties.maxRescueLevelAttempted(level); } /** * Called when {@code SettingsProvider} has been published, which is a good * opportunity to reset any settings depending on our rescue level. Loading Loading @@ -432,7 +457,7 @@ public class RescueParty { case LEVEL_WARM_REBOOT: // Request the reboot from a separate thread to avoid deadlock on PackageWatchdog // when device shutting down. CrashRecoveryProperties.attemptingReboot(true); setRebootProperty(true); runnable = () -> { try { PowerManager pm = context.getSystemService(PowerManager.class); Loading @@ -454,9 +479,9 @@ public class RescueParty { if (isRebootPropertySet()) { break; } CrashRecoveryProperties.attemptingFactoryReset(true); setFactoryResetProperty(true); long now = System.currentTimeMillis(); CrashRecoveryProperties.lastFactoryResetTimeMs(now); setLastFactoryResetTimeMs(now); runnable = new Runnable() { @Override public void run() { Loading Loading @@ -515,10 +540,10 @@ public class RescueParty { private static void resetAllSettingsIfNecessary(Context context, int mode, int level) throws Exception { // No need to reset Settings again if they are already reset in the current level once. if (CrashRecoveryProperties.maxRescueLevelAttempted().orElse(LEVEL_NONE) >= level) { if (getMaxRescueLevelAttempted() >= level) { return; } CrashRecoveryProperties.maxRescueLevelAttempted(level); setMaxRescueLevelAttempted(level); // Try our best to reset all settings possible, and once finished // rethrow any exception that we encountered Exception res = null; Loading Loading @@ -733,7 +758,7 @@ public class RescueParty { * Will return {@code false} if a factory reset was already offered recently. */ private boolean shouldThrottleReboot() { Long lastResetTime = CrashRecoveryProperties.lastFactoryResetTimeMs().orElse(0L); Long lastResetTime = getLastFactoryResetTimeMs(); long now = System.currentTimeMillis(); long throttleDurationMin = SystemProperties.getLong(PROP_THROTTLE_DURATION_MIN_FLAG, DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN); Loading services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java +159 −19 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import android.content.ContentResolver; Loading @@ -45,7 +46,6 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.Settings; import android.sysprop.CrashRecoveryProperties; import android.util.ArraySet; import com.android.dx.mockito.inline.extended.ExtendedMockito; Loading @@ -64,6 +64,7 @@ import org.mockito.MockitoSession; import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.lang.reflect.Field; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; Loading Loading @@ -101,6 +102,7 @@ public class RescuePartyTest { private MockitoSession mSession; private HashMap<String, String> mSystemSettingsMap; private HashMap<String, String> mCrashRecoveryPropertiesMap; //Records the namespaces wiped by setProperties(). private HashSet<String> mNamespacesWiped; Loading @@ -113,6 +115,9 @@ public class RescuePartyTest { @Mock(answer = Answers.RETURNS_DEEP_STUBS) private PackageManager mPackageManager; // Mock only sysprop apis private PackageWatchdog.BootThreshold mSpyBootThreshold; @Captor private ArgumentCaptor<DeviceConfig.MonitorCallback> mMonitorCallbackCaptor; @Captor Loading Loading @@ -208,11 +213,12 @@ public class RescuePartyTest { // Mock PackageWatchdog doAnswer((Answer<PackageWatchdog>) invocationOnMock -> mMockPackageWatchdog) .when(() -> PackageWatchdog.getInstance(mMockContext)); mockCrashRecoveryProperties(mMockPackageWatchdog); doReturn(CURRENT_NETWORK_TIME_MILLIS).when(() -> RescueParty.getElapsedRealtime()); CrashRecoveryProperties.rescueBootCount(0); CrashRecoveryProperties.enableRescueParty(true); setCrashRecoveryPropRescueBootCount(0); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true)); SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false)); } Loading Loading @@ -255,7 +261,7 @@ public class RescuePartyTest { noteBoot(4); assertTrue(RescueParty.isRebootPropertySet()); CrashRecoveryProperties.attemptingReboot(false); setCrashRecoveryPropAttemptingReboot(false); noteBoot(5); assertTrue(RescueParty.isFactoryResetPropertySet()); } Loading @@ -280,7 +286,7 @@ public class RescuePartyTest { noteAppCrash(4, true); assertTrue(RescueParty.isRebootPropertySet()); CrashRecoveryProperties.attemptingReboot(false); setCrashRecoveryPropAttemptingReboot(false); noteAppCrash(5, true); assertTrue(RescueParty.isFactoryResetPropertySet()); } Loading Loading @@ -438,7 +444,7 @@ public class RescuePartyTest { noteBoot(i + 1); } assertFalse(RescueParty.isFactoryResetPropertySet()); CrashRecoveryProperties.attemptingReboot(false); setCrashRecoveryPropAttemptingReboot(false); noteBoot(LEVEL_FACTORY_RESET + 1); assertTrue(RescueParty.isAttemptingFactoryReset()); assertTrue(RescueParty.isFactoryResetPropertySet()); Loading @@ -456,7 +462,7 @@ public class RescuePartyTest { noteBoot(mitigationCount++); assertFalse(RescueParty.isFactoryResetPropertySet()); noteBoot(mitigationCount++); CrashRecoveryProperties.attemptingReboot(false); setCrashRecoveryPropAttemptingReboot(false); noteBoot(mitigationCount + 1); assertTrue(RescueParty.isAttemptingFactoryReset()); assertTrue(RescueParty.isFactoryResetPropertySet()); Loading @@ -464,10 +470,10 @@ public class RescuePartyTest { @Test public void testThrottlingOnBootFailures() { CrashRecoveryProperties.attemptingReboot(false); setCrashRecoveryPropAttemptingReboot(false); long now = System.currentTimeMillis(); long beforeTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN - 1); CrashRecoveryProperties.lastFactoryResetTimeMs(beforeTimeout); setCrashRecoveryPropLastFactoryReset(beforeTimeout); for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) { noteBoot(i); } Loading @@ -476,10 +482,10 @@ public class RescuePartyTest { @Test public void testThrottlingOnAppCrash() { CrashRecoveryProperties.attemptingReboot(false); setCrashRecoveryPropAttemptingReboot(false); long now = System.currentTimeMillis(); long beforeTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN - 1); CrashRecoveryProperties.lastFactoryResetTimeMs(beforeTimeout); setCrashRecoveryPropLastFactoryReset(beforeTimeout); for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) { noteAppCrash(i + 1, true); } Loading @@ -488,10 +494,10 @@ public class RescuePartyTest { @Test public void testNotThrottlingAfterTimeoutOnBootFailures() { CrashRecoveryProperties.attemptingReboot(false); setCrashRecoveryPropAttemptingReboot(false); long now = System.currentTimeMillis(); long afterTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN + 1); CrashRecoveryProperties.lastFactoryResetTimeMs(afterTimeout); setCrashRecoveryPropLastFactoryReset(afterTimeout); for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) { noteBoot(i); } Loading @@ -499,10 +505,10 @@ public class RescuePartyTest { } @Test public void testNotThrottlingAfterTimeoutOnAppCrash() { CrashRecoveryProperties.attemptingReboot(false); setCrashRecoveryPropAttemptingReboot(false); long now = System.currentTimeMillis(); long afterTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN + 1); CrashRecoveryProperties.lastFactoryResetTimeMs(afterTimeout); setCrashRecoveryPropLastFactoryReset(afterTimeout); for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) { noteAppCrash(i + 1, true); } Loading @@ -525,26 +531,26 @@ public class RescuePartyTest { @Test public void testExplicitlyEnablingAndDisablingRescue() { CrashRecoveryProperties.enableRescueParty(false); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false)); SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true)); assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false); CrashRecoveryProperties.enableRescueParty(true); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true)); assertTrue(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1)); } @Test public void testDisablingRescueByDeviceConfigFlag() { CrashRecoveryProperties.enableRescueParty(false); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false)); SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(true)); assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false); // Restore the property value initialized in SetUp() CrashRecoveryProperties.enableRescueParty(true); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true)); SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false)); } Loading Loading @@ -753,4 +759,138 @@ public class RescuePartyTest { RescuePartyObserver.getInstance(mMockContext).execute(new VersionedPackage( packageName, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, mitigationCount); } // Mock CrashRecoveryProperties as they cannot be accessed due to SEPolicy restrictions private void mockCrashRecoveryProperties(PackageWatchdog watchdog) { // mock properties in RescueParty try { doAnswer((Answer<Boolean>) invocationOnMock -> { String storedValue = mCrashRecoveryPropertiesMap .getOrDefault("crashrecovery.attempting_factory_reset", "false"); return Boolean.parseBoolean(storedValue); }).when(() -> RescueParty.isFactoryResetPropertySet()); doAnswer((Answer<Void>) invocationOnMock -> { boolean value = invocationOnMock.getArgument(0); mCrashRecoveryPropertiesMap.put("crashrecovery.attempting_factory_reset", Boolean.toString(value)); return null; }).when(() -> RescueParty.setFactoryResetProperty(anyBoolean())); doAnswer((Answer<Boolean>) invocationOnMock -> { String storedValue = mCrashRecoveryPropertiesMap .getOrDefault("crashrecovery.attempting_reboot", "false"); return Boolean.parseBoolean(storedValue); }).when(() -> RescueParty.isRebootPropertySet()); doAnswer((Answer<Void>) invocationOnMock -> { boolean value = invocationOnMock.getArgument(0); setCrashRecoveryPropAttemptingReboot(value); return null; }).when(() -> RescueParty.setRebootProperty(anyBoolean())); doAnswer((Answer<Long>) invocationOnMock -> { String storedValue = mCrashRecoveryPropertiesMap .getOrDefault("persist.crashrecovery.last_factory_reset", "0"); return Long.parseLong(storedValue); }).when(() -> RescueParty.getLastFactoryResetTimeMs()); doAnswer((Answer<Void>) invocationOnMock -> { long value = invocationOnMock.getArgument(0); setCrashRecoveryPropLastFactoryReset(value); return null; }).when(() -> RescueParty.setLastFactoryResetTimeMs(anyLong())); doAnswer((Answer<Integer>) invocationOnMock -> { String storedValue = mCrashRecoveryPropertiesMap .getOrDefault("crashrecovery.max_rescue_level_attempted", "0"); return Integer.parseInt(storedValue); }).when(() -> RescueParty.getMaxRescueLevelAttempted()); doAnswer((Answer<Void>) invocationOnMock -> { int value = invocationOnMock.getArgument(0); mCrashRecoveryPropertiesMap.put("crashrecovery.max_rescue_level_attempted", Integer.toString(value)); return null; }).when(() -> RescueParty.setMaxRescueLevelAttempted(anyInt())); } catch (Exception e) { // tests will fail, just printing the error System.out.println("Error while mocking crashrecovery properties " + e.getMessage()); } // mock properties in BootThreshold try { 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 -> { String storedValue = mCrashRecoveryPropertiesMap .getOrDefault("crashrecovery.rescue_boot_count", "0"); return Integer.parseInt(storedValue); }).when(mSpyBootThreshold).getCount(); doAnswer((Answer<Void>) invocationOnMock -> { int count = invocationOnMock.getArgument(0); setCrashRecoveryPropRescueBootCount(count); return null; }).when(mSpyBootThreshold).setCount(anyInt()); doAnswer((Answer<Integer>) invocationOnMock -> { String storedValue = mCrashRecoveryPropertiesMap .getOrDefault("crashrecovery.boot_mitigation_count", "0"); return Integer.parseInt(storedValue); }).when(mSpyBootThreshold).getMitigationCount(); doAnswer((Answer<Void>) invocationOnMock -> { int count = invocationOnMock.getArgument(0); mCrashRecoveryPropertiesMap.put("crashrecovery.boot_mitigation_count", Integer.toString(count)); return null; }).when(mSpyBootThreshold).setMitigationCount(anyInt()); doAnswer((Answer<Long>) invocationOnMock -> { String storedValue = mCrashRecoveryPropertiesMap .getOrDefault("crashrecovery.rescue_boot_start", "0"); return Long.parseLong(storedValue); }).when(mSpyBootThreshold).getStart(); doAnswer((Answer<Void>) invocationOnMock -> { long count = invocationOnMock.getArgument(0); mCrashRecoveryPropertiesMap.put("crashrecovery.rescue_boot_start", Long.toString(count)); return null; }).when(mSpyBootThreshold).setStart(anyLong()); doAnswer((Answer<Long>) invocationOnMock -> { String storedValue = mCrashRecoveryPropertiesMap .getOrDefault("crashrecovery.boot_mitigation_start", "0"); return Long.parseLong(storedValue); }).when(mSpyBootThreshold).getMitigationStart(); doAnswer((Answer<Void>) invocationOnMock -> { long count = invocationOnMock.getArgument(0); mCrashRecoveryPropertiesMap.put("crashrecovery.boot_mitigation_start", Long.toString(count)); return null; }).when(mSpyBootThreshold).setMitigationStart(anyLong()); Field mBootThresholdField = watchdog.getClass().getDeclaredField("mBootThreshold"); mBootThresholdField.setAccessible(true); mBootThresholdField.set(watchdog, mSpyBootThreshold); } catch (Exception e) { // tests will fail, just printing the error System.out.println("Error while spying BootThreshold " + e.getMessage()); } } private void setCrashRecoveryPropRescueBootCount(int count) { mCrashRecoveryPropertiesMap.put("crashrecovery.rescue_boot_count", Integer.toString(count)); } private void setCrashRecoveryPropAttemptingReboot(boolean value) { mCrashRecoveryPropertiesMap.put("crashrecovery.attempting_reboot", Boolean.toString(value)); } private void setCrashRecoveryPropLastFactoryReset(long value) { mCrashRecoveryPropertiesMap.put("persist.crashrecovery.last_factory_reset", Long.toString(value)); } } Loading
packages/CrashRecovery/services/java/com/android/server/RescueParty.java +32 −7 Original line number Diff line number Diff line Loading @@ -74,6 +74,8 @@ import java.util.concurrent.TimeUnit; * @hide */ public class RescueParty { @VisibleForTesting static final String PROP_ENABLE_RESCUE = "persist.sys.enable_rescue"; @VisibleForTesting static final int LEVEL_NONE = 0; @VisibleForTesting Loading Loading @@ -123,7 +125,7 @@ public class RescueParty { private static boolean isDisabled() { // Check if we're explicitly enabled for testing if (CrashRecoveryProperties.enableRescueParty().orElse(false)) { if (SystemProperties.getBoolean(PROP_ENABLE_RESCUE, false)) { return false; } Loading Loading @@ -176,6 +178,29 @@ public class RescueParty { return CrashRecoveryProperties.attemptingReboot().orElse(false); } protected static long getLastFactoryResetTimeMs() { return CrashRecoveryProperties.lastFactoryResetTimeMs().orElse(0L); } protected static int getMaxRescueLevelAttempted() { return CrashRecoveryProperties.maxRescueLevelAttempted().orElse(LEVEL_NONE); } protected static void setFactoryResetProperty(boolean value) { CrashRecoveryProperties.attemptingFactoryReset(value); } protected static void setRebootProperty(boolean value) { CrashRecoveryProperties.attemptingReboot(value); } protected static void setLastFactoryResetTimeMs(long value) { CrashRecoveryProperties.lastFactoryResetTimeMs(value); } protected static void setMaxRescueLevelAttempted(int level) { CrashRecoveryProperties.maxRescueLevelAttempted(level); } /** * Called when {@code SettingsProvider} has been published, which is a good * opportunity to reset any settings depending on our rescue level. Loading Loading @@ -432,7 +457,7 @@ public class RescueParty { case LEVEL_WARM_REBOOT: // Request the reboot from a separate thread to avoid deadlock on PackageWatchdog // when device shutting down. CrashRecoveryProperties.attemptingReboot(true); setRebootProperty(true); runnable = () -> { try { PowerManager pm = context.getSystemService(PowerManager.class); Loading @@ -454,9 +479,9 @@ public class RescueParty { if (isRebootPropertySet()) { break; } CrashRecoveryProperties.attemptingFactoryReset(true); setFactoryResetProperty(true); long now = System.currentTimeMillis(); CrashRecoveryProperties.lastFactoryResetTimeMs(now); setLastFactoryResetTimeMs(now); runnable = new Runnable() { @Override public void run() { Loading Loading @@ -515,10 +540,10 @@ public class RescueParty { private static void resetAllSettingsIfNecessary(Context context, int mode, int level) throws Exception { // No need to reset Settings again if they are already reset in the current level once. if (CrashRecoveryProperties.maxRescueLevelAttempted().orElse(LEVEL_NONE) >= level) { if (getMaxRescueLevelAttempted() >= level) { return; } CrashRecoveryProperties.maxRescueLevelAttempted(level); setMaxRescueLevelAttempted(level); // Try our best to reset all settings possible, and once finished // rethrow any exception that we encountered Exception res = null; Loading Loading @@ -733,7 +758,7 @@ public class RescueParty { * Will return {@code false} if a factory reset was already offered recently. */ private boolean shouldThrottleReboot() { Long lastResetTime = CrashRecoveryProperties.lastFactoryResetTimeMs().orElse(0L); Long lastResetTime = getLastFactoryResetTimeMs(); long now = System.currentTimeMillis(); long throttleDurationMin = SystemProperties.getLong(PROP_THROTTLE_DURATION_MIN_FLAG, DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN); Loading
services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java +159 −19 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import android.content.ContentResolver; Loading @@ -45,7 +46,6 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.Settings; import android.sysprop.CrashRecoveryProperties; import android.util.ArraySet; import com.android.dx.mockito.inline.extended.ExtendedMockito; Loading @@ -64,6 +64,7 @@ import org.mockito.MockitoSession; import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.lang.reflect.Field; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; Loading Loading @@ -101,6 +102,7 @@ public class RescuePartyTest { private MockitoSession mSession; private HashMap<String, String> mSystemSettingsMap; private HashMap<String, String> mCrashRecoveryPropertiesMap; //Records the namespaces wiped by setProperties(). private HashSet<String> mNamespacesWiped; Loading @@ -113,6 +115,9 @@ public class RescuePartyTest { @Mock(answer = Answers.RETURNS_DEEP_STUBS) private PackageManager mPackageManager; // Mock only sysprop apis private PackageWatchdog.BootThreshold mSpyBootThreshold; @Captor private ArgumentCaptor<DeviceConfig.MonitorCallback> mMonitorCallbackCaptor; @Captor Loading Loading @@ -208,11 +213,12 @@ public class RescuePartyTest { // Mock PackageWatchdog doAnswer((Answer<PackageWatchdog>) invocationOnMock -> mMockPackageWatchdog) .when(() -> PackageWatchdog.getInstance(mMockContext)); mockCrashRecoveryProperties(mMockPackageWatchdog); doReturn(CURRENT_NETWORK_TIME_MILLIS).when(() -> RescueParty.getElapsedRealtime()); CrashRecoveryProperties.rescueBootCount(0); CrashRecoveryProperties.enableRescueParty(true); setCrashRecoveryPropRescueBootCount(0); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true)); SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false)); } Loading Loading @@ -255,7 +261,7 @@ public class RescuePartyTest { noteBoot(4); assertTrue(RescueParty.isRebootPropertySet()); CrashRecoveryProperties.attemptingReboot(false); setCrashRecoveryPropAttemptingReboot(false); noteBoot(5); assertTrue(RescueParty.isFactoryResetPropertySet()); } Loading @@ -280,7 +286,7 @@ public class RescuePartyTest { noteAppCrash(4, true); assertTrue(RescueParty.isRebootPropertySet()); CrashRecoveryProperties.attemptingReboot(false); setCrashRecoveryPropAttemptingReboot(false); noteAppCrash(5, true); assertTrue(RescueParty.isFactoryResetPropertySet()); } Loading Loading @@ -438,7 +444,7 @@ public class RescuePartyTest { noteBoot(i + 1); } assertFalse(RescueParty.isFactoryResetPropertySet()); CrashRecoveryProperties.attemptingReboot(false); setCrashRecoveryPropAttemptingReboot(false); noteBoot(LEVEL_FACTORY_RESET + 1); assertTrue(RescueParty.isAttemptingFactoryReset()); assertTrue(RescueParty.isFactoryResetPropertySet()); Loading @@ -456,7 +462,7 @@ public class RescuePartyTest { noteBoot(mitigationCount++); assertFalse(RescueParty.isFactoryResetPropertySet()); noteBoot(mitigationCount++); CrashRecoveryProperties.attemptingReboot(false); setCrashRecoveryPropAttemptingReboot(false); noteBoot(mitigationCount + 1); assertTrue(RescueParty.isAttemptingFactoryReset()); assertTrue(RescueParty.isFactoryResetPropertySet()); Loading @@ -464,10 +470,10 @@ public class RescuePartyTest { @Test public void testThrottlingOnBootFailures() { CrashRecoveryProperties.attemptingReboot(false); setCrashRecoveryPropAttemptingReboot(false); long now = System.currentTimeMillis(); long beforeTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN - 1); CrashRecoveryProperties.lastFactoryResetTimeMs(beforeTimeout); setCrashRecoveryPropLastFactoryReset(beforeTimeout); for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) { noteBoot(i); } Loading @@ -476,10 +482,10 @@ public class RescuePartyTest { @Test public void testThrottlingOnAppCrash() { CrashRecoveryProperties.attemptingReboot(false); setCrashRecoveryPropAttemptingReboot(false); long now = System.currentTimeMillis(); long beforeTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN - 1); CrashRecoveryProperties.lastFactoryResetTimeMs(beforeTimeout); setCrashRecoveryPropLastFactoryReset(beforeTimeout); for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) { noteAppCrash(i + 1, true); } Loading @@ -488,10 +494,10 @@ public class RescuePartyTest { @Test public void testNotThrottlingAfterTimeoutOnBootFailures() { CrashRecoveryProperties.attemptingReboot(false); setCrashRecoveryPropAttemptingReboot(false); long now = System.currentTimeMillis(); long afterTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN + 1); CrashRecoveryProperties.lastFactoryResetTimeMs(afterTimeout); setCrashRecoveryPropLastFactoryReset(afterTimeout); for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) { noteBoot(i); } Loading @@ -499,10 +505,10 @@ public class RescuePartyTest { } @Test public void testNotThrottlingAfterTimeoutOnAppCrash() { CrashRecoveryProperties.attemptingReboot(false); setCrashRecoveryPropAttemptingReboot(false); long now = System.currentTimeMillis(); long afterTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN + 1); CrashRecoveryProperties.lastFactoryResetTimeMs(afterTimeout); setCrashRecoveryPropLastFactoryReset(afterTimeout); for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) { noteAppCrash(i + 1, true); } Loading @@ -525,26 +531,26 @@ public class RescuePartyTest { @Test public void testExplicitlyEnablingAndDisablingRescue() { CrashRecoveryProperties.enableRescueParty(false); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false)); SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true)); assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false); CrashRecoveryProperties.enableRescueParty(true); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true)); assertTrue(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1)); } @Test public void testDisablingRescueByDeviceConfigFlag() { CrashRecoveryProperties.enableRescueParty(false); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false)); SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(true)); assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false); // Restore the property value initialized in SetUp() CrashRecoveryProperties.enableRescueParty(true); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true)); SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false)); } Loading Loading @@ -753,4 +759,138 @@ public class RescuePartyTest { RescuePartyObserver.getInstance(mMockContext).execute(new VersionedPackage( packageName, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, mitigationCount); } // Mock CrashRecoveryProperties as they cannot be accessed due to SEPolicy restrictions private void mockCrashRecoveryProperties(PackageWatchdog watchdog) { // mock properties in RescueParty try { doAnswer((Answer<Boolean>) invocationOnMock -> { String storedValue = mCrashRecoveryPropertiesMap .getOrDefault("crashrecovery.attempting_factory_reset", "false"); return Boolean.parseBoolean(storedValue); }).when(() -> RescueParty.isFactoryResetPropertySet()); doAnswer((Answer<Void>) invocationOnMock -> { boolean value = invocationOnMock.getArgument(0); mCrashRecoveryPropertiesMap.put("crashrecovery.attempting_factory_reset", Boolean.toString(value)); return null; }).when(() -> RescueParty.setFactoryResetProperty(anyBoolean())); doAnswer((Answer<Boolean>) invocationOnMock -> { String storedValue = mCrashRecoveryPropertiesMap .getOrDefault("crashrecovery.attempting_reboot", "false"); return Boolean.parseBoolean(storedValue); }).when(() -> RescueParty.isRebootPropertySet()); doAnswer((Answer<Void>) invocationOnMock -> { boolean value = invocationOnMock.getArgument(0); setCrashRecoveryPropAttemptingReboot(value); return null; }).when(() -> RescueParty.setRebootProperty(anyBoolean())); doAnswer((Answer<Long>) invocationOnMock -> { String storedValue = mCrashRecoveryPropertiesMap .getOrDefault("persist.crashrecovery.last_factory_reset", "0"); return Long.parseLong(storedValue); }).when(() -> RescueParty.getLastFactoryResetTimeMs()); doAnswer((Answer<Void>) invocationOnMock -> { long value = invocationOnMock.getArgument(0); setCrashRecoveryPropLastFactoryReset(value); return null; }).when(() -> RescueParty.setLastFactoryResetTimeMs(anyLong())); doAnswer((Answer<Integer>) invocationOnMock -> { String storedValue = mCrashRecoveryPropertiesMap .getOrDefault("crashrecovery.max_rescue_level_attempted", "0"); return Integer.parseInt(storedValue); }).when(() -> RescueParty.getMaxRescueLevelAttempted()); doAnswer((Answer<Void>) invocationOnMock -> { int value = invocationOnMock.getArgument(0); mCrashRecoveryPropertiesMap.put("crashrecovery.max_rescue_level_attempted", Integer.toString(value)); return null; }).when(() -> RescueParty.setMaxRescueLevelAttempted(anyInt())); } catch (Exception e) { // tests will fail, just printing the error System.out.println("Error while mocking crashrecovery properties " + e.getMessage()); } // mock properties in BootThreshold try { 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 -> { String storedValue = mCrashRecoveryPropertiesMap .getOrDefault("crashrecovery.rescue_boot_count", "0"); return Integer.parseInt(storedValue); }).when(mSpyBootThreshold).getCount(); doAnswer((Answer<Void>) invocationOnMock -> { int count = invocationOnMock.getArgument(0); setCrashRecoveryPropRescueBootCount(count); return null; }).when(mSpyBootThreshold).setCount(anyInt()); doAnswer((Answer<Integer>) invocationOnMock -> { String storedValue = mCrashRecoveryPropertiesMap .getOrDefault("crashrecovery.boot_mitigation_count", "0"); return Integer.parseInt(storedValue); }).when(mSpyBootThreshold).getMitigationCount(); doAnswer((Answer<Void>) invocationOnMock -> { int count = invocationOnMock.getArgument(0); mCrashRecoveryPropertiesMap.put("crashrecovery.boot_mitigation_count", Integer.toString(count)); return null; }).when(mSpyBootThreshold).setMitigationCount(anyInt()); doAnswer((Answer<Long>) invocationOnMock -> { String storedValue = mCrashRecoveryPropertiesMap .getOrDefault("crashrecovery.rescue_boot_start", "0"); return Long.parseLong(storedValue); }).when(mSpyBootThreshold).getStart(); doAnswer((Answer<Void>) invocationOnMock -> { long count = invocationOnMock.getArgument(0); mCrashRecoveryPropertiesMap.put("crashrecovery.rescue_boot_start", Long.toString(count)); return null; }).when(mSpyBootThreshold).setStart(anyLong()); doAnswer((Answer<Long>) invocationOnMock -> { String storedValue = mCrashRecoveryPropertiesMap .getOrDefault("crashrecovery.boot_mitigation_start", "0"); return Long.parseLong(storedValue); }).when(mSpyBootThreshold).getMitigationStart(); doAnswer((Answer<Void>) invocationOnMock -> { long count = invocationOnMock.getArgument(0); mCrashRecoveryPropertiesMap.put("crashrecovery.boot_mitigation_start", Long.toString(count)); return null; }).when(mSpyBootThreshold).setMitigationStart(anyLong()); Field mBootThresholdField = watchdog.getClass().getDeclaredField("mBootThreshold"); mBootThresholdField.setAccessible(true); mBootThresholdField.set(watchdog, mSpyBootThreshold); } catch (Exception e) { // tests will fail, just printing the error System.out.println("Error while spying BootThreshold " + e.getMessage()); } } private void setCrashRecoveryPropRescueBootCount(int count) { mCrashRecoveryPropertiesMap.put("crashrecovery.rescue_boot_count", Integer.toString(count)); } private void setCrashRecoveryPropAttemptingReboot(boolean value) { mCrashRecoveryPropertiesMap.put("crashrecovery.attempting_reboot", Boolean.toString(value)); } private void setCrashRecoveryPropLastFactoryReset(long value) { mCrashRecoveryPropertiesMap.put("persist.crashrecovery.last_factory_reset", Long.toString(value)); } }