Loading services/core/java/com/android/server/RescueParty.java +42 −16 Original line number Diff line number Diff line Loading @@ -327,18 +327,23 @@ public class RescueParty { } } private static int getMaxRescueLevel() { return SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false) ? LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS : LEVEL_FACTORY_RESET; private static int getMaxRescueLevel(boolean mayPerformFactoryReset) { if (!mayPerformFactoryReset || SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)) { return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS; } return LEVEL_FACTORY_RESET; } /** * 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. * @return the rescue level for the n-th mitigation attempt. */ private static int getRescueLevel(int mitigationCount) { private static int getRescueLevel(int mitigationCount, boolean mayPerformFactoryReset) { if (mitigationCount == 1) { return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS; } else if (mitigationCount == 2) { Loading @@ -346,9 +351,9 @@ public class RescueParty { } else if (mitigationCount == 3) { return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS; } else if (mitigationCount == 4) { return Math.min(getMaxRescueLevel(), LEVEL_WARM_REBOOT); return Math.min(getMaxRescueLevel(mayPerformFactoryReset), LEVEL_WARM_REBOOT); } else if (mitigationCount >= 5) { return Math.min(getMaxRescueLevel(), LEVEL_FACTORY_RESET); return Math.min(getMaxRescueLevel(mayPerformFactoryReset), LEVEL_FACTORY_RESET); } else { Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount); return LEVEL_NONE; Loading Loading @@ -614,7 +619,8 @@ public class RescueParty { @FailureReasons int failureReason, int mitigationCount) { if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) { return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount)); return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, mayPerformFactoryReset(failedPackage))); } else { return PackageHealthObserverImpact.USER_IMPACT_NONE; } Loading @@ -628,7 +634,8 @@ public class RescueParty { } if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) { final int level = getRescueLevel(mitigationCount); final int level = getRescueLevel(mitigationCount, mayPerformFactoryReset(failedPackage)); executeRescueLevel(mContext, failedPackage == null ? null : failedPackage.getPackageName(), level); return true; Loading @@ -653,12 +660,7 @@ public class RescueParty { } catch (PackageManager.NameNotFoundException ignore) { } try { ApplicationInfo info = pm.getApplicationInfo(packageName, 0); return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK; } catch (PackageManager.NameNotFoundException e) { return false; } return isPersistentSystemApp(packageName); } @Override Loading @@ -666,7 +668,7 @@ public class RescueParty { if (isDisabled()) { return PackageHealthObserverImpact.USER_IMPACT_NONE; } return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount)); return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, true)); } @Override Loading @@ -674,7 +676,8 @@ public class RescueParty { if (isDisabled()) { return false; } executeRescueLevel(mContext, /*failedPackage=*/ null, getRescueLevel(mitigationCount)); executeRescueLevel(mContext, /*failedPackage=*/ null, getRescueLevel(mitigationCount, true)); return true; } Loading @@ -683,6 +686,29 @@ public class RescueParty { return NAME; } /** * Returns {@code true} if the failing package is non-null and performing a reboot or * prompting a factory reset is an acceptable mitigation strategy for the package's * failure, {@code false} otherwise. */ private boolean mayPerformFactoryReset(@Nullable VersionedPackage failingPackage) { if (failingPackage == null) { return false; } return isPersistentSystemApp(failingPackage.getPackageName()); } private boolean isPersistentSystemApp(@NonNull String packageName) { PackageManager pm = mContext.getPackageManager(); try { ApplicationInfo info = pm.getApplicationInfo(packageName, 0); return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK; } catch (PackageManager.NameNotFoundException e) { return false; } } private synchronized void recordDeviceConfigAccess(@NonNull String callingPackage, @NonNull String namespace) { // Record it in calling packages to namespace map Loading services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java +53 −11 Original line number Diff line number Diff line Loading @@ -37,6 +37,8 @@ import static org.mockito.Mockito.times; import android.content.ContentResolver; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; import android.os.Bundle; import android.os.RecoverySystem; Loading Loading @@ -83,6 +85,8 @@ public class RescuePartyTest { private static final String CALLING_PACKAGE1 = "com.package.name1"; private static final String CALLING_PACKAGE2 = "com.package.name2"; private static final String CALLING_PACKAGE3 = "com.package.name3"; private static final String PERSISTENT_PACKAGE = "com.persistent.package"; private static final String NON_PERSISTENT_PACKAGE = "com.nonpersistent.package"; private static final String NAMESPACE1 = "namespace1"; private static final String NAMESPACE2 = "namespace2"; private static final String NAMESPACE3 = "namespace3"; Loading @@ -103,6 +107,8 @@ public class RescuePartyTest { private PackageWatchdog mMockPackageWatchdog; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private ContentResolver mMockContentResolver; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private PackageManager mPackageManager; @Captor private ArgumentCaptor<RemoteCallback> mMonitorCallbackCaptor; Loading @@ -129,6 +135,17 @@ public class RescuePartyTest { mNamespacesWiped = new HashSet<>(); when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver); when(mMockContext.getPackageManager()).thenReturn(mPackageManager); ApplicationInfo persistentApplicationInfo = new ApplicationInfo(); persistentApplicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_PERSISTENT; // If the package name is PERSISTENT_PACKAGE, then set the flags to be persistent and // system. Don't set any flags otherwise. when(mPackageManager.getApplicationInfo(eq(PERSISTENT_PACKAGE), anyInt())).thenReturn(persistentApplicationInfo); when(mPackageManager.getApplicationInfo(eq(NON_PERSISTENT_PACKAGE), anyInt())).thenReturn(new ApplicationInfo()); // Reset observer instance to get new mock context on every run RescuePartyObserver.reset(); Loading Loading @@ -241,28 +258,52 @@ public class RescuePartyTest { @Test public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevels() { notePersistentAppCrash(1); noteAppCrash(1, true); verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null, /*configResetVerifiedTimesMap=*/ null); notePersistentAppCrash(2); noteAppCrash(2, true); verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null, /*configResetVerifiedTimesMap=*/ null); notePersistentAppCrash(3); noteAppCrash(3, true); verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null, /*configResetVerifiedTimesMap=*/ null); notePersistentAppCrash(4); noteAppCrash(4, true); assertTrue(RescueParty.isRebootPropertySet()); notePersistentAppCrash(5); noteAppCrash(5, true); assertTrue(RescueParty.isFactoryResetPropertySet()); } @Test public void testNonPersistentAppOnlyPerformsFlagResets() { noteAppCrash(1, false); verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null, /*configResetVerifiedTimesMap=*/ null); noteAppCrash(2, false); verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null, /*configResetVerifiedTimesMap=*/ null); noteAppCrash(3, false); verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null, /*configResetVerifiedTimesMap=*/ null); noteAppCrash(4, false); assertFalse(RescueParty.isRebootPropertySet()); noteAppCrash(5, false); assertFalse(RescueParty.isFactoryResetPropertySet()); } @Test public void testNonPersistentAppCrashDetectionWithScopedResets() { RescueParty.onSettingsProviderPublished(mMockContext); Loading Loading @@ -311,11 +352,11 @@ public class RescuePartyTest { observer.execute(new VersionedPackage( CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4); assertTrue(RescueParty.isRebootPropertySet()); assertFalse(RescueParty.isRebootPropertySet()); observer.execute(new VersionedPackage( CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5); assertTrue(RescueParty.isFactoryResetPropertySet()); assertFalse(RescueParty.isFactoryResetPropertySet()); } @Test Loading Loading @@ -376,11 +417,11 @@ public class RescuePartyTest { observer.execute(new VersionedPackage( CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4); assertTrue(RescueParty.isRebootPropertySet()); assertFalse(RescueParty.isRebootPropertySet()); observer.execute(new VersionedPackage( CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5); assertTrue(RescueParty.isFactoryResetPropertySet()); assertFalse(RescueParty.isFactoryResetPropertySet()); } @Test Loading Loading @@ -627,9 +668,10 @@ public class RescuePartyTest { RescuePartyObserver.getInstance(mMockContext).executeBootLoopMitigation(mitigationCount); } private void notePersistentAppCrash(int mitigationCount) { private void noteAppCrash(int mitigationCount, boolean isPersistent) { String packageName = isPersistent ? PERSISTENT_PACKAGE : NON_PERSISTENT_PACKAGE; RescuePartyObserver.getInstance(mMockContext).execute(new VersionedPackage( "com.package.name", 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, mitigationCount); packageName, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, mitigationCount); } private Bundle getConfigAccessBundle(String callingPackage, String namespace) { Loading Loading
services/core/java/com/android/server/RescueParty.java +42 −16 Original line number Diff line number Diff line Loading @@ -327,18 +327,23 @@ public class RescueParty { } } private static int getMaxRescueLevel() { return SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false) ? LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS : LEVEL_FACTORY_RESET; private static int getMaxRescueLevel(boolean mayPerformFactoryReset) { if (!mayPerformFactoryReset || SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)) { return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS; } return LEVEL_FACTORY_RESET; } /** * 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. * @return the rescue level for the n-th mitigation attempt. */ private static int getRescueLevel(int mitigationCount) { private static int getRescueLevel(int mitigationCount, boolean mayPerformFactoryReset) { if (mitigationCount == 1) { return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS; } else if (mitigationCount == 2) { Loading @@ -346,9 +351,9 @@ public class RescueParty { } else if (mitigationCount == 3) { return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS; } else if (mitigationCount == 4) { return Math.min(getMaxRescueLevel(), LEVEL_WARM_REBOOT); return Math.min(getMaxRescueLevel(mayPerformFactoryReset), LEVEL_WARM_REBOOT); } else if (mitigationCount >= 5) { return Math.min(getMaxRescueLevel(), LEVEL_FACTORY_RESET); return Math.min(getMaxRescueLevel(mayPerformFactoryReset), LEVEL_FACTORY_RESET); } else { Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount); return LEVEL_NONE; Loading Loading @@ -614,7 +619,8 @@ public class RescueParty { @FailureReasons int failureReason, int mitigationCount) { if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) { return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount)); return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, mayPerformFactoryReset(failedPackage))); } else { return PackageHealthObserverImpact.USER_IMPACT_NONE; } Loading @@ -628,7 +634,8 @@ public class RescueParty { } if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) { final int level = getRescueLevel(mitigationCount); final int level = getRescueLevel(mitigationCount, mayPerformFactoryReset(failedPackage)); executeRescueLevel(mContext, failedPackage == null ? null : failedPackage.getPackageName(), level); return true; Loading @@ -653,12 +660,7 @@ public class RescueParty { } catch (PackageManager.NameNotFoundException ignore) { } try { ApplicationInfo info = pm.getApplicationInfo(packageName, 0); return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK; } catch (PackageManager.NameNotFoundException e) { return false; } return isPersistentSystemApp(packageName); } @Override Loading @@ -666,7 +668,7 @@ public class RescueParty { if (isDisabled()) { return PackageHealthObserverImpact.USER_IMPACT_NONE; } return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount)); return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, true)); } @Override Loading @@ -674,7 +676,8 @@ public class RescueParty { if (isDisabled()) { return false; } executeRescueLevel(mContext, /*failedPackage=*/ null, getRescueLevel(mitigationCount)); executeRescueLevel(mContext, /*failedPackage=*/ null, getRescueLevel(mitigationCount, true)); return true; } Loading @@ -683,6 +686,29 @@ public class RescueParty { return NAME; } /** * Returns {@code true} if the failing package is non-null and performing a reboot or * prompting a factory reset is an acceptable mitigation strategy for the package's * failure, {@code false} otherwise. */ private boolean mayPerformFactoryReset(@Nullable VersionedPackage failingPackage) { if (failingPackage == null) { return false; } return isPersistentSystemApp(failingPackage.getPackageName()); } private boolean isPersistentSystemApp(@NonNull String packageName) { PackageManager pm = mContext.getPackageManager(); try { ApplicationInfo info = pm.getApplicationInfo(packageName, 0); return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK; } catch (PackageManager.NameNotFoundException e) { return false; } } private synchronized void recordDeviceConfigAccess(@NonNull String callingPackage, @NonNull String namespace) { // Record it in calling packages to namespace map Loading
services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java +53 −11 Original line number Diff line number Diff line Loading @@ -37,6 +37,8 @@ import static org.mockito.Mockito.times; import android.content.ContentResolver; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; import android.os.Bundle; import android.os.RecoverySystem; Loading Loading @@ -83,6 +85,8 @@ public class RescuePartyTest { private static final String CALLING_PACKAGE1 = "com.package.name1"; private static final String CALLING_PACKAGE2 = "com.package.name2"; private static final String CALLING_PACKAGE3 = "com.package.name3"; private static final String PERSISTENT_PACKAGE = "com.persistent.package"; private static final String NON_PERSISTENT_PACKAGE = "com.nonpersistent.package"; private static final String NAMESPACE1 = "namespace1"; private static final String NAMESPACE2 = "namespace2"; private static final String NAMESPACE3 = "namespace3"; Loading @@ -103,6 +107,8 @@ public class RescuePartyTest { private PackageWatchdog mMockPackageWatchdog; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private ContentResolver mMockContentResolver; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private PackageManager mPackageManager; @Captor private ArgumentCaptor<RemoteCallback> mMonitorCallbackCaptor; Loading @@ -129,6 +135,17 @@ public class RescuePartyTest { mNamespacesWiped = new HashSet<>(); when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver); when(mMockContext.getPackageManager()).thenReturn(mPackageManager); ApplicationInfo persistentApplicationInfo = new ApplicationInfo(); persistentApplicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_PERSISTENT; // If the package name is PERSISTENT_PACKAGE, then set the flags to be persistent and // system. Don't set any flags otherwise. when(mPackageManager.getApplicationInfo(eq(PERSISTENT_PACKAGE), anyInt())).thenReturn(persistentApplicationInfo); when(mPackageManager.getApplicationInfo(eq(NON_PERSISTENT_PACKAGE), anyInt())).thenReturn(new ApplicationInfo()); // Reset observer instance to get new mock context on every run RescuePartyObserver.reset(); Loading Loading @@ -241,28 +258,52 @@ public class RescuePartyTest { @Test public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevels() { notePersistentAppCrash(1); noteAppCrash(1, true); verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null, /*configResetVerifiedTimesMap=*/ null); notePersistentAppCrash(2); noteAppCrash(2, true); verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null, /*configResetVerifiedTimesMap=*/ null); notePersistentAppCrash(3); noteAppCrash(3, true); verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null, /*configResetVerifiedTimesMap=*/ null); notePersistentAppCrash(4); noteAppCrash(4, true); assertTrue(RescueParty.isRebootPropertySet()); notePersistentAppCrash(5); noteAppCrash(5, true); assertTrue(RescueParty.isFactoryResetPropertySet()); } @Test public void testNonPersistentAppOnlyPerformsFlagResets() { noteAppCrash(1, false); verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null, /*configResetVerifiedTimesMap=*/ null); noteAppCrash(2, false); verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null, /*configResetVerifiedTimesMap=*/ null); noteAppCrash(3, false); verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null, /*configResetVerifiedTimesMap=*/ null); noteAppCrash(4, false); assertFalse(RescueParty.isRebootPropertySet()); noteAppCrash(5, false); assertFalse(RescueParty.isFactoryResetPropertySet()); } @Test public void testNonPersistentAppCrashDetectionWithScopedResets() { RescueParty.onSettingsProviderPublished(mMockContext); Loading Loading @@ -311,11 +352,11 @@ public class RescuePartyTest { observer.execute(new VersionedPackage( CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4); assertTrue(RescueParty.isRebootPropertySet()); assertFalse(RescueParty.isRebootPropertySet()); observer.execute(new VersionedPackage( CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5); assertTrue(RescueParty.isFactoryResetPropertySet()); assertFalse(RescueParty.isFactoryResetPropertySet()); } @Test Loading Loading @@ -376,11 +417,11 @@ public class RescuePartyTest { observer.execute(new VersionedPackage( CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4); assertTrue(RescueParty.isRebootPropertySet()); assertFalse(RescueParty.isRebootPropertySet()); observer.execute(new VersionedPackage( CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5); assertTrue(RescueParty.isFactoryResetPropertySet()); assertFalse(RescueParty.isFactoryResetPropertySet()); } @Test Loading Loading @@ -627,9 +668,10 @@ public class RescuePartyTest { RescuePartyObserver.getInstance(mMockContext).executeBootLoopMitigation(mitigationCount); } private void notePersistentAppCrash(int mitigationCount) { private void noteAppCrash(int mitigationCount, boolean isPersistent) { String packageName = isPersistent ? PERSISTENT_PACKAGE : NON_PERSISTENT_PACKAGE; RescuePartyObserver.getInstance(mMockContext).execute(new VersionedPackage( "com.package.name", 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, mitigationCount); packageName, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, mitigationCount); } private Bundle getConfigAccessBundle(String callingPackage, String namespace) { Loading