diff options
| author | 2022-06-09 16:02:03 +0000 | |
|---|---|---|
| committer | 2022-06-10 10:26:20 +0000 | |
| commit | 06272cf2fd9628b45f23a0b3a3cd456009f41c11 (patch) | |
| tree | afc0f586ec47d17dd6a952258a1f5fe0ee38454c | |
| parent | 4316f708257d54ea27888a6d4d811514130e4ee1 (diff) | |
Add DeviceConfig feature flag type to SystemUI
Adds support of DeviceConfig feature flags.
DeviceConfig flags are used by Phenotype
A/B testing framework.
Bug: 218314434
Test: change phenotype flag =>
check that FeatureFlags returns correct value
Test: atest com.android.systemui.flags.FeatureFlagsDebugTest
Test: atest com.android.systemui.flags.FeatureFlagsReleaseTest
Change-Id: I7dd9e47280ba56b7f9ea552c0d44d3e36fc6b041
7 files changed, 102 insertions, 1 deletions
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 19a53091b35c..b4646aee444c 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<T> : Flag<T> { val resourceId: Int } +interface DeviceConfigFlag<T> : Flag<T> { + val name: String + val namespace: String + val default: T +} + interface SysPropFlag<T> : Flag<T> { val name: String val default: T @@ -74,6 +80,14 @@ data class ResourceBooleanFlag @JvmOverloads constructor( override val teamfood: Boolean = false ) : ResourceFlag<Boolean> +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<Boolean> + 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 9d6e3c295100..2cee2520ce55 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt @@ -29,6 +29,9 @@ interface FeatureFlags : FlagListenable { 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 /** Returns a string value for the given flag. */ diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java index c4531b573d22..49b3908558d8 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<Integer, Flag<?>> mAllFlags; private final Map<Integer, Boolean> mBooleanFlagCache = new TreeMap<>(); private final Map<Integer, String> 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<Integer, Flag<?>> 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; @@ -138,6 +143,18 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { } @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(); if (!mBooleanFlagCache.containsKey(id)) { @@ -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 cacef161a862..1492a2bc0ceb 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<String> 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); } @@ -79,6 +84,18 @@ public class FeatureFlagsRelease implements FeatureFlags, Dumpable { } @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()); if (cacheIndex < 0) { diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index 37d4b5061760..2d9d759cba95 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 b43856aae4cf..51f3404a9bca 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<Int> + 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 @@ -195,6 +198,21 @@ class FeatureFlagsDebugTest : 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(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<String>(eq(3), any())).thenReturn("foo") whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar") 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 b5deb0b55e11..6b683f456ea7 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 @@ -85,6 +89,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 val flagName = "sys_prop_flag" |