summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java217
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt221
3 files changed, 363 insertions, 106 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
index aaa4d9eefd29..c4531b573d22 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
@@ -42,10 +42,14 @@ 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.statusbar.commandline.Command;
+import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.util.settings.SecureSettings;
import java.io.PrintWriter;
+import java.lang.reflect.Field;
import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
@@ -59,6 +63,10 @@ import javax.inject.Named;
*
* Flags can be set (or unset) via the following adb command:
*
+ * adb shell cmd statusbar flag <id> <on|off|toggle|erase>
+ *
+ * Alternatively, you can change flags via a broadcast intent:
+ *
* adb shell am broadcast -a com.android.systemui.action.SET_FLAG --ei id <id> [--ez value <0|1>]
*
* To restore a flag back to its default, leave the `--ez value <0|1>` off of the command.
@@ -67,6 +75,7 @@ import javax.inject.Named;
public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
private static final String TAG = "SysUIFlags";
static final String ALL_FLAGS = "all_flags";
+ private static final String FLAG_COMMAND = "flag";
private final FlagManager mFlagManager;
private final SecureSettings mSecureSettings;
@@ -86,12 +95,15 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
@Main Resources resources,
DumpManager dumpManager,
@Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags,
+ CommandRegistry commandRegistry,
IStatusBarService barService) {
mFlagManager = flagManager;
mSecureSettings = secureSettings;
mResources = resources;
mSystemProperties = systemProperties;
mAllFlags = allFlags;
+ mBarService = barService;
+
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_SET_FLAG);
filter.addAction(ACTION_GET_FLAGS);
@@ -100,7 +112,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
context.registerReceiver(mReceiver, filter, null, null,
Context.RECEIVER_EXPORTED_UNAUDITED);
dumpManager.registerDumpable(TAG, this);
- mBarService = barService;
+ commandRegistry.registerCommand(FLAG_COMMAND, FlagCommand::new);
}
@Override
@@ -276,6 +288,31 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
}
}
+ private void setBooleanFlagInternal(Flag<?> flag, boolean value) {
+ if (flag instanceof BooleanFlag) {
+ setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
+ } else if (flag instanceof ResourceBooleanFlag) {
+ 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);
+ dispatchListenersAndMaybeRestart(flag.getId(),
+ FeatureFlagsDebug.this::restartAndroid);
+ } else {
+ throw new IllegalArgumentException("Unknown flag type");
+ }
+ }
+
+ private void setStringFlagInternal(Flag<?> flag, String value) {
+ if (flag instanceof StringFlag) {
+ setFlagValue(flag.getId(), value, StringFlagSerializer.INSTANCE);
+ } else if (flag instanceof ResourceStringFlag) {
+ setFlagValue(flag.getId(), value, StringFlagSerializer.INSTANCE);
+ } else {
+ throw new IllegalArgumentException("Unknown flag type");
+ }
+ }
+
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -327,24 +364,19 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
}
Object value = extras.get(EXTRA_VALUE);
- if (flag instanceof BooleanFlag && value instanceof Boolean) {
- setFlagValue(id, (Boolean) value, BooleanFlagSerializer.INSTANCE);
- } else if (flag instanceof ResourceBooleanFlag && value instanceof Boolean) {
- setFlagValue(id, (Boolean) value, BooleanFlagSerializer.INSTANCE);
- } else if (flag instanceof SysPropBooleanFlag && value instanceof Boolean) {
- // Store SysProp flags in SystemProperties where they can read by outside parties.
- mSystemProperties.setBoolean(
- ((SysPropBooleanFlag) flag).getName(), (Boolean) value);
- dispatchListenersAndMaybeRestart(flag.getId(),
- FeatureFlagsDebug.this::restartAndroid);
- } else if (flag instanceof StringFlag && value instanceof String) {
- setFlagValue(id, (String) value, StringFlagSerializer.INSTANCE);
- } else if (flag instanceof ResourceStringFlag && value instanceof String) {
- setFlagValue(id, (String) value, StringFlagSerializer.INSTANCE);
- } else {
+
+ try {
+ if (value instanceof Boolean) {
+ setBooleanFlagInternal(flag, (Boolean) value);
+ } else if (value instanceof String) {
+ setStringFlagInternal(flag, (String) value);
+ } else {
+ throw new IllegalArgumentException("Unknown value type");
+ }
+ } catch (IllegalArgumentException e) {
Log.w(TAG,
- "Unable to set " + id + " of type " + flag.getClass() + " to value of type "
- + (value == null ? null : value.getClass()));
+ "Unable to set " + flag.getId() + " of type " + flag.getClass()
+ + " to value of type " + (value == null ? null : value.getClass()));
}
}
@@ -388,4 +420,153 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
mStringFlagCache.forEach((key, value) -> pw.println(" sysui_flag_" + key
+ ": [length=" + value.length() + "] \"" + value + "\""));
}
+
+ class FlagCommand implements Command {
+ private final List<String> mOnCommands = List.of("true", "on", "1", "enabled");
+ private final List<String> mOffCommands = List.of("false", "off", "0", "disable");
+
+ @Override
+ public void execute(@NonNull PrintWriter pw, @NonNull List<String> args) {
+ if (args.size() == 0) {
+ pw.println("Error: no flag id supplied");
+ help(pw);
+ pw.println();
+ printKnownFlags(pw);
+ return;
+ }
+
+ if (args.size() > 2) {
+ pw.println("Invalid number of arguments.");
+ help(pw);
+ return;
+ }
+
+ int id = 0;
+ try {
+ id = Integer.parseInt(args.get(0));
+ if (!mAllFlags.containsKey(id)) {
+ pw.println("Unknown flag id: " + id);
+ pw.println();
+ printKnownFlags(pw);
+ return;
+ }
+ } catch (NumberFormatException e) {
+ id = flagNameToId(args.get(0));
+ if (id == 0) {
+ pw.println("Invalid flag. Must an integer id or flag name: " + args.get(0));
+ return;
+ }
+ }
+ Flag<?> flag = mAllFlags.get(id);
+
+ String cmd = "";
+ if (args.size() == 2) {
+ cmd = args.get(1).toLowerCase();
+ }
+
+ if ("erase".equals(cmd) || "reset".equals(cmd)) {
+ eraseFlag(flag);
+ return;
+ }
+
+ boolean newValue = true;
+ if (args.size() == 1 || "toggle".equals(cmd)) {
+ boolean enabled = isBooleanFlagEnabled(flag);
+
+ if (args.size() == 1) {
+ pw.println("Flag " + id + " is " + enabled);
+ return;
+ }
+
+ newValue = !enabled;
+ } else {
+ newValue = mOnCommands.contains(cmd);
+ if (!newValue && !mOffCommands.contains(cmd)) {
+ pw.println("Invalid on/off argument supplied");
+ help(pw);
+ return;
+ }
+ }
+
+ pw.flush(); // Next command will restart sysui, so flush before we do so.
+ setBooleanFlagInternal(flag, newValue);
+ }
+
+ @Override
+ public void help(PrintWriter pw) {
+ pw.println(
+ "Usage: adb shell cmd statusbar flag <id> "
+ + "[true|false|1|0|on|off|enable|disable|toggle|erase|reset]");
+ pw.println("The id can either be a numeric integer or the corresponding field name");
+ pw.println(
+ "If no argument is supplied after the id, the flags runtime value is output");
+ }
+
+ private boolean isBooleanFlagEnabled(Flag<?> flag) {
+ if (flag instanceof BooleanFlag) {
+ return isEnabled((BooleanFlag) flag);
+ } else if (flag instanceof ResourceBooleanFlag) {
+ return isEnabled((ResourceBooleanFlag) flag);
+ } else if (flag instanceof SysPropFlag) {
+ return isEnabled((SysPropBooleanFlag) flag);
+ }
+
+ return false;
+ }
+
+ private int flagNameToId(String flagName) {
+ List<Field> fields = Flags.getFlagFields();
+ for (Field field : fields) {
+ if (flagName.equals(field.getName())) {
+ return fieldToId(field);
+ }
+ }
+
+ return 0;
+ }
+
+ private int fieldToId(Field field) {
+ try {
+ Flag<?> flag = (Flag<?>) field.get(null);
+ return flag.getId();
+ } catch (IllegalAccessException e) {
+ // no-op
+ }
+
+ return 0;
+ }
+
+ private void printKnownFlags(PrintWriter pw) {
+ List<Field> fields = Flags.getFlagFields();
+
+ int longestFieldName = 0;
+ for (Field field : fields) {
+ longestFieldName = Math.max(longestFieldName, field.getName().length());
+ }
+
+ pw.println("Known Flags:");
+ pw.print("Flag Name");
+ for (int i = 0; i < longestFieldName - "Flag Name".length() + 1; i++) {
+ pw.print(" ");
+ }
+ pw.println("ID Enabled?");
+ for (int i = 0; i < longestFieldName; i++) {
+ pw.print("=");
+ }
+ pw.println(" ==== ========");
+ for (Field field : fields) {
+ int id = fieldToId(field);
+ if (id == 0 || !mAllFlags.containsKey(id)) {
+ continue;
+ }
+ pw.print(field.getName());
+ int fieldWidth = field.getName().length();
+ for (int i = 0; i < longestFieldName - fieldWidth + 1; i++) {
+ pw.print(" ");
+ }
+ pw.printf("%-4d ", id);
+ pw.println(isBooleanFlagEnabled(mAllFlags.get(id)));
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 44580aa4230a..afa7d5e0a9c4 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -20,7 +20,9 @@ import com.android.internal.annotations.Keep;
import com.android.systemui.R;
import java.lang.reflect.Field;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -182,25 +184,36 @@ public class Flags {
if (sFlagMap != null) {
return sFlagMap;
}
+
Map<Integer, Flag<?>> flags = new HashMap<>();
+ List<Field> flagFields = getFlagFields();
+
+ for (Field field : flagFields) {
+ try {
+ Flag<?> flag = (Flag<?>) field.get(null);
+ flags.put(flag.getId(), flag);
+ } catch (IllegalAccessException e) {
+ // no-op
+ }
+ }
+ sFlagMap = flags;
+
+ return sFlagMap;
+ }
+
+ static List<Field> getFlagFields() {
Field[] fields = Flags.class.getFields();
+ List<Field> result = new ArrayList<>();
for (Field field : fields) {
Class<?> t = field.getType();
if (Flag.class.isAssignableFrom(t)) {
- try {
- Flag<?> flag = (Flag<?>) field.get(null);
- flags.put(flag.getId(), flag);
- } catch (IllegalAccessException e) {
- // no-op
- }
+ result.add(field);
}
}
- sFlagMap = flags;
-
- return sFlagMap;
+ return result;
}
// | . . . . . . . . . . . . . . . . . . . |
// | |
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 6626bbe69706..b43856aae4cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
@@ -24,7 +24,11 @@ import android.test.suitebuilder.annotation.SmallTest
import com.android.internal.statusbar.IStatusBarService
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.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.withArgCaptor
@@ -37,10 +41,12 @@ import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyString
+import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.inOrder
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
@@ -56,16 +62,18 @@ import org.mockito.Mockito.`when` as whenever
class FeatureFlagsDebugTest : SysuiTestCase() {
private lateinit var mFeatureFlagsDebug: FeatureFlagsDebug
- @Mock private lateinit var mFlagManager: FlagManager
- @Mock private lateinit var mMockContext: Context
- @Mock private lateinit var mSecureSettings: SecureSettings
- @Mock private lateinit var mSystemProperties: SystemPropertiesHelper
- @Mock private lateinit var mResources: Resources
- @Mock private lateinit var mDumpManager: DumpManager
- @Mock private lateinit var mBarService: IStatusBarService
- private val mFlagMap = mutableMapOf<Int, Flag<*>>()
- private lateinit var mBroadcastReceiver: BroadcastReceiver
- private lateinit var mClearCacheAction: Consumer<Int>
+ @Mock private lateinit var flagManager: FlagManager
+ @Mock private lateinit var mockContext: Context
+ @Mock private lateinit var secureSettings: SecureSettings
+ @Mock private lateinit var systemProperties: SystemPropertiesHelper
+ @Mock private lateinit var resources: Resources
+ @Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var commandRegistry: CommandRegistry
+ @Mock private lateinit var barService: IStatusBarService
+ @Mock private lateinit var pw: PrintWriter
+ private val flagMap = mutableMapOf<Int, Flag<*>>()
+ private lateinit var broadcastReceiver: BroadcastReceiver
+ private lateinit var clearCacheAction: Consumer<Int>
private val teamfoodableFlagA = BooleanFlag(500, false, true)
private val teamfoodableFlagB = BooleanFlag(501, true, true)
@@ -73,34 +81,35 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- mFlagMap.put(teamfoodableFlagA.id, teamfoodableFlagA)
- mFlagMap.put(teamfoodableFlagB.id, teamfoodableFlagB)
+ flagMap.put(teamfoodableFlagA.id, teamfoodableFlagA)
+ flagMap.put(teamfoodableFlagB.id, teamfoodableFlagB)
mFeatureFlagsDebug = FeatureFlagsDebug(
- mFlagManager,
- mMockContext,
- mSecureSettings,
- mSystemProperties,
- mResources,
- mDumpManager,
- mFlagMap,
- mBarService
+ flagManager,
+ mockContext,
+ secureSettings,
+ systemProperties,
+ resources,
+ dumpManager,
+ flagMap,
+ commandRegistry,
+ barService
)
- verify(mFlagManager).onSettingsChangedAction = any()
- mBroadcastReceiver = withArgCaptor {
- verify(mMockContext).registerReceiver(capture(), any(), nullable(), nullable(),
+ verify(flagManager).onSettingsChangedAction = any()
+ broadcastReceiver = withArgCaptor {
+ verify(mockContext).registerReceiver(capture(), any(), nullable(), nullable(),
any())
}
- mClearCacheAction = withArgCaptor {
- verify(mFlagManager).clearCacheAction = capture()
+ clearCacheAction = withArgCaptor {
+ verify(flagManager).clearCacheAction = capture()
}
- whenever(mFlagManager.idToSettingsKey(any())).thenAnswer { "key-${it.arguments[0]}" }
+ whenever(flagManager.idToSettingsKey(any())).thenAnswer { "key-${it.arguments[0]}" }
}
@Test
fun testReadBooleanFlag() {
// Remember that the TEAMFOOD flag is id#1 and has special behavior.
- whenever(mFlagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
- whenever(mFlagManager.readFlagValue<Boolean>(eq(4), any())).thenReturn(false)
+ 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()
@@ -109,7 +118,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test
fun testTeamFoodFlag_False() {
- whenever(mFlagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(false)
+ whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(false)
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isFalse()
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue()
@@ -120,7 +129,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test
fun testTeamFoodFlag_True() {
- whenever(mFlagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true)
+ whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true)
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isTrue()
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue()
@@ -131,11 +140,11 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test
fun testTeamFoodFlag_Overridden() {
- whenever(mFlagManager.readFlagValue<Boolean>(eq(teamfoodableFlagA.id), any()))
+ whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagA.id), any()))
.thenReturn(true)
- whenever(mFlagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.id), any()))
+ whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.id), any()))
.thenReturn(false)
- whenever(mFlagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true)
+ whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true)
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isTrue()
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isFalse()
@@ -146,14 +155,14 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test
fun testReadResourceBooleanFlag() {
- whenever(mResources.getBoolean(1001)).thenReturn(false)
- whenever(mResources.getBoolean(1002)).thenReturn(true)
- whenever(mResources.getBoolean(1003)).thenReturn(false)
- whenever(mResources.getBoolean(1004)).thenAnswer { throw NameNotFoundException() }
- whenever(mResources.getBoolean(1005)).thenAnswer { throw NameNotFoundException() }
+ whenever(resources.getBoolean(1001)).thenReturn(false)
+ whenever(resources.getBoolean(1002)).thenReturn(true)
+ whenever(resources.getBoolean(1003)).thenReturn(false)
+ whenever(resources.getBoolean(1004)).thenAnswer { throw NameNotFoundException() }
+ whenever(resources.getBoolean(1005)).thenAnswer { throw NameNotFoundException() }
- whenever(mFlagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
- whenever(mFlagManager.readFlagValue<Boolean>(eq(5), any())).thenReturn(false)
+ whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
+ whenever(flagManager.readFlagValue<Boolean>(eq(5), any())).thenReturn(false)
assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(1, 1001))).isFalse()
assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(2, 1002))).isTrue()
@@ -171,7 +180,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test
fun testReadSysPropBooleanFlag() {
- whenever(mSystemProperties.getBoolean(anyString(), anyBoolean())).thenAnswer {
+ whenever(systemProperties.getBoolean(anyString(), anyBoolean())).thenAnswer {
if ("b".equals(it.getArgument<String?>(0))) {
return@thenAnswer true
}
@@ -187,8 +196,8 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test
fun testReadStringFlag() {
- whenever(mFlagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo")
- whenever(mFlagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar")
+ 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")
assertThat(mFeatureFlagsDebug.getString(StringFlag(2, "baz"))).isEqualTo("baz")
assertThat(mFeatureFlagsDebug.getString(StringFlag(3, "buz"))).isEqualTo("foo")
@@ -197,16 +206,16 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test
fun testReadResourceStringFlag() {
- whenever(mResources.getString(1001)).thenReturn("")
- whenever(mResources.getString(1002)).thenReturn("resource2")
- whenever(mResources.getString(1003)).thenReturn("resource3")
- whenever(mResources.getString(1004)).thenReturn(null)
- whenever(mResources.getString(1005)).thenAnswer { throw NameNotFoundException() }
- whenever(mResources.getString(1006)).thenAnswer { throw NameNotFoundException() }
+ whenever(resources.getString(1001)).thenReturn("")
+ whenever(resources.getString(1002)).thenReturn("resource2")
+ whenever(resources.getString(1003)).thenReturn("resource3")
+ whenever(resources.getString(1004)).thenReturn(null)
+ whenever(resources.getString(1005)).thenAnswer { throw NameNotFoundException() }
+ whenever(resources.getString(1006)).thenAnswer { throw NameNotFoundException() }
- whenever(mFlagManager.readFlagValue<String>(eq(3), any())).thenReturn("override3")
- whenever(mFlagManager.readFlagValue<String>(eq(4), any())).thenReturn("override4")
- whenever(mFlagManager.readFlagValue<String>(eq(6), any())).thenReturn("override6")
+ whenever(flagManager.readFlagValue<String>(eq(3), any())).thenReturn("override3")
+ whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("override4")
+ whenever(flagManager.readFlagValue<String>(eq(6), any())).thenReturn("override6")
assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(1, 1001))).isEqualTo("")
assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(2, 1002))).isEqualTo("resource2")
@@ -232,16 +241,16 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
addFlag(StringFlag(3, "flag3"))
addFlag(ResourceStringFlag(4, 1004))
- mBroadcastReceiver.onReceive(mMockContext, null)
- mBroadcastReceiver.onReceive(mMockContext, Intent())
- mBroadcastReceiver.onReceive(mMockContext, Intent("invalid action"))
- mBroadcastReceiver.onReceive(mMockContext, Intent(FlagManager.ACTION_SET_FLAG))
+ broadcastReceiver.onReceive(mockContext, null)
+ broadcastReceiver.onReceive(mockContext, Intent())
+ broadcastReceiver.onReceive(mockContext, Intent("invalid action"))
+ broadcastReceiver.onReceive(mockContext, Intent(FlagManager.ACTION_SET_FLAG))
setByBroadcast(0, false) // unknown id does nothing
setByBroadcast(1, "string") // wrong type does nothing
setByBroadcast(2, 123) // wrong type does nothing
setByBroadcast(3, false) // wrong type does nothing
setByBroadcast(4, 123) // wrong type does nothing
- verifyNoMoreInteractions(mFlagManager, mSecureSettings)
+ verifyNoMoreInteractions(flagManager, secureSettings)
}
@Test
@@ -249,15 +258,15 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
addFlag(BooleanFlag(1, false))
// trying to erase an id not in the map does noting
- mBroadcastReceiver.onReceive(
- mMockContext,
+ broadcastReceiver.onReceive(
+ mockContext,
Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_ID, 0)
)
- verifyNoMoreInteractions(mFlagManager, mSecureSettings)
+ verifyNoMoreInteractions(flagManager, secureSettings)
// valid id with no value puts empty string in the setting
- mBroadcastReceiver.onReceive(
- mMockContext,
+ broadcastReceiver.onReceive(
+ mockContext,
Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_ID, 1)
)
verifyPutData(1, "", numReads = 0)
@@ -298,48 +307,102 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test
fun testSetFlagClearsCache() {
val flag1 = addFlag(StringFlag(1, "flag1"))
- whenever(mFlagManager.readFlagValue<String>(eq(1), any())).thenReturn("original")
+ whenever(flagManager.readFlagValue<String>(eq(1), any())).thenReturn("original")
// gets the flag & cache it
assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("original")
- verify(mFlagManager).readFlagValue(eq(1), eq(StringFlagSerializer))
+ verify(flagManager).readFlagValue(eq(1), eq(StringFlagSerializer))
// hit the cache
assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("original")
- verifyNoMoreInteractions(mFlagManager)
+ verifyNoMoreInteractions(flagManager)
// set the flag
setByBroadcast(1, "new")
verifyPutData(1, "{\"type\":\"string\",\"value\":\"new\"}", numReads = 2)
- whenever(mFlagManager.readFlagValue<String>(eq(1), any())).thenReturn("new")
+ whenever(flagManager.readFlagValue<String>(eq(1), any())).thenReturn("new")
assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("new")
- verify(mFlagManager, times(3)).readFlagValue(eq(1), eq(StringFlagSerializer))
+ verify(flagManager, times(3)).readFlagValue(eq(1), eq(StringFlagSerializer))
+ }
+
+ @Test
+ fun testRegisterCommand() {
+ verify(commandRegistry).registerCommand(anyString(), any())
+ }
+
+ @Test
+ fun testNoOpCommand() {
+ val cmd = captureCommand()
+
+ cmd.execute(pw, ArrayList())
+ verify(pw, atLeastOnce()).println()
+ verify(flagManager).readFlagValue<Boolean>(eq(1), any())
+ verifyZeroInteractions(secureSettings)
+ }
+
+ @Test
+ fun testReadFlagCommand() {
+ addFlag(BooleanFlag(1, false))
+ val cmd = captureCommand()
+ cmd.execute(pw, listOf("1"))
+ verify(flagManager).readFlagValue<Boolean>(eq(1), any())
+ }
+
+ @Test
+ fun testSetFlagCommand() {
+ addFlag(BooleanFlag(1, false))
+ val cmd = captureCommand()
+ cmd.execute(pw, listOf("1", "on"))
+ verifyPutData(1, "{\"type\":\"boolean\",\"value\":true}")
+ }
+
+ @Test
+ fun testToggleFlagCommand() {
+ addFlag(BooleanFlag(1, true))
+ val cmd = captureCommand()
+ cmd.execute(pw, listOf("1", "toggle"))
+ verifyPutData(1, "{\"type\":\"boolean\",\"value\":false}", 2)
+ }
+
+ @Test
+ fun testEraseFlagCommand() {
+ addFlag(BooleanFlag(1, true))
+ 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(mFlagManager, mSecureSettings).apply {
- verify(mFlagManager, times(numReads)).readFlagValue(eq(id), any<FlagSerializer<*>>())
- verify(mFlagManager).idToSettingsKey(eq(id))
- verify(mSecureSettings).putStringForUser(eq("key-$id"), eq(data), anyInt())
- verify(mFlagManager).dispatchListenersAndMaybeRestart(eq(id), any())
+ 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(mFlagManager, mSecureSettings)
+ 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)
- mBroadcastReceiver.onReceive(mMockContext, intent)
+ broadcastReceiver.onReceive(mockContext, intent)
}
private fun <F : Flag<*>> addFlag(flag: F): F {
- val old = mFlagMap.put(flag.id, flag)
+ 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)
@@ -350,10 +413,10 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
val flag6 = ResourceStringFlag(6, 1006)
val flag7 = ResourceStringFlag(7, 1007)
- whenever(mResources.getBoolean(1002)).thenReturn(true)
- whenever(mResources.getString(1006)).thenReturn("resource1006")
- whenever(mResources.getString(1007)).thenReturn("resource1007")
- whenever(mFlagManager.readFlagValue(eq(7), eq(StringFlagSerializer)))
+ whenever(resources.getBoolean(1002)).thenReturn(true)
+ whenever(resources.getString(1006)).thenReturn("resource1006")
+ whenever(resources.getString(1007)).thenReturn("resource1007")
+ whenever(flagManager.readFlagValue(eq(7), eq(StringFlagSerializer)))
.thenReturn("override7")
// WHEN the flags have been accessed