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
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
@@ -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<String>(eq(3), any())).thenReturn("foo")
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user