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

Commit 335397f1 authored by Hongyi Zhang's avatar Hongyi Zhang Committed by Android (Google) Code Review
Browse files

Merge "Constrain Non-DeviceConfig settings reset times & unify DeviceConfig reset mode"

parents 05fca817 c8f1e2b3
Loading
Loading
Loading
Loading
+66 −26
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@ public class RescueParty {
    @VisibleForTesting
    static final String PROP_RESCUE_LEVEL = "sys.rescue_level";
    static final String PROP_ATTEMPTING_FACTORY_RESET = "sys.attempting_factory_reset";
    static final String PROP_MAX_RESCUE_LEVEL_ATTEMPTED = "sys.max_rescue_level_attempted";
    @VisibleForTesting
    static final int LEVEL_NONE = 0;
    @VisibleForTesting
@@ -94,6 +95,8 @@ public class RescueParty {
    static final String TAG = "RescueParty";
    @VisibleForTesting
    static final long DEFAULT_OBSERVING_DURATION_MS = TimeUnit.DAYS.toMillis(2);
    @VisibleForTesting
    static final int DEVICE_CONFIG_RESET_MODE = Settings.RESET_MODE_TRUSTED_DEFAULTS;

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

@@ -225,7 +228,7 @@ public class RescueParty {
                if (NAMESPACE_CONFIGURATION.equals(resetNativeCategories[i])) {
                    continue;
                }
                DeviceConfig.resetToDefaults(Settings.RESET_MODE_TRUSTED_DEFAULTS,
                DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE,
                        resetNativeCategories[i]);
            }
        }
@@ -300,15 +303,48 @@ public class RescueParty {
    private static void executeRescueLevelInternal(Context context, int level, @Nullable
            String failedPackage) throws Exception {
        FrameworkStatsLog.write(FrameworkStatsLog.RESCUE_PARTY_RESET_REPORTED, level);
        // Try our best to reset all settings possible, and once finished
        // rethrow any exception that we encountered
        Exception res = null;
        switch (level) {
            case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
                resetAllSettings(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS, failedPackage);
                try {
                    resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS,
                            level);
                } catch (Exception e) {
                    res = e;
                }
                try {
                    resetDeviceConfig(context, /*isScoped=*/true, failedPackage);
                } catch (Exception e) {
                    res = e;
                }
                break;
            case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
                resetAllSettings(context, Settings.RESET_MODE_UNTRUSTED_CHANGES, failedPackage);
                try {
                    resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_CHANGES,
                            level);
                } catch (Exception e) {
                    res = e;
                }
                try {
                    resetDeviceConfig(context, /*isScoped=*/true, failedPackage);
                } catch (Exception e) {
                    res = e;
                }
                break;
            case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
                resetAllSettings(context, Settings.RESET_MODE_TRUSTED_DEFAULTS, failedPackage);
                try {
                    resetAllSettingsIfNecessary(context, Settings.RESET_MODE_TRUSTED_DEFAULTS,
                            level);
                } catch (Exception e) {
                    res = e;
                }
                try {
                    resetDeviceConfig(context, /*isScoped=*/false, failedPackage);
                } catch (Exception e) {
                    res = e;
                }
                break;
            case LEVEL_FACTORY_RESET:
                // Request the reboot from a separate thread to avoid deadlock on PackageWatchdog
@@ -328,6 +364,10 @@ public class RescueParty {
                thread.start();
                break;
        }

        if (res != null) {
            throw res;
        }
    }

    private static void logRescueException(int level, Throwable t) {
@@ -350,17 +390,17 @@ public class RescueParty {
        }
    }

    private static void resetAllSettings(Context context, int mode, @Nullable String failedPackage)
            throws Exception {
    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 (SystemProperties.getInt(PROP_MAX_RESCUE_LEVEL_ATTEMPTED, LEVEL_NONE) >= level) {
            return;
        }
        SystemProperties.set(PROP_MAX_RESCUE_LEVEL_ATTEMPTED, Integer.toString(level));
        // Try our best to reset all settings possible, and once finished
        // rethrow any exception that we encountered
        Exception res = null;
        final ContentResolver resolver = context.getContentResolver();
        try {
            resetDeviceConfig(context, mode, failedPackage);
        } catch (Exception e) {
            res = new RuntimeException("Failed to reset config settings", e);
        }
        try {
            Settings.Global.resetToDefaultsAsUser(resolver, null, mode, UserHandle.USER_SYSTEM);
        } catch (Exception e) {
@@ -378,16 +418,21 @@ public class RescueParty {
        }
    }

    private static void resetDeviceConfig(Context context, int resetMode,
            @Nullable String failedPackage) {
        if (!shouldPerformScopedResets(resetMode) || failedPackage == null) {
            resetAllAffectedNamespaces(context, resetMode);
    private static void resetDeviceConfig(Context context, boolean isScoped,
            @Nullable String failedPackage) throws Exception {
        final ContentResolver resolver = context.getContentResolver();
        try {
            if (!isScoped || failedPackage == null) {
                resetAllAffectedNamespaces(context);
            } else {
            performScopedReset(context, resetMode, failedPackage);
                performScopedReset(context, failedPackage);
            }
        } catch (Exception e) {
            throw new RuntimeException("Failed to reset config settings", e);
        }
    }

    private static void resetAllAffectedNamespaces(Context context, int resetMode) {
    private static void resetAllAffectedNamespaces(Context context) {
        RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
        Set<String> allAffectedNamespaces = rescuePartyObserver.getAllAffectedNamespaceSet();

@@ -401,16 +446,11 @@ public class RescueParty {
            if (NAMESPACE_CONFIGURATION.equals(namespace)) {
                continue;
            }
            DeviceConfig.resetToDefaults(resetMode, namespace);
            DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE, namespace);
        }
    }

    private static boolean shouldPerformScopedResets(int resetMode) {
        return resetMode <= Settings.RESET_MODE_UNTRUSTED_CHANGES;
    }

    private static void performScopedReset(Context context, int resetMode,
            @NonNull String failedPackage) {
    private static void performScopedReset(Context context, @NonNull String failedPackage) {
        RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
        Set<String> affectedNamespaces = rescuePartyObserver.getAffectedNamespaceSet(
                failedPackage);
@@ -428,7 +468,7 @@ public class RescueParty {
                if (NAMESPACE_CONFIGURATION.equals(namespace)) {
                    continue;
                }
                DeviceConfig.resetToDefaults(resetMode, namespace);
                DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE, namespace);
            }
        }
    }
+95 −12
Original line number Diff line number Diff line
@@ -191,10 +191,12 @@ public class RescuePartyTest {
        RescueParty.onSettingsProviderPublished(mMockContext);
        verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver),
                mMonitorCallbackCaptor.capture()));
        HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>();

        noteBoot();

        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null);
        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
                verifiedTimesMap);
        assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
                SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));

@@ -208,13 +210,15 @@ public class RescuePartyTest {

        noteBoot();

        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, expectedAllResetNamespaces);
        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, expectedAllResetNamespaces,
                verifiedTimesMap);
        assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES,
                SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));

        noteBoot();

        verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces);
        verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces,
                verifiedTimesMap);
        assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS,
                SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));

@@ -228,15 +232,18 @@ public class RescuePartyTest {
    public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevels() {
        notePersistentAppCrash(1);

        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null);
        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
                /*configResetVerifiedTimesMap=*/ null);

        notePersistentAppCrash(2);

        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null);
        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null,
                /*configResetVerifiedTimesMap=*/ null);

        notePersistentAppCrash(3);

        verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null);
        verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
                /*configResetVerifiedTimesMap=*/ null);

        notePersistentAppCrash(4);
        assertTrue(RescueParty.isAttemptingFactoryReset());
@@ -272,17 +279,82 @@ public class RescuePartyTest {
        final String[] expectedResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
        final String[] expectedAllResetNamespaces =
                new String[]{NAMESPACE1, NAMESPACE2, NAMESPACE3};
        HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>();
        observer.execute(new VersionedPackage(
                CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, expectedResetNamespaces);
        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, expectedResetNamespaces,
                verifiedTimesMap);

        observer.execute(new VersionedPackage(
                CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2);
        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, expectedResetNamespaces);
        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, expectedResetNamespaces,
                verifiedTimesMap);

        observer.execute(new VersionedPackage(
                CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 3);
        verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces);
        verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces,
                verifiedTimesMap);

        observer.execute(new VersionedPackage(
                CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4);
        assertTrue(RescueParty.isAttemptingFactoryReset());
    }

    @Test
    public void testNonDeviceConfigSettingsOnlyResetOncePerLevel() {
        RescueParty.onSettingsProviderPublished(mMockContext);
        verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver),
                mMonitorCallbackCaptor.capture()));

        // Record DeviceConfig accesses
        RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
        RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue();
        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1));
        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE2));
        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE2));
        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE3));
        // Fake DeviceConfig value changes
        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE1));
        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE2));
        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE3));
        // Perform and verify scoped resets
        final String[] expectedPackage1ResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
        final String[] expectedPackage2ResetNamespaces = new String[]{NAMESPACE2, NAMESPACE3};
        final String[] expectedAllResetNamespaces =
                new String[]{NAMESPACE1, NAMESPACE2, NAMESPACE3};
        HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>();
        observer.execute(new VersionedPackage(
                CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS,
                expectedPackage1ResetNamespaces, verifiedTimesMap);

        // Settings.Global & Settings.Secure should still remain the same execution times.
        observer.execute(new VersionedPackage(
                CALLING_PACKAGE2, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS,
                expectedPackage2ResetNamespaces, verifiedTimesMap);

        observer.execute(new VersionedPackage(
                CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2);
        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES,
                expectedPackage1ResetNamespaces, verifiedTimesMap);

        // Settings.Global & Settings.Secure should still remain the same execution times.
        observer.execute(new VersionedPackage(
                CALLING_PACKAGE2, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2);
        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES,
                expectedPackage2ResetNamespaces, verifiedTimesMap);

        observer.execute(new VersionedPackage(
                CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 3);
        verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces,
                verifiedTimesMap);

        // Settings.Global & Settings.Secure should still remain the same execution times.
        observer.execute(new VersionedPackage(
                CALLING_PACKAGE2, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 3);
        verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces,
                verifiedTimesMap);

        observer.execute(new VersionedPackage(
                CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4);
@@ -303,7 +375,8 @@ public class RescuePartyTest {

        RescueParty.onSettingsProviderPublished(mMockContext);

        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null);
        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
                /*configResetVerifiedTimesMap=*/ null);
        assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
                SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
    }
@@ -415,7 +488,8 @@ public class RescuePartyTest {
        assertEquals(observer.onBootLoop(), PackageHealthObserverImpact.USER_IMPACT_HIGH);
    }

    private void verifySettingsResets(int resetMode, String[] resetNamespaces) {
    private void verifySettingsResets(int resetMode, String[] resetNamespaces,
            HashMap<String, Integer> configResetVerifiedTimesMap) {
        verify(() -> Settings.Global.resetToDefaultsAsUser(mMockContentResolver, null,
                resetMode, UserHandle.USER_SYSTEM));
        verify(() -> Settings.Secure.resetToDefaultsAsUser(eq(mMockContentResolver), isNull(),
@@ -425,7 +499,16 @@ public class RescuePartyTest {
            verify(() -> DeviceConfig.resetToDefaults(anyInt(), anyString()), never());
        } else {
            for (String namespace : resetNamespaces) {
                verify(() -> DeviceConfig.resetToDefaults(resetMode, namespace));
                int verifiedTimes = 0;
                if (configResetVerifiedTimesMap != null
                        && configResetVerifiedTimesMap.get(namespace) != null) {
                    verifiedTimes = configResetVerifiedTimesMap.get(namespace);
                }
                verify(() -> DeviceConfig.resetToDefaults(RescueParty.DEVICE_CONFIG_RESET_MODE,
                        namespace), times(verifiedTimes + 1));
                if (configResetVerifiedTimesMap != null) {
                    configResetVerifiedTimesMap.put(namespace, verifiedTimes + 1);
                }
            }
        }
    }