Merge "Factory Reset flag & minor refinement over flag reset scopes"
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user