Skip to content
Snippets Groups Projects
Commit 90bf3a9e authored by Harshit Mahajan's avatar Harshit Mahajan Committed by Gerrit Code Review
Browse files

Merge "[RescuePartyTest] Mocking CrashRecoveryProperties" into main

parents ec3d9ab9 2b32d1d5
No related branches found
No related tags found
No related merge requests found
......@@ -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
......@@ -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;
}
......@@ -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.
......@@ -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);
......@@ -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() {
......@@ -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;
......@@ -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);
......
......@@ -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;
......@@ -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;
......@@ -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;
......@@ -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;
......@@ -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
......@@ -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));
}
......@@ -255,7 +261,7 @@ public class RescuePartyTest {
noteBoot(4);
assertTrue(RescueParty.isRebootPropertySet());
CrashRecoveryProperties.attemptingReboot(false);
setCrashRecoveryPropAttemptingReboot(false);
noteBoot(5);
assertTrue(RescueParty.isFactoryResetPropertySet());
}
......@@ -280,7 +286,7 @@ public class RescuePartyTest {
noteAppCrash(4, true);
assertTrue(RescueParty.isRebootPropertySet());
CrashRecoveryProperties.attemptingReboot(false);
setCrashRecoveryPropAttemptingReboot(false);
noteAppCrash(5, true);
assertTrue(RescueParty.isFactoryResetPropertySet());
}
......@@ -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());
......@@ -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());
......@@ -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);
}
......@@ -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);
}
......@@ -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);
}
......@@ -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);
}
......@@ -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));
}
......@@ -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));
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment