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

Commit 38b109a4 authored by Alexei Nicoara's avatar Alexei Nicoara Committed by Android (Google) Code Review
Browse files

Merge "Adding reboot throttling" into udc-dev

parents 2faeca44 cde1c58d
Loading
Loading
Loading
Loading
+30 −12
Original line number Diff line number Diff line
@@ -79,6 +79,7 @@ public class RescueParty {
    static final String PROP_ATTEMPTING_FACTORY_RESET = "sys.attempting_factory_reset";
    static final String PROP_ATTEMPTING_REBOOT = "sys.attempting_reboot";
    static final String PROP_MAX_RESCUE_LEVEL_ATTEMPTED = "sys.max_rescue_level_attempted";
    static final String PROP_LAST_FACTORY_RESET_TIME_MS = "persist.sys.last_factory_reset";
    @VisibleForTesting
    static final int LEVEL_NONE = 0;
    @VisibleForTesting
@@ -105,10 +106,11 @@ public class RescueParty {
    @VisibleForTesting
    static final String NAMESPACE_TO_PACKAGE_MAPPING_FLAG =
            "namespace_to_package_mapping";
    @VisibleForTesting
    static final long FACTORY_RESET_THROTTLE_DURATION_MS = TimeUnit.MINUTES.toMillis(10);

    private static final String NAME = "rescue-party-observer";


    private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
    private static final String PROP_VIRTUAL_DEVICE = "ro.hardware.virtual_device";
    private static final String PROP_DEVICE_CONFIG_DISABLE_FLAG =
@@ -327,8 +329,8 @@ public class RescueParty {
        }
    }

    private static int getMaxRescueLevel(boolean mayPerformFactoryReset) {
        if (!mayPerformFactoryReset
    private static int getMaxRescueLevel(boolean mayPerformReboot) {
        if (!mayPerformReboot
                || SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)) {
            return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS;
        }
@@ -339,11 +341,11 @@ public class RescueParty {
     * Get the rescue level to perform if this is the n-th attempt at mitigating failure.
     *
     * @param mitigationCount: the mitigation attempt number (1 = first attempt etc.)
     * @param mayPerformFactoryReset: whether or not a factory reset may be performed for the given
     *                              failure.
     * @param mayPerformReboot: whether or not a reboot and factory reset may be performed
     *                          for the given failure.
     * @return the rescue level for the n-th mitigation attempt.
     */
    private static int getRescueLevel(int mitigationCount, boolean mayPerformFactoryReset) {
    private static int getRescueLevel(int mitigationCount, boolean mayPerformReboot) {
        if (mitigationCount == 1) {
            return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS;
        } else if (mitigationCount == 2) {
@@ -351,9 +353,9 @@ public class RescueParty {
        } else if (mitigationCount == 3) {
            return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS;
        } else if (mitigationCount == 4) {
            return Math.min(getMaxRescueLevel(mayPerformFactoryReset), LEVEL_WARM_REBOOT);
            return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_WARM_REBOOT);
        } else if (mitigationCount >= 5) {
            return Math.min(getMaxRescueLevel(mayPerformFactoryReset), LEVEL_FACTORY_RESET);
            return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_FACTORY_RESET);
        } else {
            Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount);
            return LEVEL_NONE;
@@ -450,6 +452,8 @@ public class RescueParty {
                    break;
                }
                SystemProperties.set(PROP_ATTEMPTING_FACTORY_RESET, "true");
                long now = System.currentTimeMillis();
                SystemProperties.set(PROP_LAST_FACTORY_RESET_TIME_MS, Long.toString(now));
                runnable = new Runnable() {
                    @Override
                    public void run() {
@@ -627,7 +631,7 @@ public class RescueParty {
            if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
                    || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) {
                return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount,
                        mayPerformFactoryReset(failedPackage)));
                        mayPerformReboot(failedPackage)));
            } else {
                return PackageHealthObserverImpact.USER_IMPACT_NONE;
            }
@@ -642,7 +646,7 @@ public class RescueParty {
            if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
                    || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
                final int level = getRescueLevel(mitigationCount,
                        mayPerformFactoryReset(failedPackage));
                        mayPerformReboot(failedPackage));
                executeRescueLevel(mContext,
                        failedPackage == null ? null : failedPackage.getPackageName(), level);
                return true;
@@ -683,8 +687,9 @@ public class RescueParty {
            if (isDisabled()) {
                return false;
            }
            boolean mayPerformReboot = !shouldThrottleReboot();
            executeRescueLevel(mContext, /*failedPackage=*/ null,
                    getRescueLevel(mitigationCount, true));
                    getRescueLevel(mitigationCount, mayPerformReboot));
            return true;
        }

@@ -698,14 +703,27 @@ public class RescueParty {
         * prompting a factory reset is an acceptable mitigation strategy for the package's
         * failure, {@code false} otherwise.
         */
        private boolean mayPerformFactoryReset(@Nullable VersionedPackage failingPackage) {
        private boolean mayPerformReboot(@Nullable VersionedPackage failingPackage) {
            if (failingPackage == null) {
                return false;
            }
            if (shouldThrottleReboot())  {
                return false;
            }

            return isPersistentSystemApp(failingPackage.getPackageName());
        }

        /**
         * Returns {@code true} if Rescue Party is allowed to attempt a reboot or factory reset.
         * Will return {@code false} if a factory reset was already offered recently.
         */
        private boolean shouldThrottleReboot() {
            Long lastResetTime = SystemProperties.getLong(PROP_LAST_FACTORY_RESET_TIME_MS, 0);
            long now = System.currentTimeMillis();
            return now < lastResetTime + FACTORY_RESET_THROTTLE_DURATION_MS;
        }

        private boolean isPersistentSystemApp(@NonNull String packageName) {
            PackageManager pm = mContext.getPackageManager();
            try {
+51 −0
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

/**
 * Test RescueParty.
@@ -94,6 +95,9 @@ public class RescuePartyTest {
            "persist.device_config.configuration.disable_rescue_party";
    private static final String PROP_DISABLE_FACTORY_RESET_FLAG =
            "persist.device_config.configuration.disable_rescue_party_factory_reset";
    private static final String PROP_LAST_FACTORY_RESET_TIME_MS = "persist.sys.last_factory_reset";

    private static final int THROTTLING_DURATION_MIN = 10;

    private MockitoSession mSession;
    private HashMap<String, String> mSystemSettingsMap;
@@ -458,6 +462,53 @@ public class RescuePartyTest {
        assertTrue(RescueParty.isFactoryResetPropertySet());
    }

    @Test
    public void testThrottlingOnBootFailures() {
        SystemProperties.set(RescueParty.PROP_ATTEMPTING_REBOOT, Boolean.toString(false));
        long now = System.currentTimeMillis();
        long beforeTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN - 1);
        SystemProperties.set(PROP_LAST_FACTORY_RESET_TIME_MS, Long.toString(beforeTimeout));
        for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) {
            noteBoot(i);
        }
        assertFalse(RescueParty.isAttemptingFactoryReset());
    }

    @Test
    public void testThrottlingOnAppCrash() {
        SystemProperties.set(RescueParty.PROP_ATTEMPTING_REBOOT, Boolean.toString(false));
        long now = System.currentTimeMillis();
        long beforeTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN - 1);
        SystemProperties.set(PROP_LAST_FACTORY_RESET_TIME_MS, Long.toString(beforeTimeout));
        for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) {
            noteAppCrash(i + 1, true);
        }
        assertFalse(RescueParty.isAttemptingFactoryReset());
    }

    @Test
    public void testNotThrottlingAfterTimeoutOnBootFailures() {
        SystemProperties.set(RescueParty.PROP_ATTEMPTING_REBOOT, Boolean.toString(false));
        long now = System.currentTimeMillis();
        long afterTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN + 1);
        SystemProperties.set(PROP_LAST_FACTORY_RESET_TIME_MS, Long.toString(afterTimeout));
        for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) {
            noteBoot(i);
        }
        assertTrue(RescueParty.isAttemptingFactoryReset());
    }
    @Test
    public void testNotThrottlingAfterTimeoutOnAppCrash() {
        SystemProperties.set(RescueParty.PROP_ATTEMPTING_REBOOT, Boolean.toString(false));
        long now = System.currentTimeMillis();
        long afterTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN + 1);
        SystemProperties.set(PROP_LAST_FACTORY_RESET_TIME_MS, Long.toString(afterTimeout));
        for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) {
            noteAppCrash(i + 1, true);
        }
        assertTrue(RescueParty.isAttemptingFactoryReset());
    }

    @Test
    public void testNativeRescuePartyResets() {
        doReturn(true).when(() -> SettingsToPropertiesMapper.isNativeFlagsResetPerformed());