summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt51
-rw-r--r--packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt5
-rw-r--r--packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java87
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java108
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt80
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlags.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt175
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java6
14 files changed, 430 insertions, 215 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 a8a526a33229..f7049cf8f4f2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
@@ -48,9 +48,13 @@ interface SysPropFlag<T> : Flag<T> {
val default: T
}
+/**
+ * Base class for most common boolean flags.
+ *
+ * See [UnreleasedFlag] and [ReleasedFlag] for useful implementations.
+ */
// Consider using the "parcelize" kotlin library.
-
-data class BooleanFlag @JvmOverloads constructor(
+abstract class BooleanFlag constructor(
override val id: Int,
override val default: Boolean = false,
override val teamfood: Boolean = false,
@@ -60,7 +64,7 @@ data class BooleanFlag @JvmOverloads constructor(
companion object {
@JvmField
val CREATOR = object : Parcelable.Creator<BooleanFlag> {
- override fun createFromParcel(parcel: Parcel) = BooleanFlag(parcel)
+ override fun createFromParcel(parcel: Parcel) = object : BooleanFlag(parcel) {}
override fun newArray(size: Int) = arrayOfNulls<BooleanFlag>(size)
}
}
@@ -80,12 +84,46 @@ data class BooleanFlag @JvmOverloads constructor(
}
}
+/**
+ * A Flag that is is false by default.
+ *
+ * It can be changed or overridden in debug builds but not in release builds.
+ */
+data class UnreleasedFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val teamfood: Boolean = false,
+ override val overridden: Boolean = false
+) : BooleanFlag(id, false, teamfood, overridden)
+
+/**
+ * A Flag that is is true by default.
+ *
+ * It can be changed or overridden in any build, meaning it can be turned off if needed.
+ */
+data class ReleasedFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val teamfood: Boolean = false,
+ override val overridden: Boolean = false
+) : BooleanFlag(id, true, teamfood, overridden)
+
+/**
+ * A Flag that reads its default values from a resource overlay instead of code.
+ *
+ * Prefer [UnreleasedFlag] and [ReleasedFlag].
+ */
data class ResourceBooleanFlag @JvmOverloads constructor(
override val id: Int,
@BoolRes override val resourceId: Int,
override val teamfood: Boolean = false
) : ResourceFlag<Boolean>
+/**
+ * A Flag that can reads its overrides from DeviceConfig.
+ *
+ * This is generally useful for flags that come from or are used _outside_ of SystemUI.
+ *
+ * Prefer [UnreleasedFlag] and [ReleasedFlag].
+ */
data class DeviceConfigBooleanFlag @JvmOverloads constructor(
override val id: Int,
override val name: String,
@@ -94,6 +132,13 @@ data class DeviceConfigBooleanFlag @JvmOverloads constructor(
override val teamfood: Boolean = false
) : DeviceConfigFlag<Boolean>
+/**
+ * A Flag that can reads its overrides from System Properties.
+ *
+ * This is generally useful for flags that come from or are used _outside_ of SystemUI.
+ *
+ * Prefer [UnreleasedFlag] and [ReleasedFlag].
+ */
data class SysPropBooleanFlag @JvmOverloads constructor(
override val id: Int,
override val name: String,
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
index 256028417a1d..74bd9c6c287d 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
@@ -27,7 +27,8 @@ import dagger.Provides
import javax.inject.Named
@Module(includes = [
- SettingsUtilModule::class
+ ServerFlagReaderModule::class,
+ SettingsUtilModule::class,
])
abstract class FlagsModule {
@Binds
@@ -46,4 +47,4 @@ abstract class FlagsModule {
@Named(ALL_FLAGS)
fun providesAllFlags(): Map<Int, Flag<*>> = Flags.collectFlags()
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
index ab9e01eebaf7..38b5c9a9fa79 100644
--- a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
@@ -19,8 +19,8 @@ package com.android.systemui.flags
import dagger.Binds
import dagger.Module
-@Module
+@Module(includes = [ServerFlagReaderModule::class])
abstract class FlagsModule {
@Binds
abstract fun bindsFeatureFlagRelease(impl: FeatureFlagsRelease): FeatureFlags
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
index 2cee2520ce55..dfa3bcda7d72 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
@@ -23,7 +23,10 @@ package com.android.systemui.flags
*/
interface FeatureFlags : FlagListenable {
/** Returns a boolean value for the given flag. */
- fun isEnabled(flag: BooleanFlag): Boolean
+ fun isEnabled(flag: UnreleasedFlag): Boolean
+
+ /** Returns a boolean value for the given flag. */
+ fun isEnabled(flag: ReleasedFlag): Boolean
/** Returns a boolean value for the given flag. */
fun isEnabled(flag: ResourceBooleanFlag): Boolean
@@ -39,4 +42,4 @@ interface FeatureFlags : FlagListenable {
/** Returns a string value for the given flag. */
fun getString(flag: ResourceStringFlag): String
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
index c5221cd9641b..00c1a99983df 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
@@ -47,6 +47,8 @@ import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.settings.SecureSettings;
+import org.jetbrains.annotations.NotNull;
+
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
@@ -83,6 +85,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
private final Resources mResources;
private final SystemPropertiesHelper mSystemProperties;
private final DeviceConfigProxy mDeviceConfigProxy;
+ private final ServerFlagReader mServerFlagReader;
private final Map<Integer, Flag<?>> mAllFlags;
private final Map<Integer, Boolean> mBooleanFlagCache = new TreeMap<>();
private final Map<Integer, String> mStringFlagCache = new TreeMap<>();
@@ -97,6 +100,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
@Main Resources resources,
DumpManager dumpManager,
DeviceConfigProxy deviceConfigProxy,
+ ServerFlagReader serverFlagReader,
@Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags,
CommandRegistry commandRegistry,
IStatusBarService barService) {
@@ -105,6 +109,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
mResources = resources;
mSystemProperties = systemProperties;
mDeviceConfigProxy = deviceConfigProxy;
+ mServerFlagReader = serverFlagReader;
mAllFlags = allFlags;
mBarService = barService;
@@ -120,7 +125,16 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
}
@Override
- public boolean isEnabled(@NonNull BooleanFlag flag) {
+ public boolean isEnabled(@NotNull UnreleasedFlag flag) {
+ return isEnabledInternal(flag);
+ }
+
+ @Override
+ public boolean isEnabled(@NotNull ReleasedFlag flag) {
+ return isEnabledInternal(flag);
+ }
+
+ private boolean isEnabledInternal(@NotNull BooleanFlag flag) {
int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
mBooleanFlagCache.put(id,
@@ -197,14 +211,17 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
/** Specific override for Boolean flags that checks against the teamfood list.*/
private boolean readFlagValue(int id, boolean defaultValue) {
Boolean result = readBooleanFlagOverride(id);
- // Only check for teamfood if the default is false.
- if (!defaultValue && result == null && id != Flags.TEAMFOOD.getId()) {
+ boolean hasServerOverride = mServerFlagReader.hasOverride(id);
+
+ // Only check for teamfood if the default is false
+ // and there is no server override.
+ if (!hasServerOverride && !defaultValue && result == null && id != Flags.TEAMFOOD.getId()) {
if (mAllFlags.containsKey(id) && mAllFlags.get(id).getTeamfood()) {
return isEnabled(Flags.TEAMFOOD);
}
}
- return result == null ? defaultValue : result;
+ return result == null ? mServerFlagReader.readServerOverride(id, defaultValue) : result;
}
private Boolean readBooleanFlagOverride(int id) {
@@ -409,36 +426,38 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
*/
@Nullable
private ParcelableFlag<?> toParcelableFlag(Flag<?> f) {
- if (f instanceof BooleanFlag) {
- return new BooleanFlag(
- f.getId(),
- isEnabled((BooleanFlag) f),
- f.getTeamfood(),
- readBooleanFlagOverride(f.getId()) != null);
- }
- if (f instanceof ResourceBooleanFlag) {
- return new BooleanFlag(
- f.getId(),
- isEnabled((ResourceBooleanFlag) f),
- f.getTeamfood(),
- readBooleanFlagOverride(f.getId()) != null);
- }
- if (f instanceof DeviceConfigBooleanFlag) {
- return new BooleanFlag(
- f.getId(), isEnabled((DeviceConfigBooleanFlag) f), f.getTeamfood());
- }
- if (f instanceof SysPropBooleanFlag) {
+ boolean enabled;
+ boolean teamfood = f.getTeamfood();
+ boolean overridden;
+
+ if (f instanceof ReleasedFlag) {
+ enabled = isEnabled((ReleasedFlag) f);
+ overridden = readBooleanFlagOverride(f.getId()) != null;
+ } else if (f instanceof UnreleasedFlag) {
+ enabled = isEnabled((UnreleasedFlag) f);
+ overridden = readBooleanFlagOverride(f.getId()) != null;
+ } else if (f instanceof ResourceBooleanFlag) {
+ enabled = isEnabled((ResourceBooleanFlag) f);
+ overridden = readBooleanFlagOverride(f.getId()) != null;
+ } else if (f instanceof DeviceConfigBooleanFlag) {
+ enabled = isEnabled((DeviceConfigBooleanFlag) f);
+ overridden = false;
+ } else if (f instanceof SysPropBooleanFlag) {
// TODO(b/223379190): Teamfood not supported for sysprop flags yet.
- return new BooleanFlag(
- f.getId(),
- ((SysPropBooleanFlag) f).getDefault(),
- false,
- !mSystemProperties.get(((SysPropBooleanFlag) f).getName()).isEmpty());
+ enabled = isEnabled((SysPropBooleanFlag) f);
+ teamfood = false;
+ overridden = !mSystemProperties.get(((SysPropBooleanFlag) f).getName()).isEmpty();
+ } else {
+ // TODO: add support for other flag types.
+ Log.w(TAG, "Unsupported Flag Type. Please file a bug.");
+ return null;
}
- // TODO: add support for other flag types.
- Log.w(TAG, "Unsupported Flag Type. Please file a bug.");
- return null;
+ if (enabled) {
+ return new ReleasedFlag(f.getId(), teamfood, overridden);
+ } else {
+ return new UnreleasedFlag(f.getId(), teamfood, overridden);
+ }
}
};
@@ -539,8 +558,10 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
}
private boolean isBooleanFlagEnabled(Flag<?> flag) {
- if (flag instanceof BooleanFlag) {
- return isEnabled((BooleanFlag) flag);
+ if (flag instanceof ReleasedFlag) {
+ return isEnabled((ReleasedFlag) flag);
+ } else if (flag instanceof UnreleasedFlag) {
+ return isEnabled((UnreleasedFlag) flag);
} else if (flag instanceof ResourceBooleanFlag) {
return isEnabled((ResourceBooleanFlag) flag);
} else if (flag instanceof SysPropFlag) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
index 1492a2bc0ceb..049b17d383a2 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
@@ -19,7 +19,6 @@ 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;
@@ -31,6 +30,8 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.DeviceConfigProxy;
+import org.jetbrains.annotations.NotNull;
+
import java.io.PrintWriter;
import java.util.Map;
@@ -47,18 +48,22 @@ public class FeatureFlagsRelease implements FeatureFlags, Dumpable {
private final Resources mResources;
private final SystemPropertiesHelper mSystemProperties;
private final DeviceConfigProxy mDeviceConfigProxy;
+ private final ServerFlagReader mServerFlagReader;
SparseBooleanArray mBooleanCache = new SparseBooleanArray();
SparseArray<String> mStringCache = new SparseArray<>();
+ private boolean mInited;
@Inject
public FeatureFlagsRelease(
@Main Resources resources,
SystemPropertiesHelper systemProperties,
DeviceConfigProxy deviceConfigProxy,
+ ServerFlagReader serverFlagReader,
DumpManager dumpManager) {
mResources = resources;
mSystemProperties = systemProperties;
mDeviceConfigProxy = deviceConfigProxy;
+ mServerFlagReader = serverFlagReader;
dumpManager.registerDumpable("SysUIFlags", this);
}
@@ -69,8 +74,13 @@ public class FeatureFlagsRelease implements FeatureFlags, Dumpable {
public void removeListener(@NonNull Listener listener) {}
@Override
- public boolean isEnabled(BooleanFlag flag) {
- return flag.getDefault();
+ public boolean isEnabled(@NotNull UnreleasedFlag flag) {
+ return false;
+ }
+
+ @Override
+ public boolean isEnabled(@NotNull ReleasedFlag flag) {
+ return mServerFlagReader.readServerOverride(flag.getId(), true);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index f13729e57fa0..0dc07ac35e3d 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -32,7 +32,7 @@ import java.util.Map;
*
* Flag Ids are integers.
* Ids must be unique. This is enforced in a unit test.
- * Ids need not be sequential. Flags can "claim" a chunk of ids for flags in related featurs with
+ * Ids need not be sequential. Flags can "claim" a chunk of ids for flags in related features with
* a comment. This is purely for organizational purposes.
*
* On public release builds, flags will always return their default value. There is no way to
@@ -41,30 +41,30 @@ import java.util.Map;
* See {@link FeatureFlagsDebug} for instructions on flipping the flags via adb.
*/
public class Flags {
- public static final BooleanFlag TEAMFOOD = new BooleanFlag(1, false);
+ public static final UnreleasedFlag TEAMFOOD = new UnreleasedFlag(1);
/***************************************/
// 100 - notification
- public static final BooleanFlag NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
- new BooleanFlag(103, false);
+ public static final UnreleasedFlag NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
+ new UnreleasedFlag(103);
- public static final BooleanFlag NSSL_DEBUG_LINES =
- new BooleanFlag(105, false);
+ public static final UnreleasedFlag NSSL_DEBUG_LINES =
+ new UnreleasedFlag(105);
- public static final BooleanFlag NSSL_DEBUG_REMOVE_ANIMATION =
- new BooleanFlag(106, false);
+ public static final UnreleasedFlag NSSL_DEBUG_REMOVE_ANIMATION =
+ new UnreleasedFlag(106);
- public static final BooleanFlag NEW_PIPELINE_CRASH_ON_CALL_TO_OLD_PIPELINE =
- new BooleanFlag(107, false);
+ public static final UnreleasedFlag NEW_PIPELINE_CRASH_ON_CALL_TO_OLD_PIPELINE =
+ new UnreleasedFlag(107);
public static final ResourceBooleanFlag NOTIFICATION_DRAG_TO_CONTENTS =
new ResourceBooleanFlag(108, R.bool.config_notificationToContents);
- public static final BooleanFlag REMOVE_UNRANKED_NOTIFICATIONS =
- new BooleanFlag(109, false, true);
+ public static final UnreleasedFlag REMOVE_UNRANKED_NOTIFICATIONS =
+ new UnreleasedFlag(109, true);
- public static final BooleanFlag FSI_REQUIRES_KEYGUARD =
- new BooleanFlag(110, false, true);
+ public static final UnreleasedFlag FSI_REQUIRES_KEYGUARD =
+ new UnreleasedFlag(110, true);
/***************************************/
// 200 - keyguard/lockscreen
@@ -73,11 +73,11 @@ public class Flags {
// public static final BooleanFlag KEYGUARD_LAYOUT =
// new BooleanFlag(200, true);
- public static final BooleanFlag LOCKSCREEN_ANIMATIONS =
- new BooleanFlag(201, true);
+ public static final ReleasedFlag LOCKSCREEN_ANIMATIONS =
+ new ReleasedFlag(201);
- public static final BooleanFlag NEW_UNLOCK_SWIPE_ANIMATION =
- new BooleanFlag(202, true);
+ public static final ReleasedFlag NEW_UNLOCK_SWIPE_ANIMATION =
+ new ReleasedFlag(202);
public static final ResourceBooleanFlag CHARGING_RIPPLE =
new ResourceBooleanFlag(203, R.bool.flag_charging_ripple);
@@ -92,31 +92,29 @@ public class Flags {
* Whether the KeyguardBottomArea(View|Controller) should use the modern architecture or the old
* one.
*/
- public static final BooleanFlag MODERN_BOTTOM_AREA = new BooleanFlag(
- 206,
- /* default= */ false,
- /* teamfood= */ true);
+ public static final UnreleasedFlag MODERN_BOTTOM_AREA = new UnreleasedFlag(206, true);
- public static final BooleanFlag LOCKSCREEN_CUSTOM_CLOCKS = new BooleanFlag(207, false);
+
+ public static final UnreleasedFlag LOCKSCREEN_CUSTOM_CLOCKS = new UnreleasedFlag(207);
/**
* Flag to enable the usage of the new bouncer data source. This is a refactor of and
* eventual replacement of KeyguardBouncer.java.
*/
- public static final BooleanFlag MODERN_BOUNCER = new BooleanFlag(208, true);
+ public static final ReleasedFlag MODERN_BOUNCER = new ReleasedFlag(208);
/***************************************/
// 300 - power menu
- public static final BooleanFlag POWER_MENU_LITE =
- new BooleanFlag(300, true);
+ public static final ReleasedFlag POWER_MENU_LITE =
+ new ReleasedFlag(300);
/***************************************/
// 400 - smartspace
- public static final BooleanFlag SMARTSPACE_DEDUPING =
- new BooleanFlag(400, true);
+ public static final ReleasedFlag SMARTSPACE_DEDUPING =
+ new ReleasedFlag(400);
- public static final BooleanFlag SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED =
- new BooleanFlag(401, true);
+ public static final ReleasedFlag SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED =
+ new ReleasedFlag(401);
public static final ResourceBooleanFlag SMARTSPACE =
new ResourceBooleanFlag(402, R.bool.flag_smartspace);
@@ -127,11 +125,11 @@ public class Flags {
* @deprecated Not needed anymore
*/
@Deprecated
- public static final BooleanFlag NEW_USER_SWITCHER =
- new BooleanFlag(500, true);
+ public static final ReleasedFlag NEW_USER_SWITCHER =
+ new ReleasedFlag(500);
- public static final BooleanFlag COMBINED_QS_HEADERS =
- new BooleanFlag(501, false, true);
+ public static final UnreleasedFlag COMBINED_QS_HEADERS =
+ new UnreleasedFlag(501, true);
public static final ResourceBooleanFlag PEOPLE_TILE =
new ResourceBooleanFlag(502, R.bool.flag_conversations);
@@ -143,9 +141,9 @@ public class Flags {
* @deprecated Not needed anymore
*/
@Deprecated
- public static final BooleanFlag NEW_FOOTER = new BooleanFlag(504, true);
+ public static final ReleasedFlag NEW_FOOTER = new ReleasedFlag(504);
- public static final BooleanFlag NEW_HEADER = new BooleanFlag(505, false, true);
+ public static final UnreleasedFlag NEW_HEADER = new UnreleasedFlag(505, true);
public static final ResourceBooleanFlag FULL_SCREEN_USER_SWITCHER =
new ResourceBooleanFlag(506, R.bool.config_enableFullscreenUserSwitcher);
@@ -154,21 +152,21 @@ public class Flags {
public static final ResourceBooleanFlag STATUS_BAR_USER_SWITCHER =
new ResourceBooleanFlag(602, R.bool.flag_user_switcher_chip);
- public static final BooleanFlag STATUS_BAR_LETTERBOX_APPEARANCE =
- new BooleanFlag(603, false);
+ public static final UnreleasedFlag STATUS_BAR_LETTERBOX_APPEARANCE =
+ new UnreleasedFlag(603, false);
- public static final BooleanFlag NEW_STATUS_BAR_PIPELINE = new BooleanFlag(604, false);
+ public static final UnreleasedFlag NEW_STATUS_BAR_PIPELINE = new UnreleasedFlag(604, true);
/***************************************/
// 700 - dialer/calls
- public static final BooleanFlag ONGOING_CALL_STATUS_BAR_CHIP =
- new BooleanFlag(700, true);
+ public static final ReleasedFlag ONGOING_CALL_STATUS_BAR_CHIP =
+ new ReleasedFlag(700);
- public static final BooleanFlag ONGOING_CALL_IN_IMMERSIVE =
- new BooleanFlag(701, true);
+ public static final ReleasedFlag ONGOING_CALL_IN_IMMERSIVE =
+ new ReleasedFlag(701);
- public static final BooleanFlag ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP =
- new BooleanFlag(702, true);
+ public static final ReleasedFlag ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP =
+ new ReleasedFlag(702);
/***************************************/
// 800 - general visual/theme
@@ -177,15 +175,15 @@ public class Flags {
/***************************************/
// 900 - media
- public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, true);
- public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, false);
- public static final BooleanFlag MEDIA_NEARBY_DEVICES = new BooleanFlag(903, true);
- public static final BooleanFlag MEDIA_MUTE_AWAIT = new BooleanFlag(904, true);
+ public static final ReleasedFlag MEDIA_TAP_TO_TRANSFER = new ReleasedFlag(900);
+ public static final UnreleasedFlag MEDIA_SESSION_ACTIONS = new UnreleasedFlag(901);
+ public static final ReleasedFlag MEDIA_NEARBY_DEVICES = new ReleasedFlag(903);
+ public static final ReleasedFlag MEDIA_MUTE_AWAIT = new ReleasedFlag(904);
// 1000 - dock
- public static final BooleanFlag SIMULATE_DOCK_THROUGH_CHARGING =
- new BooleanFlag(1000, true);
- public static final BooleanFlag DOCK_SETUP_ENABLED = new BooleanFlag(1001, true);
+ public static final ReleasedFlag SIMULATE_DOCK_THROUGH_CHARGING =
+ new ReleasedFlag(1000);
+ public static final ReleasedFlag DOCK_SETUP_ENABLED = new ReleasedFlag(1001);
// 1100 - windowing
@@ -220,12 +218,12 @@ public class Flags {
public static final SysPropBooleanFlag WM_ALWAYS_ENFORCE_PREDICTIVE_BACK =
new SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false);
- public static final BooleanFlag NEW_BACK_AFFORDANCE =
- new BooleanFlag(1203, false /* default */, false /* teamfood */);
+ public static final UnreleasedFlag NEW_BACK_AFFORDANCE =
+ new UnreleasedFlag(1203, false /* teamfood */);
// 1300 - screenshots
- public static final BooleanFlag SCREENSHOT_REQUEST_PROCESSOR = new BooleanFlag(1300, false);
+ public static final UnreleasedFlag SCREENSHOT_REQUEST_PROCESSOR = new UnreleasedFlag(1300);
// Pay no attention to the reflection behind the curtain.
// ========================== Curtain ==========================
diff --git a/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt b/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
new file mode 100644
index 000000000000..fc5b9f4eea05
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.flags
+
+import com.android.systemui.util.DeviceConfigProxy
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+
+interface ServerFlagReader {
+ /** Returns true if there is a server-side setting stored. */
+ fun hasOverride(flagId: Int): Boolean
+
+ /** Returns any stored server-side setting or the default if not set. */
+ fun readServerOverride(flagId: Int, default: Boolean): Boolean
+}
+
+class ServerFlagReaderImpl @Inject constructor(
+ private val deviceConfig: DeviceConfigProxy
+) : ServerFlagReader {
+ override fun hasOverride(flagId: Int): Boolean =
+ deviceConfig.getProperty(
+ SYSUI_NAMESPACE,
+ getServerOverrideName(flagId)
+ ) != null
+
+ override fun readServerOverride(flagId: Int, default: Boolean): Boolean {
+ return deviceConfig.getBoolean(
+ SYSUI_NAMESPACE,
+ getServerOverrideName(flagId),
+ default
+ )
+ }
+
+ private fun getServerOverrideName(flagId: Int): String {
+ return "flag_override_$flagId"
+ }
+}
+
+private val SYSUI_NAMESPACE = "systemui"
+
+@Module
+interface ServerFlagReaderModule {
+ @Binds
+ fun bindsReader(impl: ServerFlagReaderImpl): ServerFlagReader
+}
+
+class ServerFlagReaderFake : ServerFlagReader {
+ private val flagMap: MutableMap<Int, Boolean> = mutableMapOf()
+
+ override fun hasOverride(flagId: Int): Boolean {
+ return flagMap.containsKey(flagId)
+ }
+
+ override fun readServerOverride(flagId: Int, default: Boolean): Boolean {
+ return flagMap.getOrDefault(flagId, default)
+ }
+
+ fun setFlagValue(flagId: Int, value: Boolean) {
+ flagMap.put(flagId, value)
+ }
+
+ fun eraseFlag(flagId: Int) {
+ flagMap.remove(flagId)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlags.kt
index b2a4e33978a1..7b1455cb2e46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -19,7 +19,6 @@ package com.android.systemui.flags
import android.util.SparseArray
import android.util.SparseBooleanArray
import androidx.core.util.containsKey
-import java.lang.IllegalStateException
class FakeFeatureFlags : FeatureFlags {
private val booleanFlags = SparseBooleanArray()
@@ -57,7 +56,10 @@ class FakeFeatureFlags : FeatureFlags {
stringFlags.put(flag.id, value)
}
- override fun isEnabled(flag: BooleanFlag): Boolean = requireBooleanValue(flag.id)
+
+ override fun isEnabled(flag: UnreleasedFlag): Boolean = requireBooleanValue(flag.id)
+
+ override fun isEnabled(flag: ReleasedFlag): Boolean = requireBooleanValue(flag.id)
override fun isEnabled(flag: ResourceBooleanFlag): Boolean = requireBooleanValue(flag.id)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
index 7d4b4f53e380..ff579a1cc4aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
@@ -29,11 +29,12 @@ import org.junit.runner.RunWith
@RunWith(AndroidTestingRunner::class)
class FakeFeatureFlagsTest : SysuiTestCase() {
- private val booleanFlag = BooleanFlag(-1000)
- private val stringFlag = StringFlag(-1001)
- private val resourceBooleanFlag = ResourceBooleanFlag(-1002, resourceId = -1)
- private val resourceStringFlag = ResourceStringFlag(-1003, resourceId = -1)
- private val sysPropBooleanFlag = SysPropBooleanFlag(-1004, name = "test")
+ private val unreleasedFlag = UnreleasedFlag(-1000)
+ private val releasedFlag = ReleasedFlag(-1001)
+ private val stringFlag = StringFlag(-1002)
+ private val resourceBooleanFlag = ResourceBooleanFlag(-1003, resourceId = -1)
+ private val resourceStringFlag = ResourceStringFlag(-1004, resourceId = -1)
+ private val sysPropBooleanFlag = SysPropBooleanFlag(-1005, name = "test")
/**
* FakeFeatureFlags does not honor any default values. All flags which are accessed must be
@@ -49,56 +50,66 @@ class FakeFeatureFlagsTest : SysuiTestCase() {
assertThat(ex.message).contains("TEAMFOOD")
}
try {
- assertThat(flags.isEnabled(booleanFlag)).isFalse()
+ assertThat(flags.isEnabled(unreleasedFlag)).isFalse()
fail("Expected an exception when accessing an unspecified flag.")
} catch (ex: IllegalStateException) {
assertThat(ex.message).contains("UNKNOWN(id=-1000)")
}
try {
+ assertThat(flags.isEnabled(releasedFlag)).isFalse()
+ fail("Expected an exception when accessing an unspecified flag.")
+ } catch (ex: IllegalStateException) {
+ assertThat(ex.message).contains("UNKNOWN(id=-1001)")
+ }
+ try {
assertThat(flags.isEnabled(resourceBooleanFlag)).isFalse()
fail("Expected an exception when accessing an unspecified flag.")
} catch (ex: IllegalStateException) {
- assertThat(ex.message).contains("UNKNOWN(id=-1002)")
+ assertThat(ex.message).contains("UNKNOWN(id=-1003)")
}
try {
assertThat(flags.isEnabled(sysPropBooleanFlag)).isFalse()
fail("Expected an exception when accessing an unspecified flag.")
} catch (ex: IllegalStateException) {
- assertThat(ex.message).contains("UNKNOWN(id=-1004)")
+ assertThat(ex.message).contains("UNKNOWN(id=-1005)")
}
try {
assertThat(flags.getString(stringFlag)).isEmpty()
fail("Expected an exception when accessing an unspecified flag.")
} catch (ex: IllegalStateException) {
- assertThat(ex.message).contains("UNKNOWN(id=-1001)")
+ assertThat(ex.message).contains("UNKNOWN(id=-1002)")
}
try {
assertThat(flags.getString(resourceStringFlag)).isEmpty()
fail("Expected an exception when accessing an unspecified flag.")
} catch (ex: IllegalStateException) {
- assertThat(ex.message).contains("UNKNOWN(id=-1003)")
+ assertThat(ex.message).contains("UNKNOWN(id=-1004)")
}
}
@Test
fun specifiedFlagsReturnCorrectValues() {
val flags = FakeFeatureFlags()
- flags.set(booleanFlag, false)
+ flags.set(unreleasedFlag, false)
+ flags.set(releasedFlag, false)
flags.set(resourceBooleanFlag, false)
flags.set(sysPropBooleanFlag, false)
flags.set(resourceStringFlag, "")
- assertThat(flags.isEnabled(booleanFlag)).isFalse()
+ assertThat(flags.isEnabled(unreleasedFlag)).isFalse()
+ assertThat(flags.isEnabled(releasedFlag)).isFalse()
assertThat(flags.isEnabled(resourceBooleanFlag)).isFalse()
assertThat(flags.isEnabled(sysPropBooleanFlag)).isFalse()
assertThat(flags.getString(resourceStringFlag)).isEmpty()
- flags.set(booleanFlag, true)
+ flags.set(unreleasedFlag, true)
+ flags.set(releasedFlag, true)
flags.set(resourceBooleanFlag, true)
flags.set(sysPropBooleanFlag, true)
flags.set(resourceStringFlag, "Android")
- assertThat(flags.isEnabled(booleanFlag)).isTrue()
+ assertThat(flags.isEnabled(unreleasedFlag)).isTrue()
+ assertThat(flags.isEnabled(releasedFlag)).isTrue()
assertThat(flags.isEnabled(resourceBooleanFlag)).isTrue()
assertThat(flags.isEnabled(sysPropBooleanFlag)).isTrue()
assertThat(flags.getString(resourceStringFlag)).isEqualTo("Android")
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 51f3404a9bca..4511193d41d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
@@ -35,6 +35,10 @@ import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.Serializable
+import java.io.StringWriter
+import java.util.function.Consumer
import org.junit.Assert
import org.junit.Before
import org.junit.Test
@@ -48,12 +52,8 @@ import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.MockitoAnnotations
-import java.io.PrintWriter
-import java.io.Serializable
-import java.io.StringWriter
-import java.util.function.Consumer
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
/**
* NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
@@ -75,10 +75,11 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
private val flagMap = mutableMapOf<Int, Flag<*>>()
private lateinit var broadcastReceiver: BroadcastReceiver
private lateinit var clearCacheAction: Consumer<Int>
+ private val serverFlagReader = ServerFlagReaderFake()
private val deviceConfig = DeviceConfigProxyFake()
- private val teamfoodableFlagA = BooleanFlag(500, false, true)
- private val teamfoodableFlagB = BooleanFlag(501, true, true)
+ private val teamfoodableFlagA = UnreleasedFlag(500, true)
+ private val teamfoodableFlagB = ReleasedFlag(501, true)
@Before
fun setup() {
@@ -93,6 +94,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
resources,
dumpManager,
deviceConfig,
+ serverFlagReader,
flagMap,
commandRegistry,
barService
@@ -109,40 +111,41 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testReadBooleanFlag() {
+ fun readBooleanFlag() {
// Remember that the TEAMFOOD flag is id#1 and has special behavior.
whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq(4), any())).thenReturn(false)
- assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(2, true))).isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(3, false))).isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(4, true))).isFalse()
- assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(5, false))).isFalse()
+
+ assertThat(mFeatureFlagsDebug.isEnabled(ReleasedFlag(2))).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(UnreleasedFlag(3))).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(ReleasedFlag(4))).isFalse()
+ assertThat(mFeatureFlagsDebug.isEnabled(UnreleasedFlag(5))).isFalse()
}
@Test
- fun testTeamFoodFlag_False() {
+ fun teamFoodFlag_False() {
whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(false)
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isFalse()
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue()
// Regular boolean flags should still test the same.
// Only our teamfoodableFlag should change.
- testReadBooleanFlag()
+ readBooleanFlag()
}
@Test
- fun testTeamFoodFlag_True() {
+ fun teamFoodFlag_True() {
whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true)
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isTrue()
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue()
// Regular boolean flags should still test the same.
// Only our teamfoodableFlag should change.
- testReadBooleanFlag()
+ readBooleanFlag()
}
@Test
- fun testTeamFoodFlag_Overridden() {
+ fun teamFoodFlag_Overridden() {
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagA.id), any()))
.thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.id), any()))
@@ -153,11 +156,11 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
// Regular boolean flags should still test the same.
// Only our teamfoodableFlag should change.
- testReadBooleanFlag()
+ readBooleanFlag()
}
@Test
- fun testReadResourceBooleanFlag() {
+ fun readResourceBooleanFlag() {
whenever(resources.getBoolean(1001)).thenReturn(false)
whenever(resources.getBoolean(1002)).thenReturn(true)
whenever(resources.getBoolean(1003)).thenReturn(false)
@@ -182,7 +185,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testReadSysPropBooleanFlag() {
+ fun readSysPropBooleanFlag() {
whenever(systemProperties.getBoolean(anyString(), anyBoolean())).thenAnswer {
if ("b".equals(it.getArgument<String?>(0))) {
return@thenAnswer true
@@ -198,7 +201,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testReadDeviceConfigBooleanFlag() {
+ fun readDeviceConfigBooleanFlag() {
val namespace = "test_namespace"
deviceConfig.setProperty(namespace, "a", "true", false)
deviceConfig.setProperty(namespace, "b", "false", false)
@@ -213,7 +216,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testReadStringFlag() {
+ fun readStringFlag() {
whenever(flagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo")
whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar")
assertThat(mFeatureFlagsDebug.getString(StringFlag(1, "biz"))).isEqualTo("biz")
@@ -223,7 +226,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testReadResourceStringFlag() {
+ fun readResourceStringFlag() {
whenever(resources.getString(1001)).thenReturn("")
whenever(resources.getString(1002)).thenReturn("resource2")
whenever(resources.getString(1003)).thenReturn("resource3")
@@ -253,8 +256,8 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testBroadcastReceiverIgnoresInvalidData() {
- addFlag(BooleanFlag(1, false))
+ fun broadcastReceiver_IgnoresInvalidData() {
+ addFlag(UnreleasedFlag(1))
addFlag(ResourceBooleanFlag(2, 1002))
addFlag(StringFlag(3, "flag3"))
addFlag(ResourceStringFlag(4, 1004))
@@ -272,10 +275,10 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testIntentWithIdButNoValueKeyClears() {
- addFlag(BooleanFlag(1, false))
+ fun intentWithId_NoValueKeyClears() {
+ addFlag(UnreleasedFlag(1))
- // trying to erase an id not in the map does noting
+ // trying to erase an id not in the map does nothing
broadcastReceiver.onReceive(
mockContext,
Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_ID, 0)
@@ -291,9 +294,9 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testSetBooleanFlag() {
- addFlag(BooleanFlag(1, false))
- addFlag(BooleanFlag(2, false))
+ fun setBooleanFlag() {
+ addFlag(UnreleasedFlag(1))
+ addFlag(UnreleasedFlag(2))
addFlag(ResourceBooleanFlag(3, 1003))
addFlag(ResourceBooleanFlag(4, 1004))
@@ -311,7 +314,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testSetStringFlag() {
+ fun setStringFlag() {
addFlag(StringFlag(1, "flag1"))
addFlag(ResourceStringFlag(2, 1002))
@@ -323,7 +326,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testSetFlagClearsCache() {
+ fun setFlag_ClearsCache() {
val flag1 = addFlag(StringFlag(1, "flag1"))
whenever(flagManager.readFlagValue<String>(eq(1), any())).thenReturn("original")
@@ -345,12 +348,30 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testRegisterCommand() {
+ fun serverSide_Overrides_MakesFalse() {
+ val flag = ReleasedFlag(100)
+
+ serverFlagReader.setFlagValue(flag.id, false)
+
+ assertThat(mFeatureFlagsDebug.isEnabled(flag)).isFalse()
+ }
+
+ @Test
+ fun serverSide_Overrides_MakesTrue() {
+ val flag = UnreleasedFlag(100)
+
+ serverFlagReader.setFlagValue(flag.id, true)
+
+ assertThat(mFeatureFlagsDebug.isEnabled(flag)).isTrue()
+ }
+
+ @Test
+ fun statusBarCommand_IsRegistered() {
verify(commandRegistry).registerCommand(anyString(), any())
}
@Test
- fun testNoOpCommand() {
+ fun noOpCommand() {
val cmd = captureCommand()
cmd.execute(pw, ArrayList())
@@ -360,72 +381,42 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testReadFlagCommand() {
- addFlag(BooleanFlag(1, false))
+ fun readFlagCommand() {
+ addFlag(UnreleasedFlag(1))
val cmd = captureCommand()
cmd.execute(pw, listOf("1"))
verify(flagManager).readFlagValue<Boolean>(eq(1), any())
}
@Test
- fun testSetFlagCommand() {
- addFlag(BooleanFlag(1, false))
+ fun setFlagCommand() {
+ addFlag(UnreleasedFlag(1))
val cmd = captureCommand()
cmd.execute(pw, listOf("1", "on"))
verifyPutData(1, "{\"type\":\"boolean\",\"value\":true}")
}
@Test
- fun testToggleFlagCommand() {
- addFlag(BooleanFlag(1, true))
+ fun toggleFlagCommand() {
+ addFlag(ReleasedFlag(1))
val cmd = captureCommand()
cmd.execute(pw, listOf("1", "toggle"))
verifyPutData(1, "{\"type\":\"boolean\",\"value\":false}", 2)
}
@Test
- fun testEraseFlagCommand() {
- addFlag(BooleanFlag(1, true))
+ fun eraseFlagCommand() {
+ addFlag(ReleasedFlag(1))
val cmd = captureCommand()
cmd.execute(pw, listOf("1", "erase"))
verify(secureSettings).putStringForUser(eq("key-1"), eq(""), anyInt())
}
- private fun verifyPutData(id: Int, data: String, numReads: Int = 1) {
- inOrder(flagManager, secureSettings).apply {
- verify(flagManager, times(numReads)).readFlagValue(eq(id), any<FlagSerializer<*>>())
- verify(flagManager).idToSettingsKey(eq(id))
- verify(secureSettings).putStringForUser(eq("key-$id"), eq(data), anyInt())
- verify(flagManager).dispatchListenersAndMaybeRestart(eq(id), any())
- }.verifyNoMoreInteractions()
- verifyNoMoreInteractions(flagManager, secureSettings)
- }
-
- private fun setByBroadcast(id: Int, value: Serializable?) {
- val intent = Intent(FlagManager.ACTION_SET_FLAG)
- intent.putExtra(FlagManager.EXTRA_ID, id)
- intent.putExtra(FlagManager.EXTRA_VALUE, value)
- broadcastReceiver.onReceive(mockContext, intent)
- }
-
- private fun <F : Flag<*>> addFlag(flag: F): F {
- val old = flagMap.put(flag.id, flag)
- check(old == null) { "Flag ${flag.id} already registered" }
- return flag
- }
-
- private fun captureCommand(): Command {
- val captor = argumentCaptor<Function0<Command>>()
- verify(commandRegistry).registerCommand(anyString(), capture(captor))
-
- return captor.value.invoke()
- }
-
@Test
- fun testDump() {
- val flag1 = BooleanFlag(1, true)
+ fun dumpFormat() {
+ val flag1 = ReleasedFlag(1)
val flag2 = ResourceBooleanFlag(2, 1002)
- val flag3 = BooleanFlag(3, false)
+ val flag3 = UnreleasedFlag(3)
val flag4 = StringFlag(4, "")
val flag5 = StringFlag(5, "flag5default")
val flag6 = ResourceStringFlag(6, 1006)
@@ -457,6 +448,36 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
assertThat(dump).contains(" sysui_flag_7: [length=9] \"override7\"\n")
}
+ private fun verifyPutData(id: Int, data: String, numReads: Int = 1) {
+ inOrder(flagManager, secureSettings).apply {
+ verify(flagManager, times(numReads)).readFlagValue(eq(id), any<FlagSerializer<*>>())
+ verify(flagManager).idToSettingsKey(eq(id))
+ verify(secureSettings).putStringForUser(eq("key-$id"), eq(data), anyInt())
+ verify(flagManager).dispatchListenersAndMaybeRestart(eq(id), any())
+ }.verifyNoMoreInteractions()
+ verifyNoMoreInteractions(flagManager, secureSettings)
+ }
+
+ private fun setByBroadcast(id: Int, value: Serializable?) {
+ val intent = Intent(FlagManager.ACTION_SET_FLAG)
+ intent.putExtra(FlagManager.EXTRA_ID, id)
+ intent.putExtra(FlagManager.EXTRA_VALUE, value)
+ broadcastReceiver.onReceive(mockContext, intent)
+ }
+
+ private fun <F : Flag<*>> addFlag(flag: F): F {
+ val old = flagMap.put(flag.id, flag)
+ check(old == null) { "Flag ${flag.id} already registered" }
+ return flag
+ }
+
+ private fun captureCommand(): Command {
+ val captor = argumentCaptor<Function0<Command>>()
+ verify(commandRegistry).registerCommand(anyString(), capture(captor))
+
+ return captor.value.invoke()
+ }
+
private fun dumpToString(): String {
val sw = StringWriter()
val pw = PrintWriter(sw)
@@ -464,4 +485,4 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
pw.flush()
return sw.toString()
}
-} \ No newline at end of file
+}
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 6b683f456ea7..e94b5202956d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
@@ -30,8 +30,8 @@ import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
/**
* NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
@@ -44,13 +44,18 @@ class FeatureFlagsReleaseTest : SysuiTestCase() {
@Mock private lateinit var mResources: Resources
@Mock private lateinit var mSystemProperties: SystemPropertiesHelper
@Mock private lateinit var mDumpManager: DumpManager
+ private val serverFlagReader = ServerFlagReaderFake()
private val deviceConfig = DeviceConfigProxyFake()
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- mFeatureFlagsRelease = FeatureFlagsRelease(mResources, mSystemProperties, deviceConfig,
+ mFeatureFlagsRelease = FeatureFlagsRelease(
+ mResources,
+ mSystemProperties,
+ deviceConfig,
+ serverFlagReader,
mDumpManager)
}
@@ -113,4 +118,22 @@ class FeatureFlagsReleaseTest : SysuiTestCase() {
whenever(mSystemProperties.getBoolean(flagName, flagDefault)).thenReturn(flagDefault)
assertThat(mFeatureFlagsRelease.isEnabled(flag)).isEqualTo(flagDefault)
}
-} \ No newline at end of file
+
+ @Test
+ fun serverSide_OverridesReleased_MakesFalse() {
+ val flag = ReleasedFlag(100)
+
+ serverFlagReader.setFlagValue(flag.id, false)
+
+ assertThat(mFeatureFlagsRelease.isEnabled(flag)).isFalse()
+ }
+
+ @Test
+ fun serverSide_OverridesUnreleased_Ignored() {
+ val flag = UnreleasedFlag(100)
+
+ serverFlagReader.setFlagValue(flag.id, true)
+
+ assertThat(mFeatureFlagsRelease.isEnabled(flag)).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
index a2eca81b04ed..17324a01502b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
@@ -26,6 +26,7 @@ import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
+import java.util.function.Consumer
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
@@ -33,9 +34,8 @@ import org.mockito.Mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.MockitoAnnotations
-import java.util.function.Consumer
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
/**
* NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
@@ -64,14 +64,14 @@ class FlagManagerTest : SysuiTestCase() {
verifyNoMoreInteractions(mFlagSettingsHelper)
// adding the first listener registers the observer
- mFlagManager.addListener(BooleanFlag(1, true), listener1)
+ mFlagManager.addListener(ReleasedFlag(1), listener1)
val observer = withArgCaptor<ContentObserver> {
verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
}
verifyNoMoreInteractions(mFlagSettingsHelper)
// adding another listener does nothing
- mFlagManager.addListener(BooleanFlag(2, true), listener2)
+ mFlagManager.addListener(ReleasedFlag(2), listener2)
verifyNoMoreInteractions(mFlagSettingsHelper)
// removing the original listener does nothing with second one still present
@@ -89,7 +89,7 @@ class FlagManagerTest : SysuiTestCase() {
val listener = mock<FlagListenable.Listener>()
val clearCacheAction = mock<Consumer<Int>>()
mFlagManager.clearCacheAction = clearCacheAction
- mFlagManager.addListener(BooleanFlag(1, true), listener)
+ mFlagManager.addListener(ReleasedFlag(1), listener)
val observer = withArgCaptor<ContentObserver> {
verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
}
@@ -101,8 +101,8 @@ class FlagManagerTest : SysuiTestCase() {
fun testObserverInvokesListeners() {
val listener1 = mock<FlagListenable.Listener>()
val listener10 = mock<FlagListenable.Listener>()
- mFlagManager.addListener(BooleanFlag(1, true), listener1)
- mFlagManager.addListener(BooleanFlag(10, true), listener10)
+ mFlagManager.addListener(ReleasedFlag(1), listener1)
+ mFlagManager.addListener(ReleasedFlag(10), listener10)
val observer = withArgCaptor<ContentObserver> {
verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
}
@@ -127,8 +127,8 @@ class FlagManagerTest : SysuiTestCase() {
fun testOnlySpecificFlagListenerIsInvoked() {
val listener1 = mock<FlagListenable.Listener>()
val listener10 = mock<FlagListenable.Listener>()
- mFlagManager.addListener(BooleanFlag(1, true), listener1)
- mFlagManager.addListener(BooleanFlag(10, true), listener10)
+ mFlagManager.addListener(ReleasedFlag(1), listener1)
+ mFlagManager.addListener(ReleasedFlag(10), listener10)
mFlagManager.dispatchListenersAndMaybeRestart(1, null)
val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
@@ -148,8 +148,8 @@ class FlagManagerTest : SysuiTestCase() {
@Test
fun testSameListenerCanBeUsedForMultipleFlags() {
val listener = mock<FlagListenable.Listener>()
- mFlagManager.addListener(BooleanFlag(1, true), listener)
- mFlagManager.addListener(BooleanFlag(10, true), listener)
+ mFlagManager.addListener(ReleasedFlag(1), listener)
+ mFlagManager.addListener(ReleasedFlag(10), listener)
mFlagManager.dispatchListenersAndMaybeRestart(1, null)
val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
@@ -177,7 +177,7 @@ class FlagManagerTest : SysuiTestCase() {
@Test
fun testListenerCanSuppressRestart() {
val restartAction = mock<Consumer<Boolean>>()
- mFlagManager.addListener(BooleanFlag(1, true)) { event ->
+ mFlagManager.addListener(ReleasedFlag(1)) { event ->
event.requestNoRestart()
}
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
@@ -188,7 +188,7 @@ class FlagManagerTest : SysuiTestCase() {
@Test
fun testListenerOnlySuppressesRestartForOwnFlag() {
val restartAction = mock<Consumer<Boolean>>()
- mFlagManager.addListener(BooleanFlag(10, true)) { event ->
+ mFlagManager.addListener(ReleasedFlag(10)) { event ->
event.requestNoRestart()
}
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
@@ -199,10 +199,10 @@ class FlagManagerTest : SysuiTestCase() {
@Test
fun testRestartWhenNotAllListenersRequestSuppress() {
val restartAction = mock<Consumer<Boolean>>()
- mFlagManager.addListener(BooleanFlag(10, true)) { event ->
+ mFlagManager.addListener(ReleasedFlag(10)) { event ->
event.requestNoRestart()
}
- mFlagManager.addListener(BooleanFlag(10, true)) {
+ mFlagManager.addListener(ReleasedFlag(10)) {
// do not request
}
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
@@ -295,4 +295,4 @@ class FlagManagerTest : SysuiTestCase() {
assertThat(StringFlagSerializer.toSettingsData("foo"))
.isEqualTo("{\"type\":\"string\",\"value\":\"foo\"}")
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
index 25c302885e07..250cc48fece3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
@@ -107,11 +107,11 @@ public class FlagsTest extends SysuiTestCase {
}
private static class DuplicateFlagContainer {
- public static final BooleanFlag A_FLAG = new BooleanFlag(0);
- public static final BooleanFlag B_FLAG = new BooleanFlag(0);
+ public static final BooleanFlag A_FLAG = new UnreleasedFlag(0);
+ public static final BooleanFlag B_FLAG = new UnreleasedFlag(0);
public static final StringFlag C_FLAG = new StringFlag(0);
- public static final BooleanFlag D_FLAG = new BooleanFlag(1);
+ public static final BooleanFlag D_FLAG = new UnreleasedFlag(1);
public static final DoubleFlag E_FLAG = new DoubleFlag(3);
public static final DoubleFlag F_FLAG = new DoubleFlag(3);