Step 1 of Removing Ids from Flags.

With this change, we start relying more directly on the string names
that are now associated with flags. The ids persist so that we can
look up existing overrides and push them into the new system.

After a couple of weeks, the plan will be to remove the ids entirely.

Bug: 265188950
Test: manually built before and after cl to ensure overrides persist.
Change-Id: I0faac671b43a0d24262e78ccdb4e23e44f73eeea
This commit is contained in:
Dave Mankoff
2023-01-12 17:57:26 +00:00
parent 90023480a0
commit cd1af6a4d1
18 changed files with 395 additions and 360 deletions

View File

@@ -47,10 +47,6 @@ interface ResourceFlag<T> : Flag<T> {
val resourceId: Int
}
interface DeviceConfigFlag<T> : Flag<T> {
val default: T
}
interface SysPropFlag<T> : Flag<T> {
val default: T
}
@@ -80,8 +76,8 @@ abstract class BooleanFlag constructor(
private constructor(parcel: Parcel) : this(
id = parcel.readInt(),
name = parcel.readString(),
namespace = parcel.readString(),
name = parcel.readString() ?: "",
namespace = parcel.readString() ?: "",
default = parcel.readBoolean(),
teamfood = parcel.readBoolean(),
overridden = parcel.readBoolean()
@@ -136,21 +132,6 @@ data class ResourceBooleanFlag constructor(
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 constructor(
override val id: Int,
override val name: String,
override val namespace: String,
override val default: Boolean = false,
override val teamfood: Boolean = false
) : DeviceConfigFlag<Boolean>
/**
* A Flag that can reads its overrides from System Properties.
*
@@ -186,8 +167,8 @@ data class StringFlag constructor(
private constructor(parcel: Parcel) : this(
id = parcel.readInt(),
name = parcel.readString(),
namespace = parcel.readString(),
name = parcel.readString() ?: "",
namespace = parcel.readString() ?: "",
default = parcel.readString() ?: ""
)
@@ -226,8 +207,8 @@ data class IntFlag constructor(
private constructor(parcel: Parcel) : this(
id = parcel.readInt(),
name = parcel.readString(),
namespace = parcel.readString(),
name = parcel.readString() ?: "",
namespace = parcel.readString() ?: "",
default = parcel.readInt()
)
@@ -266,8 +247,8 @@ data class LongFlag constructor(
private constructor(parcel: Parcel) : this(
id = parcel.readInt(),
name = parcel.readString(),
namespace = parcel.readString(),
name = parcel.readString() ?: "",
namespace = parcel.readString() ?: "",
default = parcel.readLong()
)
@@ -298,8 +279,8 @@ data class FloatFlag constructor(
private constructor(parcel: Parcel) : this(
id = parcel.readInt(),
name = parcel.readString(),
namespace = parcel.readString(),
name = parcel.readString() ?: "",
namespace = parcel.readString() ?: "",
default = parcel.readFloat()
)
@@ -338,8 +319,8 @@ data class DoubleFlag constructor(
private constructor(parcel: Parcel) : this(
id = parcel.readInt(),
name = parcel.readString(),
namespace = parcel.readString(),
name = parcel.readString() ?: "",
namespace = parcel.readString() ?: "",
default = parcel.readDouble()
)

View File

@@ -34,7 +34,7 @@ interface FlagListenable {
/** An event representing the change */
interface FlagEvent {
/** the id of the flag which changed */
val flagId: Int
val flagName: String
/** if all listeners alerted invoke this method, the restart will be skipped */
fun requestNoRestart()
}

View File

@@ -39,7 +39,7 @@ class FlagManager constructor(
const val ACTION_GET_FLAGS = "com.android.systemui.action.GET_FLAGS"
const val FLAGS_PERMISSION = "com.android.systemui.permission.FLAGS"
const val ACTION_SYSUI_STARTED = "com.android.systemui.STARTED"
const val EXTRA_ID = "id"
const val EXTRA_NAME = "name"
const val EXTRA_VALUE = "value"
const val EXTRA_FLAGS = "flags"
private const val SETTINGS_PREFIX = "systemui/flags"
@@ -56,7 +56,7 @@ class FlagManager constructor(
* that the restart be suppressed
*/
var onSettingsChangedAction: Consumer<Boolean>? = null
var clearCacheAction: Consumer<Int>? = null
var clearCacheAction: Consumer<String>? = null
private val listeners: MutableSet<PerFlagListener> = mutableSetOf()
private val settingsObserver: ContentObserver = SettingsObserver()
@@ -96,35 +96,42 @@ class FlagManager constructor(
* Returns the stored value or null if not set.
* This API is used by TheFlippinApp.
*/
fun isEnabled(id: Int): Boolean? = readFlagValue(id, BooleanFlagSerializer)
fun isEnabled(name: String): Boolean? = readFlagValue(name, BooleanFlagSerializer)
/**
* Sets the value of a boolean flag.
* This API is used by TheFlippinApp.
*/
fun setFlagValue(id: Int, enabled: Boolean) {
val intent = createIntent(id)
fun setFlagValue(name: String, enabled: Boolean) {
val intent = createIntent(name)
intent.putExtra(EXTRA_VALUE, enabled)
context.sendBroadcast(intent)
}
fun eraseFlag(id: Int) {
val intent = createIntent(id)
fun eraseFlag(name: String) {
val intent = createIntent(name)
context.sendBroadcast(intent)
}
/** Returns the stored value or null if not set. */
// TODO(b/265188950): Remove method this once ids are fully deprecated.
fun <T> readFlagValue(id: Int, serializer: FlagSerializer<T>): T? {
val data = settings.getString(idToSettingsKey(id))
val data = settings.getStringFromSecure(idToSettingsKey(id))
return serializer.fromSettingsData(data)
}
/** Returns the stored value or null if not set. */
fun <T> readFlagValue(name: String, serializer: FlagSerializer<T>): T? {
val data = settings.getString(nameToSettingsKey(name))
return serializer.fromSettingsData(data)
}
override fun addListener(flag: Flag<*>, listener: FlagListenable.Listener) {
synchronized(listeners) {
val registerNeeded = listeners.isEmpty()
listeners.add(PerFlagListener(flag.id, listener))
listeners.add(PerFlagListener(flag.name, listener))
if (registerNeeded) {
settings.registerContentObserver(SETTINGS_PREFIX, true, settingsObserver)
}
@@ -143,38 +150,38 @@ class FlagManager constructor(
}
}
private fun createIntent(id: Int): Intent {
private fun createIntent(name: String): Intent {
val intent = Intent(ACTION_SET_FLAG)
intent.setPackage(RECEIVING_PACKAGE)
intent.putExtra(EXTRA_ID, id)
intent.putExtra(EXTRA_NAME, name)
return intent
}
// TODO(b/265188950): Remove method this once ids are fully deprecated.
fun idToSettingsKey(id: Int): String {
return "$SETTINGS_PREFIX/$id"
}
fun nameToSettingsKey(name: String): String {
return "$SETTINGS_PREFIX/$name"
}
inner class SettingsObserver : ContentObserver(handler) {
override fun onChange(selfChange: Boolean, uri: Uri?) {
if (uri == null) {
return
}
val parts = uri.pathSegments
val idStr = parts[parts.size - 1]
val id = try {
idStr.toInt()
} catch (e: NumberFormatException) {
return
}
clearCacheAction?.accept(id)
dispatchListenersAndMaybeRestart(id, onSettingsChangedAction)
val name = parts[parts.size - 1]
clearCacheAction?.accept(name)
dispatchListenersAndMaybeRestart(name, onSettingsChangedAction)
}
}
fun dispatchListenersAndMaybeRestart(id: Int, restartAction: Consumer<Boolean>?) {
fun dispatchListenersAndMaybeRestart(name: String, restartAction: Consumer<Boolean>?) {
val filteredListeners: List<FlagListenable.Listener> = synchronized(listeners) {
listeners.mapNotNull { if (it.id == id) it.listener else null }
listeners.mapNotNull { if (it.name == name) it.listener else null }
}
// If there are no listeners, there's nothing to dispatch to, and nothing to suppress it.
if (filteredListeners.isEmpty()) {
@@ -185,7 +192,7 @@ class FlagManager constructor(
val suppressRestartList: List<Boolean> = filteredListeners.map { listener ->
var didRequestNoRestart = false
val event = object : FlagListenable.FlagEvent {
override val flagId = id
override val flagName = name
override fun requestNoRestart() {
didRequestNoRestart = true
}
@@ -198,7 +205,7 @@ class FlagManager constructor(
restartAction?.accept(suppressRestart)
}
private data class PerFlagListener(val id: Int, val listener: FlagListenable.Listener)
private data class PerFlagListener(val name: String, val listener: FlagListenable.Listener)
}
class NoFlagResultsException : Exception(

View File

@@ -22,7 +22,10 @@ import android.provider.Settings
class FlagSettingsHelper(private val contentResolver: ContentResolver) {
fun getString(key: String): String? = Settings.Secure.getString(contentResolver, key)
// TODO(b/265188950): Remove method this once ids are fully deprecated.
fun getStringFromSecure(key: String): String? = Settings.Secure.getString(contentResolver, key)
fun getString(key: String): String? = Settings.Global.getString(contentResolver, key)
fun registerContentObserver(
name: String,

View File

@@ -35,7 +35,7 @@ object FlagsFactory {
teamfood: Boolean = false
): UnreleasedFlag {
val flag = UnreleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
FlagsFactory.checkForDupesAndAdd(flag)
checkForDupesAndAdd(flag)
return flag
}
@@ -46,7 +46,7 @@ object FlagsFactory {
teamfood: Boolean = false
): ReleasedFlag {
val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
FlagsFactory.checkForDupesAndAdd(flag)
checkForDupesAndAdd(flag)
return flag
}
@@ -65,7 +65,7 @@ object FlagsFactory {
resourceId = resourceId,
teamfood = teamfood
)
FlagsFactory.checkForDupesAndAdd(flag)
checkForDupesAndAdd(flag)
return flag
}
@@ -77,18 +77,13 @@ object FlagsFactory {
): SysPropBooleanFlag {
val flag =
SysPropBooleanFlag(id = id, name = name, namespace = "systemui", default = default)
FlagsFactory.checkForDupesAndAdd(flag)
checkForDupesAndAdd(flag)
return flag
}
private fun checkForDupesAndAdd(flag: Flag<*>) {
if (flagMap.containsKey(flag.name)) {
throw IllegalArgumentException("Name {flag.name} is already registered")
}
flagMap.forEach {
if (it.value.id == flag.id) {
throw IllegalArgumentException("Name {flag.id} is already registered")
}
throw IllegalArgumentException("Name {$flag.name} is already registered")
}
flagMap[flag.name] = flag
}

View File

@@ -34,7 +34,7 @@ class ChooserSelector @Inject constructor(
override fun start() {
coroutineScope.launch {
val listener = FlagListenable.Listener { event ->
if (event.flagId == Flags.CHOOSER_UNBUNDLED.id) {
if (event.flagName == Flags.CHOOSER_UNBUNDLED.name) {
launch { updateUnbundledChooserEnabled() }
event.requestNoRestart()
}

View File

@@ -19,7 +19,7 @@ package com.android.systemui.flags;
import static com.android.systemui.flags.FlagManager.ACTION_GET_FLAGS;
import static com.android.systemui.flags.FlagManager.ACTION_SET_FLAG;
import static com.android.systemui.flags.FlagManager.EXTRA_FLAGS;
import static com.android.systemui.flags.FlagManager.EXTRA_ID;
import static com.android.systemui.flags.FlagManager.EXTRA_NAME;
import static com.android.systemui.flags.FlagManager.EXTRA_VALUE;
import static com.android.systemui.flags.FlagsCommonModule.ALL_FLAGS;
@@ -39,6 +39,7 @@ import androidx.annotation.Nullable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.settings.SecureSettings;
import org.jetbrains.annotations.NotNull;
@@ -72,14 +73,15 @@ public class FeatureFlagsDebug implements FeatureFlags {
private final FlagManager mFlagManager;
private final Context mContext;
private final GlobalSettings mGlobalSettings;
private final SecureSettings mSecureSettings;
private final Resources mResources;
private final SystemPropertiesHelper mSystemProperties;
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<>();
private final Map<Integer, Integer> mIntFlagCache = new TreeMap<>();
private final Map<String, Flag<?>> mAllFlags;
private final Map<String, Boolean> mBooleanFlagCache = new TreeMap<>();
private final Map<String, String> mStringFlagCache = new TreeMap<>();
private final Map<String, Integer> mIntFlagCache = new TreeMap<>();
private final Restarter mRestarter;
private final ServerFlagReader.ChangeListener mOnPropertiesChanged =
@@ -94,14 +96,16 @@ public class FeatureFlagsDebug implements FeatureFlags {
public FeatureFlagsDebug(
FlagManager flagManager,
Context context,
GlobalSettings globalSettings,
SecureSettings secureSettings,
SystemPropertiesHelper systemProperties,
@Main Resources resources,
ServerFlagReader serverFlagReader,
@Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags,
@Named(ALL_FLAGS) Map<String, Flag<?>> allFlags,
Restarter restarter) {
mFlagManager = flagManager;
mContext = context;
mGlobalSettings = globalSettings;
mSecureSettings = secureSettings;
mResources = resources;
mSystemProperties = systemProperties;
@@ -133,96 +137,103 @@ public class FeatureFlagsDebug implements FeatureFlags {
}
private boolean isEnabledInternal(@NotNull BooleanFlag flag) {
int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
mBooleanFlagCache.put(id,
String name = flag.getName();
if (!mBooleanFlagCache.containsKey(name)) {
mBooleanFlagCache.put(name,
readBooleanFlagInternal(flag, flag.getDefault()));
}
return mBooleanFlagCache.get(id);
return mBooleanFlagCache.get(name);
}
@Override
public boolean isEnabled(@NonNull ResourceBooleanFlag flag) {
int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
mBooleanFlagCache.put(id,
String name = flag.getName();
if (!mBooleanFlagCache.containsKey(name)) {
mBooleanFlagCache.put(name,
readBooleanFlagInternal(flag, mResources.getBoolean(flag.getResourceId())));
}
return mBooleanFlagCache.get(id);
return mBooleanFlagCache.get(name);
}
@Override
public boolean isEnabled(@NonNull SysPropBooleanFlag flag) {
int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
String name = flag.getName();
if (!mBooleanFlagCache.containsKey(name)) {
// Use #readFlagValue to get the default. That will allow it to fall through to
// teamfood if need be.
mBooleanFlagCache.put(
id,
name,
mSystemProperties.getBoolean(
flag.getName(),
readBooleanFlagInternal(flag, flag.getDefault())));
}
return mBooleanFlagCache.get(id);
return mBooleanFlagCache.get(name);
}
@NonNull
@Override
public String getString(@NonNull StringFlag flag) {
int id = flag.getId();
if (!mStringFlagCache.containsKey(id)) {
mStringFlagCache.put(id,
readFlagValueInternal(id, flag.getDefault(), StringFlagSerializer.INSTANCE));
String name = flag.getName();
if (!mStringFlagCache.containsKey(name)) {
mStringFlagCache.put(name,
readFlagValueInternal(
flag.getId(), name, flag.getDefault(), StringFlagSerializer.INSTANCE));
}
return mStringFlagCache.get(id);
return mStringFlagCache.get(name);
}
@NonNull
@Override
public String getString(@NonNull ResourceStringFlag flag) {
int id = flag.getId();
if (!mStringFlagCache.containsKey(id)) {
mStringFlagCache.put(id,
readFlagValueInternal(id, mResources.getString(flag.getResourceId()),
String name = flag.getName();
if (!mStringFlagCache.containsKey(name)) {
mStringFlagCache.put(name,
readFlagValueInternal(
flag.getId(), name, mResources.getString(flag.getResourceId()),
StringFlagSerializer.INSTANCE));
}
return mStringFlagCache.get(id);
return mStringFlagCache.get(name);
}
@NonNull
@Override
public int getInt(@NonNull IntFlag flag) {
int id = flag.getId();
if (!mIntFlagCache.containsKey(id)) {
mIntFlagCache.put(id,
readFlagValueInternal(id, flag.getDefault(), IntFlagSerializer.INSTANCE));
String name = flag.getName();
if (!mIntFlagCache.containsKey(name)) {
mIntFlagCache.put(name,
readFlagValueInternal(
flag.getId(), name, flag.getDefault(), IntFlagSerializer.INSTANCE));
}
return mIntFlagCache.get(id);
return mIntFlagCache.get(name);
}
@NonNull
@Override
public int getInt(@NonNull ResourceIntFlag flag) {
int id = flag.getId();
if (!mIntFlagCache.containsKey(id)) {
mIntFlagCache.put(id,
readFlagValueInternal(id, mResources.getInteger(flag.getResourceId()),
String name = flag.getName();
if (!mIntFlagCache.containsKey(name)) {
mIntFlagCache.put(name,
readFlagValueInternal(
flag.getId(), name, mResources.getInteger(flag.getResourceId()),
IntFlagSerializer.INSTANCE));
}
return mIntFlagCache.get(id);
return mIntFlagCache.get(name);
}
/** Specific override for Boolean flags that checks against the teamfood list.*/
private boolean readBooleanFlagInternal(Flag<Boolean> flag, boolean defaultValue) {
Boolean result = readBooleanFlagOverride(flag.getId());
Boolean result = readBooleanFlagOverride(flag.getName());
if (result == null) {
result = readBooleanFlagOverride(flag.getId());
}
boolean hasServerOverride = mServerFlagReader.hasOverride(
flag.getNamespace(), flag.getName());
@@ -231,7 +242,7 @@ public class FeatureFlagsDebug implements FeatureFlags {
if (!hasServerOverride
&& !defaultValue
&& result == null
&& flag.getId() != Flags.TEAMFOOD.getId()
&& !flag.getName().equals(Flags.TEAMFOOD.getName())
&& flag.getTeamfood()) {
return isEnabled(Flags.TEAMFOOD);
}
@@ -244,16 +255,31 @@ public class FeatureFlagsDebug implements FeatureFlags {
return readFlagValueInternal(id, BooleanFlagSerializer.INSTANCE);
}
private Boolean readBooleanFlagOverride(String name) {
return readFlagValueInternal(name, BooleanFlagSerializer.INSTANCE);
}
// TODO(b/265188950): Remove id from this method once ids are fully deprecated.
@NonNull
private <T> T readFlagValueInternal(
int id, @NonNull T defaultValue, FlagSerializer<T> serializer) {
int id, String name, @NonNull T defaultValue, FlagSerializer<T> serializer) {
requireNonNull(defaultValue, "defaultValue");
T result = readFlagValueInternal(id, serializer);
return result == null ? defaultValue : result;
T resultForName = readFlagValueInternal(name, serializer);
if (resultForName == null) {
T resultForId = readFlagValueInternal(id, serializer);
if (resultForId == null) {
return defaultValue;
} else {
setFlagValue(name, resultForId, serializer);
return resultForId;
}
}
return resultForName;
}
/** Returns the stored value or null if not set. */
// TODO(b/265188950): Remove method this once ids are fully deprecated.
@Nullable
private <T> T readFlagValueInternal(int id, FlagSerializer<T> serializer) {
try {
@@ -264,51 +290,71 @@ public class FeatureFlagsDebug implements FeatureFlags {
return null;
}
private <T> void setFlagValue(int id, @NonNull T value, FlagSerializer<T> serializer) {
/** Returns the stored value or null if not set. */
@Nullable
private <T> T readFlagValueInternal(String name, FlagSerializer<T> serializer) {
try {
return mFlagManager.readFlagValue(name, serializer);
} catch (Exception e) {
eraseInternal(name);
}
return null;
}
private <T> void setFlagValue(String name, @NonNull T value, FlagSerializer<T> serializer) {
requireNonNull(value, "Cannot set a null value");
T currentValue = readFlagValueInternal(id, serializer);
T currentValue = readFlagValueInternal(name, serializer);
if (Objects.equals(currentValue, value)) {
Log.i(TAG, "Flag id " + id + " is already " + value);
Log.i(TAG, "Flag id " + name + " is already " + value);
return;
}
final String data = serializer.toSettingsData(value);
if (data == null) {
Log.w(TAG, "Failed to set id " + id + " to " + value);
Log.w(TAG, "Failed to set id " + name + " to " + value);
return;
}
mSecureSettings.putStringForUser(mFlagManager.idToSettingsKey(id), data,
mGlobalSettings.putStringForUser(mFlagManager.nameToSettingsKey(name), data,
UserHandle.USER_CURRENT);
Log.i(TAG, "Set id " + id + " to " + value);
removeFromCache(id);
mFlagManager.dispatchListenersAndMaybeRestart(id, this::restartSystemUI);
Log.i(TAG, "Set id " + name + " to " + value);
removeFromCache(name);
mFlagManager.dispatchListenersAndMaybeRestart(name, this::restartSystemUI);
}
<T> void eraseFlag(Flag<T> flag) {
if (flag instanceof SysPropFlag) {
mSystemProperties.erase(((SysPropFlag<T>) flag).getName());
dispatchListenersAndMaybeRestart(flag.getId(), this::restartAndroid);
dispatchListenersAndMaybeRestart(flag.getName(), this::restartAndroid);
} else {
eraseFlag(flag.getId());
eraseFlag(flag.getName());
}
}
/** Erase a flag's overridden value if there is one. */
private void eraseFlag(int id) {
eraseInternal(id);
removeFromCache(id);
dispatchListenersAndMaybeRestart(id, this::restartSystemUI);
private void eraseFlag(String name) {
eraseInternal(name);
removeFromCache(name);
dispatchListenersAndMaybeRestart(name, this::restartSystemUI);
}
private void dispatchListenersAndMaybeRestart(int id, Consumer<Boolean> restartAction) {
mFlagManager.dispatchListenersAndMaybeRestart(id, restartAction);
private void dispatchListenersAndMaybeRestart(String name, Consumer<Boolean> restartAction) {
mFlagManager.dispatchListenersAndMaybeRestart(name, restartAction);
}
/** Works just like {@link #eraseFlag(int)} except that it doesn't restart SystemUI. */
/** Works just like {@link #eraseFlag(String)} except that it doesn't restart SystemUI. */
// TODO(b/265188950): Remove method this once ids are fully deprecated.
private void eraseInternal(int id) {
// We can't actually "erase" things from sysprops, but we can set them to empty!
mSecureSettings.putStringForUser(mFlagManager.idToSettingsKey(id), "",
// We can't actually "erase" things from settings, but we can set them to empty!
mGlobalSettings.putStringForUser(mFlagManager.idToSettingsKey(id), "",
UserHandle.USER_CURRENT);
Log.i(TAG, "Erase id " + id);
Log.i(TAG, "Erase name " + id);
}
/** Works just like {@link #eraseFlag(String)} except that it doesn't restart SystemUI. */
private void eraseInternal(String name) {
// We can't actually "erase" things from settings, but we can set them to empty!
mGlobalSettings.putStringForUser(mFlagManager.nameToSettingsKey(name), "",
UserHandle.USER_CURRENT);
Log.i(TAG, "Erase name " + name);
}
@Override
@@ -339,13 +385,13 @@ public class FeatureFlagsDebug implements FeatureFlags {
void setBooleanFlagInternal(Flag<?> flag, boolean value) {
if (flag instanceof BooleanFlag) {
setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
setFlagValue(flag.getName(), value, BooleanFlagSerializer.INSTANCE);
} else if (flag instanceof ResourceBooleanFlag) {
setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
setFlagValue(flag.getName(), 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(),
dispatchListenersAndMaybeRestart(flag.getName(),
FeatureFlagsDebug.this::restartAndroid);
} else {
throw new IllegalArgumentException("Unknown flag type");
@@ -354,9 +400,9 @@ public class FeatureFlagsDebug implements FeatureFlags {
void setStringFlagInternal(Flag<?> flag, String value) {
if (flag instanceof StringFlag) {
setFlagValue(flag.getId(), value, StringFlagSerializer.INSTANCE);
setFlagValue(flag.getName(), value, StringFlagSerializer.INSTANCE);
} else if (flag instanceof ResourceStringFlag) {
setFlagValue(flag.getId(), value, StringFlagSerializer.INSTANCE);
setFlagValue(flag.getName(), value, StringFlagSerializer.INSTANCE);
} else {
throw new IllegalArgumentException("Unknown flag type");
}
@@ -364,9 +410,9 @@ public class FeatureFlagsDebug implements FeatureFlags {
void setIntFlagInternal(Flag<?> flag, int value) {
if (flag instanceof IntFlag) {
setFlagValue(flag.getId(), value, IntFlagSerializer.INSTANCE);
setFlagValue(flag.getName(), value, IntFlagSerializer.INSTANCE);
} else if (flag instanceof ResourceIntFlag) {
setFlagValue(flag.getId(), value, IntFlagSerializer.INSTANCE);
setFlagValue(flag.getName(), value, IntFlagSerializer.INSTANCE);
} else {
throw new IllegalArgumentException("Unknown flag type");
}
@@ -405,17 +451,17 @@ public class FeatureFlagsDebug implements FeatureFlags {
Log.w(TAG, "No extras");
return;
}
int id = extras.getInt(EXTRA_ID);
if (id <= 0) {
Log.w(TAG, "ID not set or less than or equal to 0: " + id);
String name = extras.getString(EXTRA_NAME);
if (name == null || name.isEmpty()) {
Log.w(TAG, "NAME not set or is empty: " + name);
return;
}
if (!mAllFlags.containsKey(id)) {
Log.w(TAG, "Tried to set unknown id: " + id);
if (!mAllFlags.containsKey(name)) {
Log.w(TAG, "Tried to set unknown name: " + name);
return;
}
Flag<?> flag = mAllFlags.get(id);
Flag<?> flag = mAllFlags.get(name);
if (!extras.containsKey(EXTRA_VALUE)) {
eraseFlag(flag);
@@ -452,13 +498,16 @@ public class FeatureFlagsDebug implements FeatureFlags {
if (f instanceof ReleasedFlag) {
enabled = isEnabled((ReleasedFlag) f);
overridden = readBooleanFlagOverride(f.getId()) != null;
overridden = readBooleanFlagOverride(f.getName()) != null
|| readBooleanFlagOverride(f.getId()) != null;
} else if (f instanceof UnreleasedFlag) {
enabled = isEnabled((UnreleasedFlag) f);
overridden = readBooleanFlagOverride(f.getId()) != null;
overridden = readBooleanFlagOverride(f.getName()) != null
|| readBooleanFlagOverride(f.getId()) != null;
} else if (f instanceof ResourceBooleanFlag) {
enabled = isEnabled((ResourceBooleanFlag) f);
overridden = readBooleanFlagOverride(f.getId()) != null;
overridden = readBooleanFlagOverride(f.getName()) != null
|| readBooleanFlagOverride(f.getId()) != null;
} else if (f instanceof SysPropBooleanFlag) {
// TODO(b/223379190): Teamfood not supported for sysprop flags yet.
enabled = isEnabled((SysPropBooleanFlag) f);
@@ -480,9 +529,9 @@ public class FeatureFlagsDebug implements FeatureFlags {
}
};
private void removeFromCache(int id) {
mBooleanFlagCache.remove(id);
mStringFlagCache.remove(id);
private void removeFromCache(String name) {
mBooleanFlagCache.remove(name);
mStringFlagCache.remove(name);
}
@Override

View File

@@ -21,18 +21,16 @@ import static com.android.systemui.flags.FlagsCommonModule.ALL_FLAGS;
import static java.util.Objects.requireNonNull;
import android.content.res.Resources;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import androidx.annotation.NonNull;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.util.DeviceConfigProxy;
import org.jetbrains.annotations.NotNull;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
@@ -50,12 +48,11 @@ public class FeatureFlagsRelease implements FeatureFlags {
private final Resources mResources;
private final SystemPropertiesHelper mSystemProperties;
private final DeviceConfigProxy mDeviceConfigProxy;
private final ServerFlagReader mServerFlagReader;
private final Restarter mRestarter;
private final Map<Integer, Flag<?>> mAllFlags;
SparseBooleanArray mBooleanCache = new SparseBooleanArray();
SparseArray<String> mStringCache = new SparseArray<>();
private final Map<String, Flag<?>> mAllFlags;
private final Map<String, Boolean> mBooleanCache = new HashMap<>();
private final Map<String, String> mStringCache = new HashMap<>();
private final ServerFlagReader.ChangeListener mOnPropertiesChanged =
new ServerFlagReader.ChangeListener() {
@@ -69,13 +66,11 @@ public class FeatureFlagsRelease implements FeatureFlags {
public FeatureFlagsRelease(
@Main Resources resources,
SystemPropertiesHelper systemProperties,
DeviceConfigProxy deviceConfigProxy,
ServerFlagReader serverFlagReader,
@Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags,
@Named(ALL_FLAGS) Map<String, Flag<?>> allFlags,
Restarter restarter) {
mResources = resources;
mSystemProperties = systemProperties;
mDeviceConfigProxy = deviceConfigProxy;
mServerFlagReader = serverFlagReader;
mAllFlags = allFlags;
mRestarter = restarter;
@@ -106,50 +101,48 @@ public class FeatureFlagsRelease implements FeatureFlags {
@Override
public boolean isEnabled(ResourceBooleanFlag flag) {
int cacheIndex = mBooleanCache.indexOfKey(flag.getId());
if (cacheIndex < 0) {
return isEnabled(flag.getId(), mResources.getBoolean(flag.getResourceId()));
if (!mBooleanCache.containsKey(flag.getName())) {
return isEnabled(flag.getName(), mResources.getBoolean(flag.getResourceId()));
}
return mBooleanCache.valueAt(cacheIndex);
return mBooleanCache.get(flag.getName());
}
@Override
public boolean isEnabled(SysPropBooleanFlag flag) {
int cacheIndex = mBooleanCache.indexOfKey(flag.getId());
if (cacheIndex < 0) {
if (!mBooleanCache.containsKey(flag.getName())) {
return isEnabled(
flag.getId(), mSystemProperties.getBoolean(flag.getName(), flag.getDefault()));
flag.getName(),
mSystemProperties.getBoolean(flag.getName(), flag.getDefault()));
}
return mBooleanCache.valueAt(cacheIndex);
return mBooleanCache.get(flag.getName());
}
private boolean isEnabled(int key, boolean defaultValue) {
mBooleanCache.append(key, defaultValue);
private boolean isEnabled(String name, boolean defaultValue) {
mBooleanCache.put(name, defaultValue);
return defaultValue;
}
@NonNull
@Override
public String getString(@NonNull StringFlag flag) {
return getString(flag.getId(), flag.getDefault());
return getString(flag.getName(), flag.getDefault());
}
@NonNull
@Override
public String getString(@NonNull ResourceStringFlag flag) {
int cacheIndex = mStringCache.indexOfKey(flag.getId());
if (cacheIndex < 0) {
return getString(flag.getId(),
if (!mStringCache.containsKey(flag.getName())) {
return getString(flag.getName(),
requireNonNull(mResources.getString(flag.getResourceId())));
}
return mStringCache.valueAt(cacheIndex);
return mStringCache.get(flag.getName());
}
private String getString(int key, String defaultValue) {
mStringCache.append(key, defaultValue);
private String getString(String name, String defaultValue) {
mStringCache.put(name, defaultValue);
return defaultValue;
}
@@ -169,11 +162,17 @@ public class FeatureFlagsRelease implements FeatureFlags {
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("can override: false");
Map<String, Flag<?>> knownFlags = FlagsFactory.INSTANCE.getKnownFlags();
pw.println("Booleans: ");
for (Map.Entry<String, Flag<?>> nameToFlag : knownFlags.entrySet()) {
Flag<?> flag = nameToFlag.getValue();
int id = flag.getId();
if (!(flag instanceof BooleanFlag)
|| !(flag instanceof ResourceBooleanFlag)
|| !(flag instanceof SysPropBooleanFlag)) {
continue;
}
boolean def = false;
if (mBooleanCache.indexOfKey(flag.getId()) < 0) {
if (!mBooleanCache.containsKey(flag.getName())) {
if (flag instanceof SysPropBooleanFlag) {
SysPropBooleanFlag f = (SysPropBooleanFlag) flag;
def = mSystemProperties.getBoolean(f.getName(), f.getDefault());
@@ -185,15 +184,32 @@ public class FeatureFlagsRelease implements FeatureFlags {
def = f.getDefault();
}
}
pw.println(" sysui_flag_" + id + ": " + (mBooleanCache.get(id, def)));
pw.println(
" " + flag.getName() + ": "
+ (mBooleanCache.getOrDefault(flag.getName(), def)));
}
int numStrings = mStringCache.size();
pw.println("Strings: " + numStrings);
for (int i = 0; i < numStrings; i++) {
final int id = mStringCache.keyAt(i);
final String value = mStringCache.valueAt(i);
final int length = value.length();
pw.println(" sysui_flag_" + id + ": [length=" + length + "] \"" + value + "\"");
pw.println("Strings: ");
for (Map.Entry<String, Flag<?>> nameToFlag : knownFlags.entrySet()) {
Flag<?> flag = nameToFlag.getValue();
if (!(flag instanceof StringFlag)
|| !(flag instanceof ResourceStringFlag)) {
continue;
}
String def = "";
if (!mBooleanCache.containsKey(flag.getName())) {
if (flag instanceof ResourceStringFlag) {
ResourceStringFlag f = (ResourceStringFlag) flag;
def = mResources.getString(f.getResourceId());
} else if (flag instanceof StringFlag) {
StringFlag f = (StringFlag) flag;
def = f.getDefault();
}
}
String value = mStringCache.getOrDefault(flag.getName(), def);
pw.println(
" " + flag.getName() + ": [length=" + value.length() + "] \"" + value + "\"");
}
}
}

View File

@@ -39,12 +39,12 @@ public class FlagCommand implements Command {
private final List<String> mOffCommands = List.of("false", "off", "0", "disable");
private final List<String> mSetCommands = List.of("set", "put");
private final FeatureFlagsDebug mFeatureFlags;
private final Map<Integer, Flag<?>> mAllFlags;
private final Map<String, Flag<?>> mAllFlags;
@Inject
FlagCommand(
FeatureFlagsDebug featureFlags,
@Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags
@Named(ALL_FLAGS) Map<String, Flag<?>> allFlags
) {
mFeatureFlags = featureFlags;
mAllFlags = allFlags;
@@ -53,30 +53,22 @@ public class FlagCommand implements Command {
@Override
public void execute(@NonNull PrintWriter pw, @NonNull List<String> args) {
if (args.size() == 0) {
pw.println("Error: no flag id supplied");
pw.println("Error: no flag name supplied");
help(pw);
pw.println();
printKnownFlags(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;
}
String name = args.get(0);
if (!mAllFlags.containsKey(name)) {
pw.println("Unknown flag name: " + name);
pw.println();
printKnownFlags(pw);
return;
}
Flag<?> flag = mAllFlags.get(id);
Flag<?> flag = mAllFlags.get(name);
String cmd = "";
if (args.size() > 1) {
@@ -117,7 +109,7 @@ public class FlagCommand implements Command {
return;
}
pw.println("Flag " + id + " is " + newValue);
pw.println("Flag " + name + " is " + newValue);
pw.flush(); // Next command will restart sysui, so flush before we do so.
if (shouldSet) {
mFeatureFlags.setBooleanFlagInternal(flag, newValue);
@@ -136,11 +128,11 @@ public class FlagCommand implements Command {
return;
}
String value = args.get(2);
pw.println("Setting Flag " + id + " to " + value);
pw.println("Setting Flag " + name + " to " + value);
pw.flush(); // Next command will restart sysui, so flush before we do so.
mFeatureFlags.setStringFlagInternal(flag, args.get(2));
} else {
pw.println("Flag " + id + " is " + getStringFlag(flag));
pw.println("Flag " + name + " is " + getStringFlag(flag));
}
return;
} else if (isIntFlag(flag)) {
@@ -155,11 +147,11 @@ public class FlagCommand implements Command {
return;
}
int value = Integer.parseInt(args.get(2));
pw.println("Setting Flag " + id + " to " + value);
pw.println("Setting Flag " + name + " to " + value);
pw.flush(); // Next command will restart sysui, so flush before we do so.
mFeatureFlags.setIntFlagInternal(flag, value);
} else {
pw.println("Flag " + id + " is " + getIntFlag(flag));
pw.println("Flag " + name + " is " + getIntFlag(flag));
}
return;
}
@@ -182,8 +174,7 @@ public class FlagCommand implements Command {
private boolean isBooleanFlag(Flag<?> flag) {
return (flag instanceof BooleanFlag)
|| (flag instanceof ResourceBooleanFlag)
|| (flag instanceof SysPropFlag)
|| (flag instanceof DeviceConfigBooleanFlag);
|| (flag instanceof SysPropFlag);
}
private boolean isBooleanFlagEnabled(Flag<?> flag) {
@@ -252,15 +243,14 @@ public class FlagCommand implements Command {
for (int i = 0; i < longestFieldName - "Flag Name".length() + 1; i++) {
pw.print(" ");
}
pw.println("ID Value");
pw.println(" Value");
for (int i = 0; i < longestFieldName; i++) {
pw.print("=");
}
pw.println(" ==== ========");
pw.println(" ========");
for (String fieldName : fields.keySet()) {
Flag<?> flag = fields.get(fieldName);
int id = flag.getId();
if (id == 0 || !mAllFlags.containsKey(id)) {
if (!mAllFlags.containsKey(flag.getName())) {
continue;
}
pw.print(fieldName);
@@ -268,9 +258,9 @@ public class FlagCommand implements Command {
for (int i = 0; i < longestFieldName - fieldWidth + 1; i++) {
pw.print(" ");
}
pw.printf("%-4d ", id);
pw.print(" ");
if (isBooleanFlag(flag)) {
pw.println(isBooleanFlagEnabled(mAllFlags.get(id)));
pw.println(isBooleanFlagEnabled(flag));
} else if (isStringFlag(flag)) {
pw.println(getStringFlag(flag));
} else if (isIntFlag(flag)) {

View File

@@ -28,8 +28,8 @@ interface FlagsCommonModule {
@JvmStatic
@Provides
@Named(ALL_FLAGS)
fun providesAllFlags(): Map<Int, Flag<*>> {
return FlagsFactory.knownFlags.map { it.value.id to it.value }.toMap()
fun providesAllFlags(): Map<String, Flag<*>> {
return FlagsFactory.knownFlags
}
}
}

View File

@@ -54,10 +54,11 @@ class ServerFlagReaderImpl @Inject constructor(
return
}
for ((listener, flags) in listeners) {
propLoop@ for (propName in properties.keyset) {
for (flag in flags) {
if (propName == getServerOverrideName(flag.id)) {
if (propName == getServerOverrideName(flag.id) || propName == flag.name) {
listener.onChange()
break@propLoop
}

View File

@@ -148,7 +148,7 @@ class ChooserSelectorTest : SysuiTestCase() {
// Act
whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(true)
flagListener.value.onFlagChanged(TestFlagEvent(Flags.CHOOSER_UNBUNDLED.id))
flagListener.value.onFlagChanged(TestFlagEvent(Flags.CHOOSER_UNBUNDLED.name))
// Assert
verify(mockPackageManager, times(2)).setComponentEnabledSetting(
@@ -175,7 +175,7 @@ class ChooserSelectorTest : SysuiTestCase() {
// Act
whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
flagListener.value.onFlagChanged(TestFlagEvent(Flags.CHOOSER_UNBUNDLED.id))
flagListener.value.onFlagChanged(TestFlagEvent(Flags.CHOOSER_UNBUNDLED.name))
// Assert
verify(mockPackageManager, times(2)).setComponentEnabledSetting(
@@ -198,13 +198,13 @@ class ChooserSelectorTest : SysuiTestCase() {
// Act
whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
flagListener.value.onFlagChanged(TestFlagEvent(Flags.CHOOSER_UNBUNDLED.id + 1))
flagListener.value.onFlagChanged(TestFlagEvent("other flag"))
// Assert
verifyZeroInteractions(mockPackageManager)
}
private class TestFlagEvent(override val flagId: Int) : FlagListenable.FlagEvent {
private class TestFlagEvent(override val flagName: String) : FlagListenable.FlagEvent {
override fun requestNoRestart() {}
}
}

View File

@@ -125,7 +125,7 @@ class FakeFeatureFlagsTest : SysuiTestCase() {
flags.set(unreleasedFlag, false)
flags.set(unreleasedFlag, false)
listener.verifyInOrder(unreleasedFlag.id, unreleasedFlag.id)
listener.verifyInOrder(unreleasedFlag.name, unreleasedFlag.name)
}
@Test
@@ -137,7 +137,7 @@ class FakeFeatureFlagsTest : SysuiTestCase() {
flags.set(stringFlag, "Test")
flags.set(stringFlag, "Test")
listener.verifyInOrder(stringFlag.id)
listener.verifyInOrder(stringFlag.name)
}
@Test
@@ -149,7 +149,7 @@ class FakeFeatureFlagsTest : SysuiTestCase() {
flags.removeListener(listener)
flags.set(unreleasedFlag, false)
listener.verifyInOrder(unreleasedFlag.id)
listener.verifyInOrder(unreleasedFlag.name)
}
@Test
@@ -162,7 +162,7 @@ class FakeFeatureFlagsTest : SysuiTestCase() {
flags.removeListener(listener)
flags.set(stringFlag, "Other")
listener.verifyInOrder(stringFlag.id)
listener.verifyInOrder(stringFlag.name)
}
@Test
@@ -175,7 +175,7 @@ class FakeFeatureFlagsTest : SysuiTestCase() {
flags.set(releasedFlag, true)
flags.set(unreleasedFlag, true)
listener.verifyInOrder(releasedFlag.id, unreleasedFlag.id)
listener.verifyInOrder(releasedFlag.name, unreleasedFlag.name)
}
@Test
@@ -191,7 +191,7 @@ class FakeFeatureFlagsTest : SysuiTestCase() {
flags.set(releasedFlag, false)
flags.set(unreleasedFlag, false)
listener.verifyInOrder(releasedFlag.id, unreleasedFlag.id)
listener.verifyInOrder(releasedFlag.name, unreleasedFlag.name)
}
@Test
@@ -204,8 +204,8 @@ class FakeFeatureFlagsTest : SysuiTestCase() {
flags.set(releasedFlag, true)
listener1.verifyInOrder(releasedFlag.id)
listener2.verifyInOrder(releasedFlag.id)
listener1.verifyInOrder(releasedFlag.name)
listener2.verifyInOrder(releasedFlag.name)
}
@Test
@@ -220,18 +220,18 @@ class FakeFeatureFlagsTest : SysuiTestCase() {
flags.removeListener(listener2)
flags.set(releasedFlag, false)
listener1.verifyInOrder(releasedFlag.id, releasedFlag.id)
listener2.verifyInOrder(releasedFlag.id)
listener1.verifyInOrder(releasedFlag.name, releasedFlag.name)
listener2.verifyInOrder(releasedFlag.name)
}
class VerifyingListener : FlagListenable.Listener {
var flagEventIds = mutableListOf<Int>()
var flagEventNames = mutableListOf<String>()
override fun onFlagChanged(event: FlagListenable.FlagEvent) {
flagEventIds.add(event.flagId)
flagEventNames.add(event.flagName)
}
fun verifyInOrder(vararg eventIds: Int) {
assertThat(flagEventIds).containsExactlyElementsIn(eventIds.asList())
fun verifyInOrder(vararg eventNames: String) {
assertThat(flagEventNames).containsExactlyElementsIn(eventNames.asList())
}
}
}

View File

@@ -23,12 +23,11 @@ import android.content.res.Resources
import android.content.res.Resources.NotFoundException
import android.test.suitebuilder.annotation.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.util.DeviceConfigProxyFake
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.settings.GlobalSettings
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
import org.junit.Assert
@@ -62,21 +61,20 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Mock
private lateinit var mockContext: Context
@Mock
private lateinit var globalSettings: GlobalSettings
@Mock
private lateinit var secureSettings: SecureSettings
@Mock
private lateinit var systemProperties: SystemPropertiesHelper
@Mock
private lateinit var resources: Resources
@Mock
private lateinit var commandRegistry: CommandRegistry
@Mock
private lateinit var restarter: Restarter
private val flagMap = mutableMapOf<Int, Flag<*>>()
private val flagMap = mutableMapOf<String, Flag<*>>()
private lateinit var broadcastReceiver: BroadcastReceiver
private lateinit var clearCacheAction: Consumer<Int>
private lateinit var clearCacheAction: Consumer<String>
private val serverFlagReader = ServerFlagReaderFake()
private val deviceConfig = DeviceConfigProxyFake()
private val teamfoodableFlagA = UnreleasedFlag(
500, name = "a", namespace = "test", teamfood = true
)
@@ -87,11 +85,13 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
flagMap.put(teamfoodableFlagA.id, teamfoodableFlagA)
flagMap.put(teamfoodableFlagB.id, teamfoodableFlagB)
flagMap.put(Flags.TEAMFOOD.name, Flags.TEAMFOOD)
flagMap.put(teamfoodableFlagA.name, teamfoodableFlagA)
flagMap.put(teamfoodableFlagB.name, teamfoodableFlagB)
mFeatureFlagsDebug = FeatureFlagsDebug(
flagManager,
mockContext,
globalSettings,
secureSettings,
systemProperties,
resources,
@@ -110,14 +110,14 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
clearCacheAction = withArgCaptor {
verify(flagManager).clearCacheAction = capture()
}
whenever(flagManager.idToSettingsKey(any())).thenAnswer { "key-${it.arguments[0]}" }
whenever(flagManager.nameToSettingsKey(any())).thenAnswer { "key-${it.arguments[0]}" }
}
@Test
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)
whenever(flagManager.readFlagValue<Boolean>(eq("3"), any())).thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq("4"), any())).thenReturn(false)
assertThat(
mFeatureFlagsDebug.isEnabled(
@@ -141,7 +141,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
mFeatureFlagsDebug.isEnabled(
ReleasedFlag(
4,
name = "3",
name = "4",
namespace = "test"
)
)
@@ -150,7 +150,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
mFeatureFlagsDebug.isEnabled(
UnreleasedFlag(
5,
name = "4",
name = "5",
namespace = "test"
)
)
@@ -159,7 +159,8 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test
fun teamFoodFlag_False() {
whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(false)
whenever(flagManager.readFlagValue<Boolean>(
eq(Flags.TEAMFOOD.name), any())).thenReturn(false)
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isFalse()
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue()
@@ -170,7 +171,8 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test
fun teamFoodFlag_True() {
whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(
eq(Flags.TEAMFOOD.name), any())).thenReturn(true)
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isTrue()
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue()
@@ -181,11 +183,12 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test
fun teamFoodFlag_Overridden() {
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagA.id), any()))
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagA.name), any()))
.thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.id), any()))
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.name), any()))
.thenReturn(false)
whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(
eq(Flags.TEAMFOOD.name), any())).thenReturn(true)
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isTrue()
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isFalse()
@@ -202,8 +205,8 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
whenever(resources.getBoolean(1004)).thenAnswer { throw NameNotFoundException() }
whenever(resources.getBoolean(1005)).thenAnswer { throw NameNotFoundException() }
whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
whenever(flagManager.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(
@@ -255,8 +258,8 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test
fun readStringFlag() {
whenever(flagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo")
whenever(flagManager.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, "1", "test", "biz"))).isEqualTo("biz")
assertThat(mFeatureFlagsDebug.getString(StringFlag(2, "2", "test", "baz"))).isEqualTo("baz")
assertThat(mFeatureFlagsDebug.getString(StringFlag(3, "3", "test", "buz"))).isEqualTo("foo")
@@ -272,9 +275,9 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
whenever(resources.getString(1005)).thenAnswer { throw NameNotFoundException() }
whenever(resources.getString(1006)).thenAnswer { throw NameNotFoundException() }
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")
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(
@@ -322,8 +325,8 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test
fun readIntFlag() {
whenever(flagManager.readFlagValue<Int>(eq(3), any())).thenReturn(22)
whenever(flagManager.readFlagValue<Int>(eq(4), any())).thenReturn(48)
whenever(flagManager.readFlagValue<Int>(eq("3"), any())).thenReturn(22)
whenever(flagManager.readFlagValue<Int>(eq("4"), any())).thenReturn(48)
assertThat(mFeatureFlagsDebug.getInt(IntFlag(1, "1", "test", 12))).isEqualTo(12)
assertThat(mFeatureFlagsDebug.getInt(IntFlag(2, "2", "test", 93))).isEqualTo(93)
assertThat(mFeatureFlagsDebug.getInt(IntFlag(3, "3", "test", 8))).isEqualTo(22)
@@ -368,12 +371,12 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
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(flagManager, secureSettings)
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(flagManager, globalSettings)
}
@Test
@@ -383,16 +386,16 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
// trying to erase an id not in the map does nothing
broadcastReceiver.onReceive(
mockContext,
Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_ID, 0)
Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_NAME, "")
)
verifyNoMoreInteractions(flagManager, secureSettings)
verifyNoMoreInteractions(flagManager, globalSettings)
// valid id with no value puts empty string in the setting
broadcastReceiver.onReceive(
mockContext,
Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_ID, 1)
Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_NAME, "1")
)
verifyPutData(1, "", numReads = 0)
verifyPutData("1", "", numReads = 0)
}
@Test
@@ -402,51 +405,51 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
addFlag(ResourceBooleanFlag(3, "3", "test", 1003))
addFlag(ResourceBooleanFlag(4, "4", "test", 1004))
setByBroadcast(1, false)
verifyPutData(1, "{\"type\":\"boolean\",\"value\":false}")
setByBroadcast("1", false)
verifyPutData("1", "{\"type\":\"boolean\",\"value\":false}")
setByBroadcast(2, true)
verifyPutData(2, "{\"type\":\"boolean\",\"value\":true}")
setByBroadcast("2", true)
verifyPutData("2", "{\"type\":\"boolean\",\"value\":true}")
setByBroadcast(3, false)
verifyPutData(3, "{\"type\":\"boolean\",\"value\":false}")
setByBroadcast("3", false)
verifyPutData("3", "{\"type\":\"boolean\",\"value\":false}")
setByBroadcast(4, true)
verifyPutData(4, "{\"type\":\"boolean\",\"value\":true}")
setByBroadcast("4", true)
verifyPutData("4", "{\"type\":\"boolean\",\"value\":true}")
}
@Test
fun setStringFlag() {
addFlag(StringFlag(1, "flag1", "1", "test"))
addFlag(StringFlag(1, "1", "1", "test"))
addFlag(ResourceStringFlag(2, "2", "test", 1002))
setByBroadcast(1, "override1")
verifyPutData(1, "{\"type\":\"string\",\"value\":\"override1\"}")
setByBroadcast("1", "override1")
verifyPutData("1", "{\"type\":\"string\",\"value\":\"override1\"}")
setByBroadcast(2, "override2")
verifyPutData(2, "{\"type\":\"string\",\"value\":\"override2\"}")
setByBroadcast("2", "override2")
verifyPutData("2", "{\"type\":\"string\",\"value\":\"override2\"}")
}
@Test
fun setFlag_ClearsCache() {
val flag1 = addFlag(StringFlag(1, "1", "test", "flag1"))
whenever(flagManager.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(flagManager).readFlagValue(eq(1), eq(StringFlagSerializer))
verify(flagManager, times(1)).readFlagValue(eq("1"), eq(StringFlagSerializer))
// hit the cache
assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("original")
verifyNoMoreInteractions(flagManager)
// set the flag
setByBroadcast(1, "new")
verifyPutData(1, "{\"type\":\"string\",\"value\":\"new\"}", numReads = 2)
whenever(flagManager.readFlagValue<String>(eq(1), any())).thenReturn("new")
setByBroadcast("1", "new")
verifyPutData("1", "{\"type\":\"string\",\"value\":\"new\"}", numReads = 2)
whenever(flagManager.readFlagValue<String>(eq("1"), any())).thenReturn("new")
assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("new")
verify(flagManager, times(3)).readFlagValue(eq(1), eq(StringFlagSerializer))
verify(flagManager, times(3)).readFlagValue(eq("1"), eq(StringFlagSerializer))
}
@Test
@@ -463,7 +466,6 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
val flag = UnreleasedFlag(100, name = "100", namespace = "test")
serverFlagReader.setFlagValue(flag.namespace, flag.name, true)
assertThat(mFeatureFlagsDebug.isEnabled(flag)).isTrue()
}
@@ -503,26 +505,26 @@ 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())
private fun verifyPutData(name: String, data: String, numReads: Int = 1) {
inOrder(flagManager, globalSettings).apply {
verify(flagManager, times(numReads)).readFlagValue(eq(name), any<FlagSerializer<*>>())
verify(flagManager).nameToSettingsKey(eq(name))
verify(globalSettings).putStringForUser(eq("key-$name"), eq(data), anyInt())
verify(flagManager).dispatchListenersAndMaybeRestart(eq(name), any())
}.verifyNoMoreInteractions()
verifyNoMoreInteractions(flagManager, secureSettings)
verifyNoMoreInteractions(flagManager, globalSettings)
}
private fun setByBroadcast(id: Int, value: Serializable?) {
private fun setByBroadcast(name: String, value: Serializable?) {
val intent = Intent(FlagManager.ACTION_SET_FLAG)
intent.putExtra(FlagManager.EXTRA_ID, id)
intent.putExtra(FlagManager.EXTRA_NAME, name)
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" }
val old = flagMap.put(flag.name, flag)
check(old == null) { "Flag ${flag.name} already registered" }
return flag
}

View File

@@ -19,7 +19,6 @@ import android.content.pm.PackageManager.NameNotFoundException
import android.content.res.Resources
import android.test.suitebuilder.annotation.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.DeviceConfigProxyFake
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertThrows
import org.junit.Before
@@ -39,9 +38,8 @@ class FeatureFlagsReleaseTest : SysuiTestCase() {
@Mock private lateinit var mResources: Resources
@Mock private lateinit var mSystemProperties: SystemPropertiesHelper
@Mock private lateinit var restarter: Restarter
private val flagMap = mutableMapOf<Int, Flag<*>>()
private val flagMap = mutableMapOf<String, Flag<*>>()
private val serverFlagReader = ServerFlagReaderFake()
private val deviceConfig = DeviceConfigProxyFake()
@Before
fun setup() {
@@ -49,7 +47,6 @@ class FeatureFlagsReleaseTest : SysuiTestCase() {
mFeatureFlagsRelease = FeatureFlagsRelease(
mResources,
mSystemProperties,
deviceConfig,
serverFlagReader,
flagMap,
restarter)

View File

@@ -32,7 +32,7 @@ class FlagCommandTest : SysuiTestCase() {
@Mock private lateinit var featureFlags: FeatureFlagsDebug
@Mock private lateinit var pw: PrintWriter
private val flagMap = mutableMapOf<Int, Flag<*>>()
private val flagMap = mutableMapOf<String, Flag<*>>()
private val flagA = UnreleasedFlag(500, "500", "test")
private val flagB = ReleasedFlag(501, "501", "test")
private val stringFlag = StringFlag(502, "502", "test", "abracadabra")
@@ -53,59 +53,59 @@ class FlagCommandTest : SysuiTestCase() {
(invocation.getArgument(0) as IntFlag).default
}
flagMap.put(flagA.id, flagA)
flagMap.put(flagB.id, flagB)
flagMap.put(stringFlag.id, stringFlag)
flagMap.put(intFlag.id, intFlag)
flagMap.put(flagA.name, flagA)
flagMap.put(flagB.name, flagB)
flagMap.put(stringFlag.name, stringFlag)
flagMap.put(intFlag.name, intFlag)
cmd = FlagCommand(featureFlags, flagMap)
}
@Test
fun readBooleanFlagCommand() {
cmd.execute(pw, listOf(flagA.id.toString()))
cmd.execute(pw, listOf(flagA.name))
Mockito.verify(featureFlags).isEnabled(flagA)
}
@Test
fun readStringFlagCommand() {
cmd.execute(pw, listOf(stringFlag.id.toString()))
cmd.execute(pw, listOf(stringFlag.name))
Mockito.verify(featureFlags).getString(stringFlag)
}
@Test
fun readIntFlag() {
cmd.execute(pw, listOf(intFlag.id.toString()))
cmd.execute(pw, listOf(intFlag.name))
Mockito.verify(featureFlags).getInt(intFlag)
}
@Test
fun setBooleanFlagCommand() {
cmd.execute(pw, listOf(flagB.id.toString(), "on"))
cmd.execute(pw, listOf(flagB.name, "on"))
Mockito.verify(featureFlags).setBooleanFlagInternal(flagB, true)
}
@Test
fun setStringFlagCommand() {
cmd.execute(pw, listOf(stringFlag.id.toString(), "set", "foobar"))
cmd.execute(pw, listOf(stringFlag.name, "set", "foobar"))
Mockito.verify(featureFlags).setStringFlagInternal(stringFlag, "foobar")
}
@Test
fun setIntFlag() {
cmd.execute(pw, listOf(intFlag.id.toString(), "put", "123"))
cmd.execute(pw, listOf(intFlag.name, "put", "123"))
Mockito.verify(featureFlags).setIntFlagInternal(intFlag, 123)
}
@Test
fun toggleBooleanFlagCommand() {
cmd.execute(pw, listOf(flagB.id.toString(), "toggle"))
cmd.execute(pw, listOf(flagB.name, "toggle"))
Mockito.verify(featureFlags).setBooleanFlagInternal(flagB, false)
}
@Test
fun eraseFlagCommand() {
cmd.execute(pw, listOf(flagA.id.toString(), "erase"))
cmd.execute(pw, listOf(flagA.name, "erase"))
Mockito.verify(featureFlags).eraseFlag(flagA)
}
}

View File

@@ -87,14 +87,14 @@ class FlagManagerTest : SysuiTestCase() {
@Test
fun testObserverClearsCache() {
val listener = mock<FlagListenable.Listener>()
val clearCacheAction = mock<Consumer<Int>>()
val clearCacheAction = mock<Consumer<String>>()
mFlagManager.clearCacheAction = clearCacheAction
mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener)
val observer = withArgCaptor<ContentObserver> {
verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
}
observer.onChange(false, flagUri(1))
verify(clearCacheAction).accept(eq(1))
verify(clearCacheAction).accept(eq("1"))
}
@Test
@@ -110,14 +110,14 @@ class FlagManagerTest : SysuiTestCase() {
val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
verify(listener1).onFlagChanged(capture())
}
assertThat(flagEvent1.flagId).isEqualTo(1)
assertThat(flagEvent1.flagName).isEqualTo("1")
verifyNoMoreInteractions(listener1, listener10)
observer.onChange(false, flagUri(10))
val flagEvent10 = withArgCaptor<FlagListenable.FlagEvent> {
verify(listener10).onFlagChanged(capture())
}
assertThat(flagEvent10.flagId).isEqualTo(10)
assertThat(flagEvent10.flagName).isEqualTo("10")
verifyNoMoreInteractions(listener1, listener10)
}
@@ -130,18 +130,18 @@ class FlagManagerTest : SysuiTestCase() {
mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener1)
mFlagManager.addListener(ReleasedFlag(10, "10", "test"), listener10)
mFlagManager.dispatchListenersAndMaybeRestart(1, null)
mFlagManager.dispatchListenersAndMaybeRestart("1", null)
val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
verify(listener1).onFlagChanged(capture())
}
assertThat(flagEvent1.flagId).isEqualTo(1)
assertThat(flagEvent1.flagName).isEqualTo("1")
verifyNoMoreInteractions(listener1, listener10)
mFlagManager.dispatchListenersAndMaybeRestart(10, null)
mFlagManager.dispatchListenersAndMaybeRestart("10", null)
val flagEvent10 = withArgCaptor<FlagListenable.FlagEvent> {
verify(listener10).onFlagChanged(capture())
}
assertThat(flagEvent10.flagId).isEqualTo(10)
assertThat(flagEvent10.flagName).isEqualTo("10")
verifyNoMoreInteractions(listener1, listener10)
}
@@ -151,25 +151,25 @@ class FlagManagerTest : SysuiTestCase() {
mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener)
mFlagManager.addListener(ReleasedFlag(10, "10", "test"), listener)
mFlagManager.dispatchListenersAndMaybeRestart(1, null)
mFlagManager.dispatchListenersAndMaybeRestart("1", null)
val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
verify(listener).onFlagChanged(capture())
}
assertThat(flagEvent1.flagId).isEqualTo(1)
assertThat(flagEvent1.flagName).isEqualTo("1")
verifyNoMoreInteractions(listener)
mFlagManager.dispatchListenersAndMaybeRestart(10, null)
mFlagManager.dispatchListenersAndMaybeRestart("10", null)
val flagEvent10 = withArgCaptor<FlagListenable.FlagEvent> {
verify(listener, times(2)).onFlagChanged(capture())
}
assertThat(flagEvent10.flagId).isEqualTo(10)
assertThat(flagEvent10.flagName).isEqualTo("10")
verifyNoMoreInteractions(listener)
}
@Test
fun testRestartWithNoListeners() {
val restartAction = mock<Consumer<Boolean>>()
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
mFlagManager.dispatchListenersAndMaybeRestart("1", restartAction)
verify(restartAction).accept(eq(false))
verifyNoMoreInteractions(restartAction)
}
@@ -180,7 +180,7 @@ class FlagManagerTest : SysuiTestCase() {
mFlagManager.addListener(ReleasedFlag(1, "1", "test")) { event ->
event.requestNoRestart()
}
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
mFlagManager.dispatchListenersAndMaybeRestart("1", restartAction)
verify(restartAction).accept(eq(true))
verifyNoMoreInteractions(restartAction)
}
@@ -191,7 +191,7 @@ class FlagManagerTest : SysuiTestCase() {
mFlagManager.addListener(ReleasedFlag(10, "10", "test")) { event ->
event.requestNoRestart()
}
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
mFlagManager.dispatchListenersAndMaybeRestart("1", restartAction)
verify(restartAction).accept(eq(false))
verifyNoMoreInteractions(restartAction)
}
@@ -205,7 +205,7 @@ class FlagManagerTest : SysuiTestCase() {
mFlagManager.addListener(ReleasedFlag(10, "10", "test")) {
// do not request
}
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
mFlagManager.dispatchListenersAndMaybeRestart("1", restartAction)
verify(restartAction).accept(eq(false))
verifyNoMoreInteractions(restartAction)
}
@@ -214,31 +214,31 @@ class FlagManagerTest : SysuiTestCase() {
fun testReadBooleanFlag() {
// test that null string returns null
whenever(mFlagSettingsHelper.getString(any())).thenReturn(null)
assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isNull()
assertThat(mFlagManager.readFlagValue("1", BooleanFlagSerializer)).isNull()
// test that empty string returns null
whenever(mFlagSettingsHelper.getString(any())).thenReturn("")
assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isNull()
assertThat(mFlagManager.readFlagValue("1", BooleanFlagSerializer)).isNull()
// test false
whenever(mFlagSettingsHelper.getString(any()))
.thenReturn("{\"type\":\"boolean\",\"value\":false}")
assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isFalse()
assertThat(mFlagManager.readFlagValue("1", BooleanFlagSerializer)).isFalse()
// test true
whenever(mFlagSettingsHelper.getString(any()))
.thenReturn("{\"type\":\"boolean\",\"value\":true}")
assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isTrue()
assertThat(mFlagManager.readFlagValue("1", BooleanFlagSerializer)).isTrue()
// Reading a value of a different type should just return null
whenever(mFlagSettingsHelper.getString(any()))
.thenReturn("{\"type\":\"string\",\"value\":\"foo\"}")
assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isNull()
assertThat(mFlagManager.readFlagValue("1", BooleanFlagSerializer)).isNull()
// Reading a value that isn't json should throw an exception
assertThrows(InvalidFlagStorageException::class.java) {
whenever(mFlagSettingsHelper.getString(any())).thenReturn("1")
mFlagManager.readFlagValue(1, BooleanFlagSerializer)
mFlagManager.readFlagValue("1", BooleanFlagSerializer)
}
}
@@ -257,31 +257,31 @@ class FlagManagerTest : SysuiTestCase() {
fun testReadStringFlag() {
// test that null string returns null
whenever(mFlagSettingsHelper.getString(any())).thenReturn(null)
assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isNull()
assertThat(mFlagManager.readFlagValue("1", StringFlagSerializer)).isNull()
// test that empty string returns null
whenever(mFlagSettingsHelper.getString(any())).thenReturn("")
assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isNull()
assertThat(mFlagManager.readFlagValue("1", StringFlagSerializer)).isNull()
// test json with the empty string value returns empty string
whenever(mFlagSettingsHelper.getString(any()))
.thenReturn("{\"type\":\"string\",\"value\":\"\"}")
assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isEqualTo("")
assertThat(mFlagManager.readFlagValue("1", StringFlagSerializer)).isEqualTo("")
// test string with value is returned
whenever(mFlagSettingsHelper.getString(any()))
.thenReturn("{\"type\":\"string\",\"value\":\"foo\"}")
assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isEqualTo("foo")
assertThat(mFlagManager.readFlagValue("1", StringFlagSerializer)).isEqualTo("foo")
// Reading a value of a different type should just return null
whenever(mFlagSettingsHelper.getString(any()))
.thenReturn("{\"type\":\"boolean\",\"value\":false}")
assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isNull()
assertThat(mFlagManager.readFlagValue("1", StringFlagSerializer)).isNull()
// Reading a value that isn't json should throw an exception
assertThrows(InvalidFlagStorageException::class.java) {
whenever(mFlagSettingsHelper.getString(any())).thenReturn("1")
mFlagManager.readFlagValue(1, StringFlagSerializer)
mFlagManager.readFlagValue("1", StringFlagSerializer)
}
}

View File

@@ -38,12 +38,6 @@ class FakeFeatureFlags : FeatureFlags {
}
}
fun set(flag: DeviceConfigBooleanFlag, value: Boolean) {
if (booleanFlags.put(flag.id, value)?.let { value != it } != false) {
notifyFlagChanged(flag)
}
}
fun set(flag: ResourceBooleanFlag, value: Boolean) {
if (booleanFlags.put(flag.id, value)?.let { value != it } != false) {
notifyFlagChanged(flag)
@@ -73,7 +67,7 @@ class FakeFeatureFlags : FeatureFlags {
listeners.forEach { listener ->
listener.onFlagChanged(
object : FlagListenable.FlagEvent {
override val flagId = flag.id
override val flagName = flag.name
override fun requestNoRestart() {}
}
)