diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt index 19a53091b35c2..b4646aee444ca 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt @@ -36,6 +36,12 @@ interface ResourceFlag : Flag { val resourceId: Int } +interface DeviceConfigFlag : Flag { + val name: String + val namespace: String + val default: T +} + interface SysPropFlag : Flag { val name: String val default: T @@ -74,6 +80,14 @@ data class ResourceBooleanFlag @JvmOverloads constructor( override val teamfood: Boolean = false ) : ResourceFlag +data class DeviceConfigBooleanFlag @JvmOverloads constructor( + override val id: Int, + override val name: String, + override val namespace: String, + override val default: Boolean = false, + override val teamfood: Boolean = false +) : DeviceConfigFlag + data class SysPropBooleanFlag @JvmOverloads constructor( override val id: Int, override val name: String, diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt index 9d6e3c2951009..2cee2520ce556 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt @@ -28,6 +28,9 @@ interface FeatureFlags : FlagListenable { /** Returns a boolean value for the given flag. */ fun isEnabled(flag: ResourceBooleanFlag): Boolean + /** Returns a boolean value for the given flag. */ + fun isEnabled(flag: DeviceConfigBooleanFlag): Boolean + /** Returns a boolean value for the given flag. */ fun isEnabled(flag: SysPropBooleanFlag): Boolean diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java index c4531b573d227..49b3908558d8c 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java @@ -32,6 +32,7 @@ import android.content.res.Resources; import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; +import android.provider.DeviceConfig; import android.util.Log; import androidx.annotation.NonNull; @@ -44,6 +45,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.commandline.Command; import com.android.systemui.statusbar.commandline.CommandRegistry; +import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.settings.SecureSettings; import java.io.PrintWriter; @@ -81,6 +83,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { private final SecureSettings mSecureSettings; private final Resources mResources; private final SystemPropertiesHelper mSystemProperties; + private final DeviceConfigProxy mDeviceConfigProxy; private final Map> mAllFlags; private final Map mBooleanFlagCache = new TreeMap<>(); private final Map mStringFlagCache = new TreeMap<>(); @@ -94,6 +97,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { SystemPropertiesHelper systemProperties, @Main Resources resources, DumpManager dumpManager, + DeviceConfigProxy deviceConfigProxy, @Named(ALL_FLAGS) Map> allFlags, CommandRegistry commandRegistry, IStatusBarService barService) { @@ -101,6 +105,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { mSecureSettings = secureSettings; mResources = resources; mSystemProperties = systemProperties; + mDeviceConfigProxy = deviceConfigProxy; mAllFlags = allFlags; mBarService = barService; @@ -137,6 +142,18 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { return mBooleanFlagCache.get(id); } + @Override + public boolean isEnabled(@NonNull DeviceConfigBooleanFlag flag) { + int id = flag.getId(); + if (!mBooleanFlagCache.containsKey(id)) { + boolean deviceConfigValue = mDeviceConfigProxy.getBoolean(flag.getNamespace(), + flag.getName(), flag.getDefault()); + mBooleanFlagCache.put(id, readFlagValue(id, deviceConfigValue)); + } + + return mBooleanFlagCache.get(id); + } + @Override public boolean isEnabled(@NonNull SysPropBooleanFlag flag) { int id = flag.getId(); @@ -293,6 +310,8 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE); } else if (flag instanceof ResourceBooleanFlag) { setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE); + } else if (flag instanceof DeviceConfigBooleanFlag) { + setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE); } else if (flag instanceof SysPropBooleanFlag) { // Store SysProp flags in SystemProperties where they can read by outside parties. mSystemProperties.setBoolean(((SysPropBooleanFlag) flag).getName(), value); @@ -394,6 +413,10 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { return new BooleanFlag( f.getId(), isEnabled((ResourceBooleanFlag) f), f.getTeamfood()); } + if (f instanceof DeviceConfigBooleanFlag) { + return new BooleanFlag( + f.getId(), isEnabled((DeviceConfigBooleanFlag) f), f.getTeamfood()); + } if (f instanceof SysPropBooleanFlag) { // TODO(b/223379190): Teamfood not supported for sysprop flags yet. return new BooleanFlag( diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java index cacef161a8626..1492a2bc0cebf 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java @@ -19,6 +19,7 @@ package com.android.systemui.flags; import static java.util.Objects.requireNonNull; import android.content.res.Resources; +import android.provider.DeviceConfig; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -28,6 +29,7 @@ import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; +import com.android.systemui.util.DeviceConfigProxy; import java.io.PrintWriter; import java.util.Map; @@ -44,6 +46,7 @@ import javax.inject.Inject; public class FeatureFlagsRelease implements FeatureFlags, Dumpable { private final Resources mResources; private final SystemPropertiesHelper mSystemProperties; + private final DeviceConfigProxy mDeviceConfigProxy; SparseBooleanArray mBooleanCache = new SparseBooleanArray(); SparseArray mStringCache = new SparseArray<>(); @@ -51,9 +54,11 @@ public class FeatureFlagsRelease implements FeatureFlags, Dumpable { public FeatureFlagsRelease( @Main Resources resources, SystemPropertiesHelper systemProperties, + DeviceConfigProxy deviceConfigProxy, DumpManager dumpManager) { mResources = resources; mSystemProperties = systemProperties; + mDeviceConfigProxy = deviceConfigProxy; dumpManager.registerDumpable("SysUIFlags", this); } @@ -78,6 +83,18 @@ public class FeatureFlagsRelease implements FeatureFlags, Dumpable { return mBooleanCache.valueAt(cacheIndex); } + @Override + public boolean isEnabled(@NonNull DeviceConfigBooleanFlag flag) { + int cacheIndex = mBooleanCache.indexOfKey(flag.getId()); + if (cacheIndex < 0) { + boolean deviceConfigValue = mDeviceConfigProxy.getBoolean(flag.getNamespace(), + flag.getName(), flag.getDefault()); + return isEnabled(flag.getId(), deviceConfigValue); + } + + return mBooleanCache.valueAt(cacheIndex); + } + @Override public boolean isEnabled(SysPropBooleanFlag flag) { int cacheIndex = mBooleanCache.indexOfKey(flag.getId()); diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index 37d4b50617609..2d9d759cba95f 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -16,6 +16,8 @@ package com.android.systemui.flags; +import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER; + import com.android.internal.annotations.Keep; import com.android.systemui.R; @@ -173,6 +175,11 @@ public class Flags { public static final SysPropBooleanFlag BUBBLES_HOME_GESTURE = new SysPropBooleanFlag(1101, "persist.wm.debug.bubbles_home_gesture", true); + @Keep + public static final DeviceConfigBooleanFlag WM_ENABLE_PARTIAL_SCREEN_SHARING = + new DeviceConfigBooleanFlag(1102, "record_task_content", + NAMESPACE_WINDOW_MANAGER, false, true); + // 1200 - predictive back @Keep public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK = new SysPropBooleanFlag( diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt index b43856aae4cf7..51f3404a9bca6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt @@ -26,6 +26,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry +import com.android.systemui.util.DeviceConfigProxyFake import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.capture @@ -75,6 +76,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() { private lateinit var broadcastReceiver: BroadcastReceiver private lateinit var clearCacheAction: Consumer + private val deviceConfig = DeviceConfigProxyFake() private val teamfoodableFlagA = BooleanFlag(500, false, true) private val teamfoodableFlagB = BooleanFlag(501, true, true) @@ -90,6 +92,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() { systemProperties, resources, dumpManager, + deviceConfig, flagMap, commandRegistry, barService @@ -194,6 +197,21 @@ class FeatureFlagsDebugTest : SysuiTestCase() { assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(5, "e"))).isFalse() } + @Test + fun testReadDeviceConfigBooleanFlag() { + val namespace = "test_namespace" + deviceConfig.setProperty(namespace, "a", "true", false) + deviceConfig.setProperty(namespace, "b", "false", false) + deviceConfig.setProperty(namespace, "c", null, false) + + assertThat(mFeatureFlagsDebug.isEnabled(DeviceConfigBooleanFlag(1, "a", namespace))) + .isTrue() + assertThat(mFeatureFlagsDebug.isEnabled(DeviceConfigBooleanFlag(2, "b", namespace))) + .isFalse() + assertThat(mFeatureFlagsDebug.isEnabled(DeviceConfigBooleanFlag(3, "c", namespace))) + .isFalse() + } + @Test fun testReadStringFlag() { whenever(flagManager.readFlagValue(eq(3), any())).thenReturn("foo") diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt index b5deb0b55e11e..6b683f456ea7c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt @@ -20,6 +20,7 @@ import android.content.res.Resources import android.test.suitebuilder.annotation.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager +import com.android.systemui.util.DeviceConfigProxyFake import com.android.systemui.util.mockito.any import com.google.common.truth.Truth.assertThat import org.junit.After @@ -44,10 +45,13 @@ class FeatureFlagsReleaseTest : SysuiTestCase() { @Mock private lateinit var mSystemProperties: SystemPropertiesHelper @Mock private lateinit var mDumpManager: DumpManager + private val deviceConfig = DeviceConfigProxyFake() + @Before fun setup() { MockitoAnnotations.initMocks(this) - mFeatureFlagsRelease = FeatureFlagsRelease(mResources, mSystemProperties, mDumpManager) + mFeatureFlagsRelease = FeatureFlagsRelease(mResources, mSystemProperties, deviceConfig, + mDumpManager) } @After @@ -84,6 +88,21 @@ class FeatureFlagsReleaseTest : SysuiTestCase() { } } + @Test + fun testReadDeviceConfigBooleanFlag() { + val namespace = "test_namespace" + deviceConfig.setProperty(namespace, "a", "true", false) + deviceConfig.setProperty(namespace, "b", "false", false) + deviceConfig.setProperty(namespace, "c", null, false) + + assertThat(mFeatureFlagsRelease.isEnabled(DeviceConfigBooleanFlag(1, "a", namespace))) + .isTrue() + assertThat(mFeatureFlagsRelease.isEnabled(DeviceConfigBooleanFlag(2, "b", namespace))) + .isFalse() + assertThat(mFeatureFlagsRelease.isEnabled(DeviceConfigBooleanFlag(3, "c", namespace))) + .isFalse() + } + @Test fun testSysPropBooleanFlag() { val flagId = 213