Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2b32d1d5 authored by Harshit Mahajan's avatar Harshit Mahajan
Browse files

[RescuePartyTest] Mocking CrashRecoveryProperties

This test needs to be updated to mock CrashRecovery properties.
PROP_ENABLE_RESCUE is only set in test, so using SystemProperty instead.

Bug: b/323272250
Test: atest RescuePartyTest

Change-Id: Ie614d93ad96e62ab42070fbadd18300a03f4744f
parent bcf722f0
Loading
Loading
Loading
Loading
+32 −7
Original line number Diff line number Diff line
@@ -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);
+159 −19
Original line number Diff line number Diff line
@@ -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));
    }
}