Merge "Factory Reset flag & minor refinement over flag reset scopes"

This commit is contained in:
Hongyi Zhang
2020-11-03 23:34:41 +00:00
committed by Android (Google) Code Review
2 changed files with 85 additions and 15 deletions

View File

@@ -56,6 +56,7 @@ import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -100,6 +101,10 @@ public class RescueParty {
private static final String PROP_VIRTUAL_DEVICE = "ro.hardware.virtual_device";
private static final String PROP_DEVICE_CONFIG_DISABLE_FLAG =
"persist.device_config.configuration.disable_rescue_party";
private static final String PROP_DISABLE_FACTORY_RESET_FLAG =
"persist.device_config.configuration.disable_rescue_party_factory_reset";
// The DeviceConfig namespace containing all RescueParty switches.
private static final String NAMESPACE_CONFIGURATION = "configuration";
private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT
| ApplicationInfo.FLAG_SYSTEM;
@@ -215,6 +220,10 @@ public class RescueParty {
if (SettingsToPropertiesMapper.isNativeFlagsResetPerformed()) {
String[] resetNativeCategories = SettingsToPropertiesMapper.getResetNativeCategories();
for (int i = 0; i < resetNativeCategories.length; i++) {
// Don't let RescueParty reset the namespace for RescueParty switches.
if (NAMESPACE_CONFIGURATION.equals(resetNativeCategories[i])) {
continue;
}
DeviceConfig.resetToDefaults(Settings.RESET_MODE_TRUSTED_DEFAULTS,
resetNativeCategories[i]);
}
@@ -225,8 +234,10 @@ public class RescueParty {
* Get the next rescue level. This indicates the next level of mitigation that may be taken.
*/
private static int getNextRescueLevel() {
int maxRescueLevel = SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)
? LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS : LEVEL_FACTORY_RESET;
return MathUtils.constrain(SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE) + 1,
LEVEL_NONE, LEVEL_FACTORY_RESET);
LEVEL_NONE, maxRescueLevel);
}
/**
@@ -349,12 +360,30 @@ public class RescueParty {
private static void resetDeviceConfig(Context context, int resetMode,
@Nullable String failedPackage) {
if (!shouldPerformScopedResets() || failedPackage == null) {
DeviceConfig.resetToDefaults(resetMode, /*namespace=*/ null);
resetAllAffectedNamespaces(context, resetMode);
} else {
performScopedReset(context, resetMode, failedPackage);
}
}
private static void resetAllAffectedNamespaces(Context context, int resetMode) {
RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
Set<String> allAffectedNamespaces = rescuePartyObserver.getAllAffectedNamespaceSet();
Slog.w(TAG,
"Performing reset for all affected namespaces: "
+ Arrays.toString(allAffectedNamespaces.toArray()));
Iterator<String> it = allAffectedNamespaces.iterator();
while (it.hasNext()) {
String namespace = it.next();
// Don't let RescueParty reset the namespace for RescueParty switches.
if (NAMESPACE_CONFIGURATION.equals(namespace)) {
continue;
}
DeviceConfig.resetToDefaults(resetMode, namespace);
}
}
private static boolean shouldPerformScopedResets() {
int rescueLevel = MathUtils.constrain(
SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE),
@@ -367,16 +396,21 @@ public class RescueParty {
RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
Set<String> affectedNamespaces = rescuePartyObserver.getAffectedNamespaceSet(
failedPackage);
if (affectedNamespaces == null) {
DeviceConfig.resetToDefaults(resetMode, /*namespace=*/ null);
} else {
// If we can't find namespaces affected for current package,
// skip this round of reset.
if (affectedNamespaces != null) {
Slog.w(TAG,
"Performing scoped reset for package: " + failedPackage
+ ", affected namespaces: "
+ Arrays.toString(affectedNamespaces.toArray()));
Iterator<String> it = affectedNamespaces.iterator();
while (it.hasNext()) {
DeviceConfig.resetToDefaults(resetMode, it.next());
String namespace = it.next();
// Don't let RescueParty reset the namespace for RescueParty switches.
if (NAMESPACE_CONFIGURATION.equals(namespace)) {
continue;
}
DeviceConfig.resetToDefaults(resetMode, namespace);
}
}
}
@@ -514,6 +548,10 @@ public class RescueParty {
return mCallingPackageNamespaceSetMap.get(failedPackage);
}
private synchronized Set<String> getAllAffectedNamespaceSet() {
return new HashSet<String>(mNamespaceCallingPackageSetMap.keySet());
}
private synchronized Set<String> getCallingPackagesSet(String namespace) {
return mNamespaceCallingPackageSetMap.get(namespace);
}

View File

@@ -27,9 +27,11 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.RescueParty.LEVEL_FACTORY_RESET;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import android.content.ContentResolver;
@@ -79,8 +81,11 @@ public class RescuePartyTest {
private static final String CALLING_PACKAGE2 = "com.package.name2";
private static final String NAMESPACE1 = "namespace1";
private static final String NAMESPACE2 = "namespace2";
private static final String NAMESPACE3 = "namespace3";
private static final String PROP_DEVICE_CONFIG_DISABLE_FLAG =
"persist.device_config.configuration.disable_rescue_party";
private static final String PROP_DISABLE_FACTORY_RESET_FLAG =
"persist.device_config.configuration.disable_rescue_party_factory_reset";
private MockitoSession mSession;
private HashMap<String, String> mSystemSettingsMap;
@@ -183,27 +188,38 @@ public class RescuePartyTest {
@Test
public void testBootLoopDetectionWithExecutionForAllRescueLevels() {
RescueParty.onSettingsProviderPublished(mMockContext);
verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver),
mMonitorCallbackCaptor.capture()));
noteBoot();
verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
// Record DeviceConfig accesses
RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue();
monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1));
monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE2));
final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
noteBoot();
verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null);
verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, expectedAllResetNamespaces);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
noteBoot();
verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null);
verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
noteBoot();
verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
assertEquals(LEVEL_FACTORY_RESET,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
}
@@ -230,7 +246,6 @@ public class RescuePartyTest {
notePersistentAppCrash();
verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
assertEquals(LEVEL_FACTORY_RESET,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
}
@@ -247,6 +262,7 @@ public class RescuePartyTest {
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));
verify(mMockPackageWatchdog).startObservingHealth(observer,
@@ -255,10 +271,15 @@ public class RescuePartyTest {
verify(mMockPackageWatchdog, times(2)).startObservingHealth(eq(observer),
mPackageListCaptor.capture(),
eq(RescueParty.DEFAULT_OBSERVING_DURATION_MS));
monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE3));
verify(mMockPackageWatchdog).startObservingHealth(observer,
Arrays.asList(CALLING_PACKAGE2), RescueParty.DEFAULT_OBSERVING_DURATION_MS);
assertTrue(mPackageListCaptor.getValue().containsAll(
Arrays.asList(CALLING_PACKAGE1, CALLING_PACKAGE2)));
// Perform and verify scoped resets
final String[] expectedResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
final String[] expectedAllResetNamespaces =
new String[]{NAMESPACE1, NAMESPACE2, NAMESPACE3};
observer.execute(new VersionedPackage(
CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH);
verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, expectedResetNamespaces);
@@ -273,13 +294,12 @@ public class RescuePartyTest {
observer.execute(new VersionedPackage(
CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING);
verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/null);
verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
observer.execute(new VersionedPackage(
CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH);
verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
assertTrue(RescueParty.isAttemptingFactoryReset());
}
@@ -288,7 +308,6 @@ public class RescuePartyTest {
for (int i = 0; i < LEVEL_FACTORY_RESET; i++) {
noteBoot();
}
verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
assertTrue(RescueParty.isAttemptingFactoryReset());
}
@@ -337,11 +356,24 @@ public class RescuePartyTest {
assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING), false);
// Restore the property value initalized in SetUp()
// Restore the property value initialized in SetUp()
SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false));
}
@Test
public void testDisablingFactoryResetByDeviceConfigFlag() {
SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, Boolean.toString(true));
for (int i = 0; i < LEVEL_FACTORY_RESET; i++) {
noteBoot();
}
assertFalse(RescueParty.isAttemptingFactoryReset());
// Restore the property value initialized in SetUp()
SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, "");
}
@Test
public void testHealthCheckLevels() {
RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
@@ -437,7 +469,7 @@ public class RescuePartyTest {
eq(resetMode), anyInt()));
// Verify DeviceConfig resets
if (resetNamespaces == null) {
verify(() -> DeviceConfig.resetToDefaults(resetMode, /*namespace=*/ null));
verify(() -> DeviceConfig.resetToDefaults(anyInt(), anyString()), never());
} else {
for (String namespace : resetNamespaces) {
verify(() -> DeviceConfig.resetToDefaults(resetMode, namespace));