summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/flags/Flag.kt52
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/flags/Flags.java23
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/FlagReaderPlugin.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java79
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java119
7 files changed, 382 insertions, 1 deletions
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/plugin/src/com/android/systemui/flags/Flag.kt
new file mode 100644
index 000000000000..68834bc2aa23
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/flags/Flag.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 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
+
+interface Flag<T> {
+ val id: Int
+ val default: T
+}
+
+data class BooleanFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val default: Boolean = false
+) : Flag<Boolean>
+
+data class StringFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val default: String = ""
+) : Flag<String>
+
+data class IntFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val default: Int = 0
+) : Flag<Int>
+
+data class LongFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val default: Long = 0
+) : Flag<Long>
+
+data class FloatFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val default: Float = 0f
+) : Flag<Float>
+
+data class DoubleFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val default: Double = 0.0
+) : Flag<Double> \ No newline at end of file
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/plugin/src/com/android/systemui/flags/Flags.java
new file mode 100644
index 000000000000..d5b9243ebe86
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/flags/Flags.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 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;
+
+/**
+ * List of {@link Flag} objects for use in SystemUI.
+ */
+public class Flags {
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FlagReaderPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FlagReaderPlugin.java
new file mode 100644
index 000000000000..d153bd8eac09
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FlagReaderPlugin.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 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.plugins;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+
+/**
+ * Plugin for loading flag values from an alternate source of truth.
+ */
+@ProvidesInterface(action = FlagReaderPlugin.ACTION, version = FlagReaderPlugin.VERSION)
+public interface FlagReaderPlugin extends Plugin {
+ int VERSION = 1;
+ String ACTION = "com.android.systemui.flags.FLAG_READER_PLUGIN";
+
+ /** Returns a boolean value for the given flag. */
+ default boolean isEnabled(int id, boolean def) {
+ return def;
+ }
+
+ /** Returns a string value for the given flag id. */
+ default String getValue(int id, String def) {
+ return def;
+ }
+
+ /** Returns a int value for the given flag. */
+ default int getValue(int id, int def) {
+ return def;
+ }
+
+ /** Returns a long value for the given flag. */
+ default long getValue(int id, long def) {
+ return def;
+ }
+
+ /** Returns a float value for the given flag. */
+ default float getValue(int id, float def) {
+ return def;
+ }
+
+ /** Returns a double value for the given flag. */
+ default double getValue(int id, double def) {
+ return def;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java
index 1fec7a669cc8..08247a824ffb 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java
@@ -16,6 +16,7 @@
package com.android.systemui.flags;
+import android.content.Context;
import android.content.res.Resources;
import android.util.SparseArray;
@@ -25,6 +26,9 @@ import androidx.annotation.Nullable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.FlagReaderPlugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.wrapper.BuildInfo;
import javax.inject.Inject;
@@ -54,18 +58,60 @@ import javax.inject.Inject;
public class FeatureFlagReader {
private final Resources mResources;
private final boolean mAreFlagsOverrideable;
+ private final PluginManager mPluginManager;
private final SystemPropertiesHelper mSystemPropertiesHelper;
private final SparseArray<CachedFlag> mCachedFlags = new SparseArray<>();
+ private FlagReaderPlugin mPlugin = new FlagReaderPlugin(){};
+
@Inject
public FeatureFlagReader(
@Main Resources resources,
BuildInfo build,
+ PluginManager pluginManager,
SystemPropertiesHelper systemPropertiesHelper) {
mResources = resources;
+ mPluginManager = pluginManager;
mSystemPropertiesHelper = systemPropertiesHelper;
mAreFlagsOverrideable =
build.isDebuggable() && mResources.getBoolean(R.bool.are_flags_overrideable);
+
+ mPluginManager.addPluginListener(mPluginListener, FlagReaderPlugin.class);
+ }
+
+ private final PluginListener<FlagReaderPlugin> mPluginListener =
+ new PluginListener<FlagReaderPlugin>() {
+ public void onPluginConnected(FlagReaderPlugin plugin, Context context) {
+ mPlugin = plugin;
+ }
+
+ public void onPluginDisconnected(FlagReaderPlugin plugin) {
+ mPlugin = new FlagReaderPlugin() {};
+ }
+ };
+
+ boolean isEnabled(BooleanFlag flag) {
+ return mPlugin.isEnabled(flag.getId(), flag.getDefault());
+ }
+
+ String getValue(StringFlag flag) {
+ return mPlugin.getValue(flag.getId(), flag.getDefault());
+ }
+
+ int getValue(IntFlag flag) {
+ return mPlugin.getValue(flag.getId(), flag.getDefault());
+ }
+
+ long getValue(LongFlag flag) {
+ return mPlugin.getValue(flag.getId(), flag.getDefault());
+ }
+
+ float getValue(FloatFlag flag) {
+ return mPlugin.getValue(flag.getId(), flag.getDefault());
+ }
+
+ double getValue(DoubleFlag flag) {
+ return mPlugin.getValue(flag.getId(), flag.getDefault());
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
index 5882179226c9..0c9e6de4e4d0 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
@@ -22,6 +22,10 @@ import android.util.FeatureFlagUtils;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+
import javax.inject.Inject;
/**
@@ -40,6 +44,54 @@ public class FeatureFlags {
mContext = context;
}
+ /**
+ * @param flag The {@link BooleanFlag} of interest.
+ * @return The value of the flag.
+ */
+ public boolean isEnabled(BooleanFlag flag) {
+ return mFlagReader.isEnabled(flag);
+ }
+
+ /**
+ * @param flag The {@link StringFlag} of interest.
+ * @return The value of the flag.
+ */
+ public String getValue(StringFlag flag) {
+ return mFlagReader.getValue(flag);
+ }
+
+ /**
+ * @param flag The {@link IntFlag} of interest.
+ * @return The value of the flag.
+ */
+ public int getValue(IntFlag flag) {
+ return mFlagReader.getValue(flag);
+ }
+
+ /**
+ * @param flag The {@link LongFlag} of interest.
+ * @return The value of the flag.
+ */
+ public long getValue(LongFlag flag) {
+ return mFlagReader.getValue(flag);
+ }
+
+ /**
+ * @param flag The {@link FloatFlag} of interest.
+ * @return The value of the flag.
+ */
+ public float getValue(FloatFlag flag) {
+ return mFlagReader.getValue(flag);
+ }
+
+ /**
+ * @param flag The {@link DoubleFlag} of interest.
+ * @return The value of the flag.
+ */
+ public double getValue(DoubleFlag flag) {
+ return mFlagReader.getValue(flag);
+ }
+
public boolean isNewNotifPipelineEnabled() {
return mFlagReader.isEnabled(R.bool.flag_notification_pipeline2);
}
@@ -107,4 +159,31 @@ public class FeatureFlags {
public static boolean isProviderModelSettingEnabled(Context context) {
return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
}
+
+ private Map<Integer, Flag<?>> collectFlags() {
+ Map<Integer, Flag<?>> flags = new HashMap<>();
+
+ Field[] fields = this.getClass().getFields();
+
+ for (Field field : fields) {
+ Class<?> t = field.getType();
+ if (Flag.class.isAssignableFrom(t)) {
+ try {
+ //flags.add((Flag<?>) field.get(null));
+ Flag flag = (Flag) field.get(null);
+ flags.put(flag.getId(), flag);
+ } catch (IllegalAccessException e) {
+ // no-op
+ }
+ }
+ }
+
+ return flags;
+ }
+
+ /** Simple interface for beinga alerted when a specific flag changes value. */
+ public interface Listener {
+ /** */
+ void onFlagChanged(Flag<?> flag);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java
index 223714cfda30..7bc5f86510a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java
@@ -32,6 +32,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.wrapper.BuildInfo;
import org.junit.Before;
@@ -43,6 +44,7 @@ import org.mockito.MockitoAnnotations;
public class FeatureFlagReaderTest extends SysuiTestCase {
@Mock private Resources mResources;
@Mock private BuildInfo mBuildInfo;
+ @Mock private PluginManager mPluginManager;
@Mock private SystemPropertiesHelper mSystemPropertiesHelper;
private FeatureFlagReader mReader;
@@ -63,7 +65,8 @@ public class FeatureFlagReaderTest extends SysuiTestCase {
private void initialize(boolean isDebuggable, boolean isOverrideable) {
when(mBuildInfo.isDebuggable()).thenReturn(isDebuggable);
when(mResources.getBoolean(R.bool.are_flags_overrideable)).thenReturn(isOverrideable);
- mReader = new FeatureFlagReader(mResources, mBuildInfo, mSystemPropertiesHelper);
+ mReader = new FeatureFlagReader(
+ mResources, mBuildInfo, mPluginManager, mSystemPropertiesHelper);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
new file mode 100644
index 000000000000..25c302885e07
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 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 static com.google.common.truth.Truth.assertWithMessage;
+
+import android.util.Pair;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@SmallTest
+public class FlagsTest extends SysuiTestCase {
+
+ @Test
+ public void testDuplicateFlagIdCheckWorks() {
+ List<Pair<String, Flag<?>>> flags = collectFlags(DuplicateFlagContainer.class);
+ Map<Integer, List<String>> duplicates = groupDuplicateFlags(flags);
+
+ assertWithMessage(generateAssertionMessage(duplicates))
+ .that(duplicates.size()).isEqualTo(2);
+ }
+
+ @Test
+ public void testNoDuplicateFlagIds() {
+ List<Pair<String, Flag<?>>> flags = collectFlags(Flags.class);
+ Map<Integer, List<String>> duplicates = groupDuplicateFlags(flags);
+
+ assertWithMessage(generateAssertionMessage(duplicates))
+ .that(duplicates.size()).isEqualTo(0);
+ }
+
+ private String generateAssertionMessage(Map<Integer, List<String>> duplicates) {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("Duplicate flag keys found: {");
+ for (int id : duplicates.keySet()) {
+ stringBuilder
+ .append(" ")
+ .append(id)
+ .append(": [")
+ .append(String.join(", ", duplicates.get(id)))
+ .append("]");
+ }
+ stringBuilder.append(" }");
+
+ return stringBuilder.toString();
+ }
+
+ private List<Pair<String, Flag<?>>> collectFlags(Class<?> clz) {
+ List<Pair<String, Flag<?>>> flags = new ArrayList<>();
+
+ Field[] fields = clz.getFields();
+
+ for (Field field : fields) {
+ Class<?> t = field.getType();
+ if (Flag.class.isAssignableFrom(t)) {
+ try {
+ flags.add(Pair.create(field.getName(), (Flag<?>) field.get(null)));
+ } catch (IllegalAccessException e) {
+ // no-op
+ }
+ }
+ }
+
+ return flags;
+ }
+
+ private Map<Integer, List<String>> groupDuplicateFlags(List<Pair<String, Flag<?>>> flags) {
+ Map<Integer, List<String>> grouping = new HashMap<>();
+
+ for (Pair<String, Flag<?>> flag : flags) {
+ grouping.putIfAbsent(flag.second.getId(), new ArrayList<>());
+ grouping.get(flag.second.getId()).add(flag.first);
+ }
+
+ Map<Integer, List<String>> result = new HashMap<>();
+ for (Integer id : grouping.keySet()) {
+ if (grouping.get(id).size() > 1) {
+ result.put(id, grouping.get(id));
+ }
+ }
+
+ return result;
+ }
+
+ 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 StringFlag C_FLAG = new StringFlag(0);
+
+ public static final BooleanFlag D_FLAG = new BooleanFlag(1);
+
+ public static final DoubleFlag E_FLAG = new DoubleFlag(3);
+ public static final DoubleFlag F_FLAG = new DoubleFlag(3);
+ }
+}