Loading services/core/java/com/android/server/RescueParty.java +66 −26 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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"; Loading Loading @@ -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]); } } Loading Loading @@ -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 Loading @@ -328,6 +364,10 @@ public class RescueParty { thread.start(); break; } if (res != null) { throw res; } } private static void logRescueException(int level, Throwable t) { Loading @@ -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) { Loading @@ -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(); Loading @@ -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); Loading @@ -428,7 +468,7 @@ public class RescueParty { if (NAMESPACE_CONFIGURATION.equals(namespace)) { continue; } DeviceConfig.resetToDefaults(resetMode, namespace); DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE, namespace); } } } Loading services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java +95 −12 Original line number Diff line number Diff line Loading @@ -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)); Loading @@ -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)); Loading @@ -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()); Loading Loading @@ -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); Loading @@ -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)); } Loading Loading @@ -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(), Loading @@ -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); } } } } Loading Loading
services/core/java/com/android/server/RescueParty.java +66 −26 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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"; Loading Loading @@ -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]); } } Loading Loading @@ -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 Loading @@ -328,6 +364,10 @@ public class RescueParty { thread.start(); break; } if (res != null) { throw res; } } private static void logRescueException(int level, Throwable t) { Loading @@ -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) { Loading @@ -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(); Loading @@ -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); Loading @@ -428,7 +468,7 @@ public class RescueParty { if (NAMESPACE_CONFIGURATION.equals(namespace)) { continue; } DeviceConfig.resetToDefaults(resetMode, namespace); DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE, namespace); } } } Loading
services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java +95 −12 Original line number Diff line number Diff line Loading @@ -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)); Loading @@ -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)); Loading @@ -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()); Loading Loading @@ -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); Loading @@ -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)); } Loading Loading @@ -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(), Loading @@ -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); } } } } Loading