Merge "Restrict Rescue Party factory reset packages" into sc-dev

This commit is contained in:
Gavin Corkery
2021-09-24 08:46:59 +00:00
committed by Android (Google) Code Review
2 changed files with 95 additions and 27 deletions

View File

@@ -327,18 +327,23 @@ public class RescueParty {
} }
} }
private static int getMaxRescueLevel() { private static int getMaxRescueLevel(boolean mayPerformFactoryReset) {
return SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false) if (!mayPerformFactoryReset
? LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS : LEVEL_FACTORY_RESET; || 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. * 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 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. * @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) { if (mitigationCount == 1) {
return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS; return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS;
} else if (mitigationCount == 2) { } else if (mitigationCount == 2) {
@@ -346,9 +351,9 @@ public class RescueParty {
} else if (mitigationCount == 3) { } else if (mitigationCount == 3) {
return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS; return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS;
} else if (mitigationCount == 4) { } else if (mitigationCount == 4) {
return Math.min(getMaxRescueLevel(), LEVEL_WARM_REBOOT); return Math.min(getMaxRescueLevel(mayPerformFactoryReset), LEVEL_WARM_REBOOT);
} else if (mitigationCount >= 5) { } else if (mitigationCount >= 5) {
return Math.min(getMaxRescueLevel(), LEVEL_FACTORY_RESET); return Math.min(getMaxRescueLevel(mayPerformFactoryReset), LEVEL_FACTORY_RESET);
} else { } else {
Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount); Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount);
return LEVEL_NONE; return LEVEL_NONE;
@@ -614,7 +619,8 @@ public class RescueParty {
@FailureReasons int failureReason, int mitigationCount) { @FailureReasons int failureReason, int mitigationCount) {
if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
|| failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) { || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) {
return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount)); return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount,
mayPerformFactoryReset(failedPackage)));
} else { } else {
return PackageHealthObserverImpact.USER_IMPACT_NONE; return PackageHealthObserverImpact.USER_IMPACT_NONE;
} }
@@ -628,7 +634,8 @@ public class RescueParty {
} }
if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
|| failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) { || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
final int level = getRescueLevel(mitigationCount); final int level = getRescueLevel(mitigationCount,
mayPerformFactoryReset(failedPackage));
executeRescueLevel(mContext, executeRescueLevel(mContext,
failedPackage == null ? null : failedPackage.getPackageName(), level); failedPackage == null ? null : failedPackage.getPackageName(), level);
return true; return true;
@@ -653,12 +660,7 @@ public class RescueParty {
} catch (PackageManager.NameNotFoundException ignore) { } catch (PackageManager.NameNotFoundException ignore) {
} }
try { return isPersistentSystemApp(packageName);
ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
} }
@Override @Override
@@ -666,7 +668,7 @@ public class RescueParty {
if (isDisabled()) { if (isDisabled()) {
return PackageHealthObserverImpact.USER_IMPACT_NONE; return PackageHealthObserverImpact.USER_IMPACT_NONE;
} }
return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount)); return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, true));
} }
@Override @Override
@@ -674,7 +676,8 @@ public class RescueParty {
if (isDisabled()) { if (isDisabled()) {
return false; return false;
} }
executeRescueLevel(mContext, /*failedPackage=*/ null, getRescueLevel(mitigationCount)); executeRescueLevel(mContext, /*failedPackage=*/ null,
getRescueLevel(mitigationCount, true));
return true; return true;
} }
@@ -683,6 +686,29 @@ public class RescueParty {
return NAME; 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, private synchronized void recordDeviceConfigAccess(@NonNull String callingPackage,
@NonNull String namespace) { @NonNull String namespace) {
// Record it in calling packages to namespace map // Record it in calling packages to namespace map

View File

@@ -37,6 +37,8 @@ import static org.mockito.Mockito.times;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage; import android.content.pm.VersionedPackage;
import android.os.Bundle; import android.os.Bundle;
import android.os.RecoverySystem; import android.os.RecoverySystem;
@@ -83,6 +85,8 @@ public class RescuePartyTest {
private static final String CALLING_PACKAGE1 = "com.package.name1"; private static final String CALLING_PACKAGE1 = "com.package.name1";
private static final String CALLING_PACKAGE2 = "com.package.name2"; private static final String CALLING_PACKAGE2 = "com.package.name2";
private static final String CALLING_PACKAGE3 = "com.package.name3"; 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 NAMESPACE1 = "namespace1";
private static final String NAMESPACE2 = "namespace2"; private static final String NAMESPACE2 = "namespace2";
private static final String NAMESPACE3 = "namespace3"; private static final String NAMESPACE3 = "namespace3";
@@ -103,6 +107,8 @@ public class RescuePartyTest {
private PackageWatchdog mMockPackageWatchdog; private PackageWatchdog mMockPackageWatchdog;
@Mock(answer = Answers.RETURNS_DEEP_STUBS) @Mock(answer = Answers.RETURNS_DEEP_STUBS)
private ContentResolver mMockContentResolver; private ContentResolver mMockContentResolver;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private PackageManager mPackageManager;
@Captor @Captor
private ArgumentCaptor<RemoteCallback> mMonitorCallbackCaptor; private ArgumentCaptor<RemoteCallback> mMonitorCallbackCaptor;
@@ -129,6 +135,17 @@ public class RescuePartyTest {
mNamespacesWiped = new HashSet<>(); mNamespacesWiped = new HashSet<>();
when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver); 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 // Reset observer instance to get new mock context on every run
RescuePartyObserver.reset(); RescuePartyObserver.reset();
@@ -241,28 +258,52 @@ public class RescuePartyTest {
@Test @Test
public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevels() { public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevels() {
notePersistentAppCrash(1); noteAppCrash(1, true);
verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null, verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
/*configResetVerifiedTimesMap=*/ null); /*configResetVerifiedTimesMap=*/ null);
notePersistentAppCrash(2); noteAppCrash(2, true);
verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null, verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null,
/*configResetVerifiedTimesMap=*/ null); /*configResetVerifiedTimesMap=*/ null);
notePersistentAppCrash(3); noteAppCrash(3, true);
verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null, verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
/*configResetVerifiedTimesMap=*/ null); /*configResetVerifiedTimesMap=*/ null);
notePersistentAppCrash(4); noteAppCrash(4, true);
assertTrue(RescueParty.isRebootPropertySet()); assertTrue(RescueParty.isRebootPropertySet());
notePersistentAppCrash(5); noteAppCrash(5, true);
assertTrue(RescueParty.isFactoryResetPropertySet()); 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 @Test
public void testNonPersistentAppCrashDetectionWithScopedResets() { public void testNonPersistentAppCrashDetectionWithScopedResets() {
RescueParty.onSettingsProviderPublished(mMockContext); RescueParty.onSettingsProviderPublished(mMockContext);
@@ -311,11 +352,11 @@ public class RescuePartyTest {
observer.execute(new VersionedPackage( observer.execute(new VersionedPackage(
CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4); CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4);
assertTrue(RescueParty.isRebootPropertySet()); assertFalse(RescueParty.isRebootPropertySet());
observer.execute(new VersionedPackage( observer.execute(new VersionedPackage(
CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5); CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5);
assertTrue(RescueParty.isFactoryResetPropertySet()); assertFalse(RescueParty.isFactoryResetPropertySet());
} }
@Test @Test
@@ -376,11 +417,11 @@ public class RescuePartyTest {
observer.execute(new VersionedPackage( observer.execute(new VersionedPackage(
CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4); CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4);
assertTrue(RescueParty.isRebootPropertySet()); assertFalse(RescueParty.isRebootPropertySet());
observer.execute(new VersionedPackage( observer.execute(new VersionedPackage(
CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5); CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5);
assertTrue(RescueParty.isFactoryResetPropertySet()); assertFalse(RescueParty.isFactoryResetPropertySet());
} }
@Test @Test
@@ -627,9 +668,10 @@ public class RescuePartyTest {
RescuePartyObserver.getInstance(mMockContext).executeBootLoopMitigation(mitigationCount); 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( 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) { private Bundle getConfigAccessBundle(String callingPackage, String namespace) {