Merge "Restrict Rescue Party factory reset packages" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
67306315aa
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user