diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index aa511cc46de98..fb81d675939cf 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -399,6 +399,13 @@ public final class DeviceConfig { public static final String NAMESPACE_CONNECTIVITY_THERMAL_POWER_MANAGER = "connectivity_thermal_power_manager"; + /** + * Namespace for configuration related features. + * + * @hide + */ + public static final String NAMESPACE_CONFIGURATION = "configuration"; + private static final Object sLock = new Object(); @GuardedBy("sLock") private static ArrayMap> sListeners = diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java index 80036bb4b4fa5..808d322020cb1 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -99,6 +99,8 @@ public class RescueParty { private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue"; private static final String PROP_VIRTUAL_DEVICE = "ro.hardware.virtual_device"; + private static final String DEVICE_CONFIG_DISABLE_FLAG = "disable_rescue_party"; + private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT | ApplicationInfo.FLAG_SYSTEM; @@ -114,6 +116,14 @@ public class RescueParty { return false; } + // We're disabled if the DeviceConfig disable flag is set to true. + // This is in case that an emergency rollback of the feature is needed. + if (DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_CONFIGURATION, DEVICE_CONFIG_DISABLE_FLAG, false)) { + Slog.v(TAG, "Disabled because of DeviceConfig flag"); + return true; + } + // We're disabled on all engineering devices if (Build.IS_ENG) { Slog.v(TAG, "Disabled because of eng build"); diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 0a8e70c7d772f..bac7565adfa57 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -80,6 +80,7 @@ public class SettingsToPropertiesMapper { @VisibleForTesting static final String[] sDeviceConfigScopes = new String[] { DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT, + DeviceConfig.NAMESPACE_CONFIGURATION, DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT, DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS, DeviceConfig.NAMESPACE_MEDIA_NATIVE, diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java index c94bb879f4601..5c8220049d09d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java @@ -79,6 +79,7 @@ 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 DISABLE_RESCUE_PARTY_FLAG = "disable_rescue_party"; private MockitoSession mSession; private HashMap mSystemSettingsMap; @@ -316,6 +317,13 @@ public class RescuePartyTest { @Test public void testExplicitlyEnablingAndDisablingRescue() { + // mock the DeviceConfig get call to avoid hitting + // android.permission.READ_DEVICE_CONFIG when calling real DeviceConfig. + doReturn(true) + .when(() -> DeviceConfig.getBoolean( + eq(DeviceConfig.NAMESPACE_CONFIGURATION), + eq(DISABLE_RESCUE_PARTY_FLAG), + eq(false))); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false)); SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true)); assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, @@ -326,6 +334,22 @@ public class RescuePartyTest { PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)); } + @Test + public void testDisablingRescueByDeviceConfigFlag() { + doReturn(true) + .when(() -> DeviceConfig.getBoolean( + eq(DeviceConfig.NAMESPACE_CONFIGURATION), + eq(DISABLE_RESCUE_PARTY_FLAG), + eq(false))); + SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false)); + + assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING), false); + + // Restore the property value initalized in SetUp() + SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true)); + } + @Test public void testHealthCheckLevels() { RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);