Merge "Merge "Add new Flag class. New FlagReaderPlugin." into sc-v2-dev am: d80f48c3bd" into sc-v2-dev-plus-aosp

This commit is contained in:
Automerger Merge Worker
2021-08-04 20:50:31 +00:00
committed by Android (Google) Code Review
7 changed files with 382 additions and 1 deletions

View File

@@ -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>

View File

@@ -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 {
}

View File

@@ -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;
}
}

View File

@@ -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());
}
/**

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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);
}
}