Merge changes I29bf2065,Ic858826c,I7fe0bd12 into tm-qpr-dev

* changes:
  Define server flag names for all existing flags.
  RESTRICT AUTOMERGE Resynchronize QPR flags with Main
  Add server flag support to flags.
This commit is contained in:
Dave Mankoff
2022-11-10 01:37:23 +00:00
committed by Android (Google) Code Review
19 changed files with 639 additions and 461 deletions

View File

@@ -116,7 +116,6 @@ android_library {
"androidx.exifinterface_exifinterface", "androidx.exifinterface_exifinterface",
"androidx.test.ext.junit", "androidx.test.ext.junit",
"com.google.android.material_material", "com.google.android.material_material",
"kotlin-reflect",
"kotlinx_coroutines_android", "kotlinx_coroutines_android",
"kotlinx_coroutines", "kotlinx_coroutines",
"iconloader_base", "iconloader_base",

View File

@@ -22,9 +22,19 @@ import android.annotation.StringRes
import android.os.Parcel import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
/**
* Base interface for flags that can change value on a running device.
* @property id unique id to help identify this flag. Must be unique. This will be removed soon.
* @property teamfood Set to true to include this flag as part of the teamfood flag. This will
* be removed soon.
* @property name Used for server-side flagging where appropriate. Also used for display. No spaces.
* @property namespace The server-side namespace that this flag lives under.
*/
interface Flag<T> { interface Flag<T> {
val id: Int val id: Int
val teamfood: Boolean val teamfood: Boolean
val name: String
val namespace: String
} }
interface ParcelableFlag<T> : Flag<T>, Parcelable { interface ParcelableFlag<T> : Flag<T>, Parcelable {
@@ -38,13 +48,10 @@ interface ResourceFlag<T> : Flag<T> {
} }
interface DeviceConfigFlag<T> : Flag<T> { interface DeviceConfigFlag<T> : Flag<T> {
val name: String
val namespace: String
val default: T val default: T
} }
interface SysPropFlag<T> : Flag<T> { interface SysPropFlag<T> : Flag<T> {
val name: String
val default: T val default: T
} }
@@ -56,6 +63,8 @@ interface SysPropFlag<T> : Flag<T> {
// Consider using the "parcelize" kotlin library. // Consider using the "parcelize" kotlin library.
abstract class BooleanFlag constructor( abstract class BooleanFlag constructor(
override val id: Int, override val id: Int,
override val name: String,
override val namespace: String,
override val default: Boolean = false, override val default: Boolean = false,
override val teamfood: Boolean = false, override val teamfood: Boolean = false,
override val overridden: Boolean = false override val overridden: Boolean = false
@@ -71,6 +80,8 @@ abstract class BooleanFlag constructor(
private constructor(parcel: Parcel) : this( private constructor(parcel: Parcel) : this(
id = parcel.readInt(), id = parcel.readInt(),
name = parcel.readString(),
namespace = parcel.readString(),
default = parcel.readBoolean(), default = parcel.readBoolean(),
teamfood = parcel.readBoolean(), teamfood = parcel.readBoolean(),
overridden = parcel.readBoolean() overridden = parcel.readBoolean()
@@ -78,6 +89,8 @@ abstract class BooleanFlag constructor(
override fun writeToParcel(parcel: Parcel, flags: Int) { override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id) parcel.writeInt(id)
parcel.writeString(name)
parcel.writeString(namespace)
parcel.writeBoolean(default) parcel.writeBoolean(default)
parcel.writeBoolean(teamfood) parcel.writeBoolean(teamfood)
parcel.writeBoolean(overridden) parcel.writeBoolean(overridden)
@@ -91,20 +104,24 @@ abstract class BooleanFlag constructor(
*/ */
data class UnreleasedFlag constructor( data class UnreleasedFlag constructor(
override val id: Int, override val id: Int,
override val name: String,
override val namespace: String,
override val teamfood: Boolean = false, override val teamfood: Boolean = false,
override val overridden: Boolean = false override val overridden: Boolean = false
) : BooleanFlag(id, false, teamfood, overridden) ) : BooleanFlag(id, name, namespace, false, teamfood, overridden)
/** /**
* A Flag that is is true by default. * A Flag that is true by default.
* *
* It can be changed or overridden in any build, meaning it can be turned off if needed. * It can be changed or overridden in any build, meaning it can be turned off if needed.
*/ */
data class ReleasedFlag constructor( data class ReleasedFlag constructor(
override val id: Int, override val id: Int,
override val name: String,
override val namespace: String,
override val teamfood: Boolean = false, override val teamfood: Boolean = false,
override val overridden: Boolean = false override val overridden: Boolean = false
) : BooleanFlag(id, true, teamfood, overridden) ) : BooleanFlag(id, name, namespace, true, teamfood, overridden)
/** /**
* A Flag that reads its default values from a resource overlay instead of code. * A Flag that reads its default values from a resource overlay instead of code.
@@ -113,6 +130,8 @@ data class ReleasedFlag constructor(
*/ */
data class ResourceBooleanFlag constructor( data class ResourceBooleanFlag constructor(
override val id: Int, override val id: Int,
override val name: String,
override val namespace: String,
@BoolRes override val resourceId: Int, @BoolRes override val resourceId: Int,
override val teamfood: Boolean = false override val teamfood: Boolean = false
) : ResourceFlag<Boolean> ) : ResourceFlag<Boolean>
@@ -142,7 +161,8 @@ data class DeviceConfigBooleanFlag constructor(
data class SysPropBooleanFlag constructor( data class SysPropBooleanFlag constructor(
override val id: Int, override val id: Int,
override val name: String, override val name: String,
override val default: Boolean = false override val namespace: String,
override val default: Boolean = false,
) : SysPropFlag<Boolean> { ) : SysPropFlag<Boolean> {
// TODO(b/223379190): Teamfood not supported for sysprop flags yet. // TODO(b/223379190): Teamfood not supported for sysprop flags yet.
override val teamfood: Boolean = false override val teamfood: Boolean = false
@@ -150,6 +170,8 @@ data class SysPropBooleanFlag constructor(
data class StringFlag constructor( data class StringFlag constructor(
override val id: Int, override val id: Int,
override val name: String,
override val namespace: String,
override val default: String = "", override val default: String = "",
override val teamfood: Boolean = false, override val teamfood: Boolean = false,
override val overridden: Boolean = false override val overridden: Boolean = false
@@ -164,23 +186,31 @@ data class StringFlag constructor(
private constructor(parcel: Parcel) : this( private constructor(parcel: Parcel) : this(
id = parcel.readInt(), id = parcel.readInt(),
name = parcel.readString(),
namespace = parcel.readString(),
default = parcel.readString() ?: "" default = parcel.readString() ?: ""
) )
override fun writeToParcel(parcel: Parcel, flags: Int) { override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id) parcel.writeInt(id)
parcel.writeString(name)
parcel.writeString(namespace)
parcel.writeString(default) parcel.writeString(default)
} }
} }
data class ResourceStringFlag constructor( data class ResourceStringFlag constructor(
override val id: Int, override val id: Int,
override val name: String,
override val namespace: String,
@StringRes override val resourceId: Int, @StringRes override val resourceId: Int,
override val teamfood: Boolean = false override val teamfood: Boolean = false
) : ResourceFlag<String> ) : ResourceFlag<String>
data class IntFlag constructor( data class IntFlag constructor(
override val id: Int, override val id: Int,
override val name: String,
override val namespace: String,
override val default: Int = 0, override val default: Int = 0,
override val teamfood: Boolean = false, override val teamfood: Boolean = false,
override val overridden: Boolean = false override val overridden: Boolean = false
@@ -196,17 +226,23 @@ data class IntFlag constructor(
private constructor(parcel: Parcel) : this( private constructor(parcel: Parcel) : this(
id = parcel.readInt(), id = parcel.readInt(),
name = parcel.readString(),
namespace = parcel.readString(),
default = parcel.readInt() default = parcel.readInt()
) )
override fun writeToParcel(parcel: Parcel, flags: Int) { override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id) parcel.writeInt(id)
parcel.writeString(name)
parcel.writeString(namespace)
parcel.writeInt(default) parcel.writeInt(default)
} }
} }
data class ResourceIntFlag constructor( data class ResourceIntFlag constructor(
override val id: Int, override val id: Int,
override val name: String,
override val namespace: String,
@IntegerRes override val resourceId: Int, @IntegerRes override val resourceId: Int,
override val teamfood: Boolean = false override val teamfood: Boolean = false
) : ResourceFlag<Int> ) : ResourceFlag<Int>
@@ -215,6 +251,8 @@ data class LongFlag constructor(
override val id: Int, override val id: Int,
override val default: Long = 0, override val default: Long = 0,
override val teamfood: Boolean = false, override val teamfood: Boolean = false,
override val name: String,
override val namespace: String,
override val overridden: Boolean = false override val overridden: Boolean = false
) : ParcelableFlag<Long> { ) : ParcelableFlag<Long> {
@@ -228,17 +266,23 @@ data class LongFlag constructor(
private constructor(parcel: Parcel) : this( private constructor(parcel: Parcel) : this(
id = parcel.readInt(), id = parcel.readInt(),
name = parcel.readString(),
namespace = parcel.readString(),
default = parcel.readLong() default = parcel.readLong()
) )
override fun writeToParcel(parcel: Parcel, flags: Int) { override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id) parcel.writeInt(id)
parcel.writeString(name)
parcel.writeString(namespace)
parcel.writeLong(default) parcel.writeLong(default)
} }
} }
data class FloatFlag constructor( data class FloatFlag constructor(
override val id: Int, override val id: Int,
override val name: String,
override val namespace: String,
override val default: Float = 0f, override val default: Float = 0f,
override val teamfood: Boolean = false, override val teamfood: Boolean = false,
override val overridden: Boolean = false override val overridden: Boolean = false
@@ -254,23 +298,31 @@ data class FloatFlag constructor(
private constructor(parcel: Parcel) : this( private constructor(parcel: Parcel) : this(
id = parcel.readInt(), id = parcel.readInt(),
name = parcel.readString(),
namespace = parcel.readString(),
default = parcel.readFloat() default = parcel.readFloat()
) )
override fun writeToParcel(parcel: Parcel, flags: Int) { override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id) parcel.writeInt(id)
parcel.writeString(name)
parcel.writeString(namespace)
parcel.writeFloat(default) parcel.writeFloat(default)
} }
} }
data class ResourceFloatFlag constructor( data class ResourceFloatFlag constructor(
override val id: Int, override val id: Int,
override val name: String,
override val namespace: String,
override val resourceId: Int, override val resourceId: Int,
override val teamfood: Boolean = false override val teamfood: Boolean = false,
) : ResourceFlag<Int> ) : ResourceFlag<Int>
data class DoubleFlag constructor( data class DoubleFlag constructor(
override val id: Int, override val id: Int,
override val name: String,
override val namespace: String,
override val default: Double = 0.0, override val default: Double = 0.0,
override val teamfood: Boolean = false, override val teamfood: Boolean = false,
override val overridden: Boolean = false override val overridden: Boolean = false
@@ -286,11 +338,15 @@ data class DoubleFlag constructor(
private constructor(parcel: Parcel) : this( private constructor(parcel: Parcel) : this(
id = parcel.readInt(), id = parcel.readInt(),
name = parcel.readString(),
namespace = parcel.readString(),
default = parcel.readDouble() default = parcel.readDouble()
) )
override fun writeToParcel(parcel: Parcel, flags: Int) { override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id) parcel.writeInt(id)
parcel.writeString(name)
parcel.writeString(namespace)
parcel.writeDouble(default) parcel.writeDouble(default)
} }
} }

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.flags
import android.annotation.BoolRes
object FlagsFactory {
private val flagMap = mutableMapOf<String, Flag<*>>()
val knownFlags: Map<String, Flag<*>>
get() = flagMap
fun unreleasedFlag(
id: Int,
name: String,
namespace: String = "systemui",
teamfood: Boolean = false
): UnreleasedFlag {
val flag = UnreleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
FlagsFactory.checkForDupesAndAdd(flag)
return flag
}
fun releasedFlag(
id: Int,
name: String,
namespace: String = "systemui",
teamfood: Boolean = false
): ReleasedFlag {
val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
FlagsFactory.checkForDupesAndAdd(flag)
return flag
}
fun resourceBooleanFlag(
id: Int,
@BoolRes resourceId: Int,
name: String,
namespace: String = "systemui",
teamfood: Boolean = false
): ResourceBooleanFlag {
val flag =
ResourceBooleanFlag(
id = id,
name = name,
namespace = namespace,
resourceId = resourceId,
teamfood = teamfood
)
FlagsFactory.checkForDupesAndAdd(flag)
return flag
}
fun sysPropBooleanFlag(
id: Int,
name: String,
namespace: String = "systemui",
default: Boolean = false
): SysPropBooleanFlag {
val flag =
SysPropBooleanFlag(id = id, name = name, namespace = "systemui", default = default)
FlagsFactory.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")
}
}
flagMap[flag.name] = flag
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.flags
import android.annotation.BoolRes
object FlagsFactory {
private val flagMap = mutableMapOf<String, Flag<*>>()
val knownFlags: Map<String, Flag<*>>
get() = flagMap
fun unreleasedFlag(
id: Int,
name: String,
namespace: String = "systemui",
teamfood: Boolean = false
): UnreleasedFlag {
// Unreleased flags are always false in this build.
val flag = UnreleasedFlag(id = id, name = "", namespace = "", teamfood = false)
return flag
}
fun releasedFlag(
id: Int,
name: String,
namespace: String = "systemui",
teamfood: Boolean = false
): ReleasedFlag {
val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
flagMap[name] = flag
return flag
}
fun resourceBooleanFlag(
id: Int,
@BoolRes resourceId: Int,
name: String,
namespace: String = "systemui",
teamfood: Boolean = false
): ResourceBooleanFlag {
val flag =
ResourceBooleanFlag(
id = id,
name = name,
namespace = namespace,
resourceId = resourceId,
teamfood = teamfood
)
flagMap[name] = flag
return flag
}
fun sysPropBooleanFlag(
id: Int,
name: String,
namespace: String = "systemui",
default: Boolean = false
): SysPropBooleanFlag {
val flag =
SysPropBooleanFlag(id = id, name = name, namespace = namespace, default = default)
flagMap[name] = flag
return flag
}
}

View File

@@ -33,9 +33,6 @@ interface FeatureFlags : FlagListenable, Dumpable {
/** Returns a boolean value for the given flag. */ /** Returns a boolean value for the given flag. */
fun isEnabled(flag: ResourceBooleanFlag): Boolean fun isEnabled(flag: ResourceBooleanFlag): Boolean
/** Returns a boolean value for the given flag. */
fun isEnabled(flag: DeviceConfigBooleanFlag): Boolean
/** Returns a boolean value for the given flag. */ /** Returns a boolean value for the given flag. */
fun isEnabled(flag: SysPropBooleanFlag): Boolean fun isEnabled(flag: SysPropBooleanFlag): Boolean

View File

@@ -39,7 +39,6 @@ import androidx.annotation.Nullable;
import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.settings.SecureSettings;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -76,7 +75,6 @@ public class FeatureFlagsDebug implements FeatureFlags {
private final SecureSettings mSecureSettings; private final SecureSettings mSecureSettings;
private final Resources mResources; private final Resources mResources;
private final SystemPropertiesHelper mSystemProperties; private final SystemPropertiesHelper mSystemProperties;
private final DeviceConfigProxy mDeviceConfigProxy;
private final ServerFlagReader mServerFlagReader; private final ServerFlagReader mServerFlagReader;
private final Map<Integer, Flag<?>> mAllFlags; private final Map<Integer, Flag<?>> mAllFlags;
private final Map<Integer, Boolean> mBooleanFlagCache = new TreeMap<>(); private final Map<Integer, Boolean> mBooleanFlagCache = new TreeMap<>();
@@ -99,7 +97,6 @@ public class FeatureFlagsDebug implements FeatureFlags {
SecureSettings secureSettings, SecureSettings secureSettings,
SystemPropertiesHelper systemProperties, SystemPropertiesHelper systemProperties,
@Main Resources resources, @Main Resources resources,
DeviceConfigProxy deviceConfigProxy,
ServerFlagReader serverFlagReader, ServerFlagReader serverFlagReader,
@Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags, @Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags,
Restarter restarter) { Restarter restarter) {
@@ -108,7 +105,6 @@ public class FeatureFlagsDebug implements FeatureFlags {
mSecureSettings = secureSettings; mSecureSettings = secureSettings;
mResources = resources; mResources = resources;
mSystemProperties = systemProperties; mSystemProperties = systemProperties;
mDeviceConfigProxy = deviceConfigProxy;
mServerFlagReader = serverFlagReader; mServerFlagReader = serverFlagReader;
mAllFlags = allFlags; mAllFlags = allFlags;
mRestarter = restarter; mRestarter = restarter;
@@ -140,7 +136,7 @@ public class FeatureFlagsDebug implements FeatureFlags {
int id = flag.getId(); int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) { if (!mBooleanFlagCache.containsKey(id)) {
mBooleanFlagCache.put(id, mBooleanFlagCache.put(id,
readFlagValue(id, flag.getDefault())); readBooleanFlagInternal(flag, flag.getDefault()));
} }
return mBooleanFlagCache.get(id); return mBooleanFlagCache.get(id);
@@ -151,19 +147,7 @@ public class FeatureFlagsDebug implements FeatureFlags {
int id = flag.getId(); int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) { if (!mBooleanFlagCache.containsKey(id)) {
mBooleanFlagCache.put(id, mBooleanFlagCache.put(id,
readFlagValue(id, mResources.getBoolean(flag.getResourceId()))); readBooleanFlagInternal(flag, mResources.getBoolean(flag.getResourceId())));
}
return mBooleanFlagCache.get(id);
}
@Override
public boolean isEnabled(@NonNull DeviceConfigBooleanFlag flag) {
int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
boolean deviceConfigValue = mDeviceConfigProxy.getBoolean(flag.getNamespace(),
flag.getName(), flag.getDefault());
mBooleanFlagCache.put(id, readFlagValue(id, deviceConfigValue));
} }
return mBooleanFlagCache.get(id); return mBooleanFlagCache.get(id);
@@ -179,7 +163,7 @@ public class FeatureFlagsDebug implements FeatureFlags {
id, id,
mSystemProperties.getBoolean( mSystemProperties.getBoolean(
flag.getName(), flag.getName(),
readFlagValue(id, flag.getDefault()))); readBooleanFlagInternal(flag, flag.getDefault())));
} }
return mBooleanFlagCache.get(id); return mBooleanFlagCache.get(id);
@@ -191,7 +175,7 @@ public class FeatureFlagsDebug implements FeatureFlags {
int id = flag.getId(); int id = flag.getId();
if (!mStringFlagCache.containsKey(id)) { if (!mStringFlagCache.containsKey(id)) {
mStringFlagCache.put(id, mStringFlagCache.put(id,
readFlagValue(id, flag.getDefault(), StringFlagSerializer.INSTANCE)); readFlagValueInternal(id, flag.getDefault(), StringFlagSerializer.INSTANCE));
} }
return mStringFlagCache.get(id); return mStringFlagCache.get(id);
@@ -203,20 +187,21 @@ public class FeatureFlagsDebug implements FeatureFlags {
int id = flag.getId(); int id = flag.getId();
if (!mStringFlagCache.containsKey(id)) { if (!mStringFlagCache.containsKey(id)) {
mStringFlagCache.put(id, mStringFlagCache.put(id,
readFlagValue(id, mResources.getString(flag.getResourceId()), readFlagValueInternal(id, mResources.getString(flag.getResourceId()),
StringFlagSerializer.INSTANCE)); StringFlagSerializer.INSTANCE));
} }
return mStringFlagCache.get(id); return mStringFlagCache.get(id);
} }
@NonNull @NonNull
@Override @Override
public int getInt(@NonNull IntFlag flag) { public int getInt(@NonNull IntFlag flag) {
int id = flag.getId(); int id = flag.getId();
if (!mIntFlagCache.containsKey(id)) { if (!mIntFlagCache.containsKey(id)) {
mIntFlagCache.put(id, mIntFlagCache.put(id,
readFlagValue(id, flag.getDefault(), IntFlagSerializer.INSTANCE)); readFlagValueInternal(id, flag.getDefault(), IntFlagSerializer.INSTANCE));
} }
return mIntFlagCache.get(id); return mIntFlagCache.get(id);
@@ -228,7 +213,7 @@ public class FeatureFlagsDebug implements FeatureFlags {
int id = flag.getId(); int id = flag.getId();
if (!mIntFlagCache.containsKey(id)) { if (!mIntFlagCache.containsKey(id)) {
mIntFlagCache.put(id, mIntFlagCache.put(id,
readFlagValue(id, mResources.getInteger(flag.getResourceId()), readFlagValueInternal(id, mResources.getInteger(flag.getResourceId()),
IntFlagSerializer.INSTANCE)); IntFlagSerializer.INSTANCE));
} }
@@ -236,19 +221,23 @@ public class FeatureFlagsDebug implements FeatureFlags {
} }
/** Specific override for Boolean flags that checks against the teamfood list.*/ /** Specific override for Boolean flags that checks against the teamfood list.*/
private boolean readFlagValue(int id, boolean defaultValue) { private boolean readBooleanFlagInternal(Flag<Boolean> flag, boolean defaultValue) {
Boolean result = readBooleanFlagOverride(id); Boolean result = readBooleanFlagOverride(flag.getId());
boolean hasServerOverride = mServerFlagReader.hasOverride(id); boolean hasServerOverride = mServerFlagReader.hasOverride(
flag.getNamespace(), flag.getName());
// Only check for teamfood if the default is false // Only check for teamfood if the default is false
// and there is no server override. // and there is no server override.
if (!hasServerOverride && !defaultValue && result == null && id != Flags.TEAMFOOD.getId()) { if (!hasServerOverride
if (mAllFlags.containsKey(id) && mAllFlags.get(id).getTeamfood()) { && !defaultValue
&& result == null
&& flag.getId() != Flags.TEAMFOOD.getId()
&& flag.getTeamfood()) {
return isEnabled(Flags.TEAMFOOD); return isEnabled(Flags.TEAMFOOD);
} }
}
return result == null ? mServerFlagReader.readServerOverride(id, defaultValue) : result; return result == null ? mServerFlagReader.readServerOverride(
flag.getNamespace(), flag.getName(), defaultValue) : result;
} }
private Boolean readBooleanFlagOverride(int id) { private Boolean readBooleanFlagOverride(int id) {
@@ -256,7 +245,8 @@ public class FeatureFlagsDebug implements FeatureFlags {
} }
@NonNull @NonNull
private <T> T readFlagValue(int id, @NonNull T defaultValue, FlagSerializer<T> serializer) { private <T> T readFlagValueInternal(
int id, @NonNull T defaultValue, FlagSerializer<T> serializer) {
requireNonNull(defaultValue, "defaultValue"); requireNonNull(defaultValue, "defaultValue");
T result = readFlagValueInternal(id, serializer); T result = readFlagValueInternal(id, serializer);
return result == null ? defaultValue : result; return result == null ? defaultValue : result;
@@ -355,8 +345,6 @@ public class FeatureFlagsDebug implements FeatureFlags {
setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE); setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
} else if (flag instanceof ResourceBooleanFlag) { } else if (flag instanceof ResourceBooleanFlag) {
setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE); setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
} else if (flag instanceof DeviceConfigBooleanFlag) {
setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
} else if (flag instanceof SysPropBooleanFlag) { } else if (flag instanceof SysPropBooleanFlag) {
// Store SysProp flags in SystemProperties where they can read by outside parties. // Store SysProp flags in SystemProperties where they can read by outside parties.
mSystemProperties.setBoolean(((SysPropBooleanFlag) flag).getName(), value); mSystemProperties.setBoolean(((SysPropBooleanFlag) flag).getName(), value);
@@ -474,9 +462,6 @@ public class FeatureFlagsDebug implements FeatureFlags {
} else if (f instanceof ResourceBooleanFlag) { } else if (f instanceof ResourceBooleanFlag) {
enabled = isEnabled((ResourceBooleanFlag) f); enabled = isEnabled((ResourceBooleanFlag) f);
overridden = readBooleanFlagOverride(f.getId()) != null; overridden = readBooleanFlagOverride(f.getId()) != null;
} else if (f instanceof DeviceConfigBooleanFlag) {
enabled = isEnabled((DeviceConfigBooleanFlag) f);
overridden = false;
} else if (f instanceof SysPropBooleanFlag) { } else if (f instanceof SysPropBooleanFlag) {
// TODO(b/223379190): Teamfood not supported for sysprop flags yet. // TODO(b/223379190): Teamfood not supported for sysprop flags yet.
enabled = isEnabled((SysPropBooleanFlag) f); enabled = isEnabled((SysPropBooleanFlag) f);
@@ -489,9 +474,11 @@ public class FeatureFlagsDebug implements FeatureFlags {
} }
if (enabled) { if (enabled) {
return new ReleasedFlag(f.getId(), teamfood, overridden); return new ReleasedFlag(
f.getId(), f.getName(), f.getNamespace(), teamfood, overridden);
} else { } else {
return new UnreleasedFlag(f.getId(), teamfood, overridden); return new UnreleasedFlag(
f.getId(), f.getName(), f.getNamespace(), teamfood, overridden);
} }
} }
}; };

View File

@@ -101,7 +101,7 @@ public class FeatureFlagsRelease implements FeatureFlags {
@Override @Override
public boolean isEnabled(@NotNull ReleasedFlag flag) { public boolean isEnabled(@NotNull ReleasedFlag flag) {
return mServerFlagReader.readServerOverride(flag.getId(), true); return mServerFlagReader.readServerOverride(flag.getNamespace(), flag.getName(), true);
} }
@Override @Override
@@ -114,18 +114,6 @@ public class FeatureFlagsRelease implements FeatureFlags {
return mBooleanCache.valueAt(cacheIndex); return mBooleanCache.valueAt(cacheIndex);
} }
@Override
public boolean isEnabled(@NonNull DeviceConfigBooleanFlag flag) {
int cacheIndex = mBooleanCache.indexOfKey(flag.getId());
if (cacheIndex < 0) {
boolean deviceConfigValue = mDeviceConfigProxy.getBoolean(flag.getNamespace(),
flag.getName(), flag.getDefault());
return isEnabled(flag.getId(), deviceConfigValue);
}
return mBooleanCache.valueAt(cacheIndex);
}
@Override @Override
public boolean isEnabled(SysPropBooleanFlag flag) { public boolean isEnabled(SysPropBooleanFlag flag) {
int cacheIndex = mBooleanCache.indexOfKey(flag.getId()); int cacheIndex = mBooleanCache.indexOfKey(flag.getId());
@@ -180,10 +168,10 @@ public class FeatureFlagsRelease implements FeatureFlags {
@Override @Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("can override: false"); pw.println("can override: false");
Map<Integer, Flag<?>> knownFlags = Flags.collectFlags(); Map<String, Flag<?>> knownFlags = FlagsFactory.INSTANCE.getKnownFlags();
for (Map.Entry<Integer, Flag<?>> idToFlag : knownFlags.entrySet()) { for (Map.Entry<String, Flag<?>> nameToFlag : knownFlags.entrySet()) {
int id = idToFlag.getKey(); Flag<?> flag = nameToFlag.getValue();
Flag<?> flag = idToFlag.getValue(); int id = flag.getId();
boolean def = false; boolean def = false;
if (mBooleanCache.indexOfKey(flag.getId()) < 0) { if (mBooleanCache.indexOfKey(flag.getId()) < 0) {
if (flag instanceof SysPropBooleanFlag) { if (flag instanceof SysPropBooleanFlag) {

View File

@@ -229,7 +229,7 @@ public class FlagCommand implements Command {
} }
private int flagNameToId(String flagName) { private int flagNameToId(String flagName) {
Map<String, Flag<?>> flagFields = Flags.getFlagFields(); Map<String, Flag<?>> flagFields = FlagsFactory.INSTANCE.getKnownFlags();
for (String fieldName : flagFields.keySet()) { for (String fieldName : flagFields.keySet()) {
if (flagName.equals(fieldName)) { if (flagName.equals(fieldName)) {
return flagFields.get(fieldName).getId(); return flagFields.get(fieldName).getId();
@@ -240,7 +240,7 @@ public class FlagCommand implements Command {
} }
private void printKnownFlags(PrintWriter pw) { private void printKnownFlags(PrintWriter pw) {
Map<String, Flag<?>> fields = Flags.getFlagFields(); Map<String, Flag<?>> fields = FlagsFactory.INSTANCE.getKnownFlags();
int longestFieldName = 0; int longestFieldName = 0;
for (String fieldName : fields.keySet()) { for (String fieldName : fields.keySet()) {

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2021 The Android Open Source Project * Copyright (C) 2022 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -17,12 +17,11 @@ package com.android.systemui.flags
import android.provider.DeviceConfig import android.provider.DeviceConfig
import com.android.internal.annotations.Keep import com.android.internal.annotations.Keep
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.R import com.android.systemui.R
import kotlin.reflect.KClass import com.android.systemui.flags.FlagsFactory.releasedFlag
import kotlin.reflect.full.declaredMembers import com.android.systemui.flags.FlagsFactory.resourceBooleanFlag
import kotlin.reflect.full.isSubclassOf import com.android.systemui.flags.FlagsFactory.sysPropBooleanFlag
import kotlin.reflect.full.staticProperties import com.android.systemui.flags.FlagsFactory.unreleasedFlag
/** /**
* List of [Flag] objects for use in SystemUI. * List of [Flag] objects for use in SystemUI.
@@ -37,41 +36,51 @@ import kotlin.reflect.full.staticProperties
* See [FeatureFlagsDebug] for instructions on flipping the flags via adb. * See [FeatureFlagsDebug] for instructions on flipping the flags via adb.
*/ */
object Flags { object Flags {
@JvmField val TEAMFOOD = UnreleasedFlag(1) @JvmField val TEAMFOOD = unreleasedFlag(1, "teamfood")
// 100 - notification // 100 - notification
// TODO(b/254512751): Tracking Bug // TODO(b/254512751): Tracking Bug
val NOTIFICATION_PIPELINE_DEVELOPER_LOGGING = UnreleasedFlag(103) val NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
unreleasedFlag(103, "notification_pipeline_developer_logging")
// TODO(b/254512732): Tracking Bug // TODO(b/254512732): Tracking Bug
@JvmField val NSSL_DEBUG_LINES = UnreleasedFlag(105) @JvmField val NSSL_DEBUG_LINES = unreleasedFlag(105, "nssl_debug_lines")
// TODO(b/254512505): Tracking Bug // TODO(b/254512505): Tracking Bug
@JvmField val NSSL_DEBUG_REMOVE_ANIMATION = UnreleasedFlag(106) @JvmField val NSSL_DEBUG_REMOVE_ANIMATION = unreleasedFlag(106, "nssl_debug_remove_animation")
// TODO(b/254512624): Tracking Bug // TODO(b/254512624): Tracking Bug
@JvmField @JvmField
val NOTIFICATION_DRAG_TO_CONTENTS = val NOTIFICATION_DRAG_TO_CONTENTS =
ResourceBooleanFlag(108, R.bool.config_notificationToContents) resourceBooleanFlag(
108,
R.bool.config_notificationToContents,
"notification_drag_to_contents"
)
// TODO(b/254512517): Tracking Bug // TODO(b/254512517): Tracking Bug
val FSI_REQUIRES_KEYGUARD = UnreleasedFlag(110, teamfood = true) val FSI_REQUIRES_KEYGUARD = unreleasedFlag(110, "fsi_requires_keyguard", teamfood = true)
// TODO(b/254512538): Tracking Bug // TODO(b/254512538): Tracking Bug
val INSTANT_VOICE_REPLY = UnreleasedFlag(111, teamfood = true) val INSTANT_VOICE_REPLY = unreleasedFlag(111, "instant_voice_reply", teamfood = true)
// TODO(b/254512425): Tracking Bug // TODO(b/254512425): Tracking Bug
val NOTIFICATION_MEMORY_MONITOR_ENABLED = ReleasedFlag(112) val NOTIFICATION_MEMORY_MONITOR_ENABLED =
releasedFlag(112, "notification_memory_monitor_enabled")
// TODO(b/254512731): Tracking Bug // TODO(b/254512731): Tracking Bug
@JvmField val NOTIFICATION_DISMISSAL_FADE = UnreleasedFlag(113, teamfood = true) @JvmField
val STABILITY_INDEX_FIX = UnreleasedFlag(114, teamfood = true) val NOTIFICATION_DISMISSAL_FADE =
val SEMI_STABLE_SORT = UnreleasedFlag(115, teamfood = true) unreleasedFlag(113, "notification_dismissal_fade", teamfood = true)
val STABILITY_INDEX_FIX = unreleasedFlag(114, "stability_index_fix", teamfood = true)
val SEMI_STABLE_SORT = unreleasedFlag(115, "semi_stable_sort", teamfood = true)
@JvmField val NOTIFICATION_GROUP_CORNER = UnreleasedFlag(116, teamfood = true) @JvmField
val NOTIFICATION_GROUP_CORNER =
unreleasedFlag(116, "notification_group_corner", teamfood = true)
// TODO(b/257506350): Tracking Bug // TODO(b/257506350): Tracking Bug
val FSI_CHROME = UnreleasedFlag(117) val FSI_CHROME = unreleasedFlag(117, "fsi_chrome")
// next id: 118 // next id: 118
@@ -80,25 +89,27 @@ object Flags {
// public static final BooleanFlag KEYGUARD_LAYOUT = // public static final BooleanFlag KEYGUARD_LAYOUT =
// new BooleanFlag(200, true); // new BooleanFlag(200, true);
// TODO(b/254512713): Tracking Bug // TODO(b/254512713): Tracking Bug
@JvmField val LOCKSCREEN_ANIMATIONS = ReleasedFlag(201) @JvmField val LOCKSCREEN_ANIMATIONS = releasedFlag(201, "lockscreen_animations")
// TODO(b/254512750): Tracking Bug // TODO(b/254512750): Tracking Bug
val NEW_UNLOCK_SWIPE_ANIMATION = ReleasedFlag(202) val NEW_UNLOCK_SWIPE_ANIMATION = releasedFlag(202, "new_unlock_swipe_animation")
val CHARGING_RIPPLE = ResourceBooleanFlag(203, R.bool.flag_charging_ripple) val CHARGING_RIPPLE = resourceBooleanFlag(203, R.bool.flag_charging_ripple, "charging_ripple")
// TODO(b/254512281): Tracking Bug // TODO(b/254512281): Tracking Bug
@JvmField @JvmField
val BOUNCER_USER_SWITCHER = ResourceBooleanFlag(204, R.bool.config_enableBouncerUserSwitcher) val BOUNCER_USER_SWITCHER =
resourceBooleanFlag(204, R.bool.config_enableBouncerUserSwitcher, "bouncer_user_switcher")
// TODO(b/254512676): Tracking Bug // TODO(b/254512676): Tracking Bug
@JvmField val LOCKSCREEN_CUSTOM_CLOCKS = UnreleasedFlag(207, teamfood = true) @JvmField
val LOCKSCREEN_CUSTOM_CLOCKS = unreleasedFlag(207, "lockscreen_custom_clocks", teamfood = true)
/** /**
* Flag to enable the usage of the new bouncer data source. This is a refactor of and eventual * Flag to enable the usage of the new bouncer data source. This is a refactor of and eventual
* replacement of KeyguardBouncer.java. * replacement of KeyguardBouncer.java.
*/ */
// TODO(b/254512385): Tracking Bug // TODO(b/254512385): Tracking Bug
@JvmField val MODERN_BOUNCER = ReleasedFlag(208) @JvmField val MODERN_BOUNCER = releasedFlag(208, "modern_bouncer")
/** /**
* Whether the user interactor and repository should use `UserSwitcherController`. * Whether the user interactor and repository should use `UserSwitcherController`.
@@ -107,7 +118,8 @@ object Flags {
* framework APIs. * framework APIs.
*/ */
// TODO(b/254513286): Tracking Bug // TODO(b/254513286): Tracking Bug
val USER_INTERACTOR_AND_REPO_USE_CONTROLLER = UnreleasedFlag(210) val USER_INTERACTOR_AND_REPO_USE_CONTROLLER =
unreleasedFlag(210, "user_interactor_and_repo_use_controller")
/** /**
* Whether `UserSwitcherController` should use the user interactor. * Whether `UserSwitcherController` should use the user interactor.
@@ -119,20 +131,24 @@ object Flags {
* would created a cycle between controller -> interactor -> controller. * would created a cycle between controller -> interactor -> controller.
*/ */
// TODO(b/254513102): Tracking Bug // TODO(b/254513102): Tracking Bug
val USER_CONTROLLER_USES_INTERACTOR = ReleasedFlag(211) val USER_CONTROLLER_USES_INTERACTOR = releasedFlag(211, "user_controller_uses_interactor")
/** /**
* Whether the clock on a wide lock screen should use the new "stepping" animation for moving * Whether the clock on a wide lock screen should use the new "stepping" animation for moving
* the digits when the clock moves. * the digits when the clock moves.
*/ */
@JvmField val STEP_CLOCK_ANIMATION = UnreleasedFlag(212) @JvmField val STEP_CLOCK_ANIMATION = unreleasedFlag(212, "step_clock_animation")
/** /**
* Migration from the legacy isDozing/dozeAmount paths to the new KeyguardTransitionRepository * Migration from the legacy isDozing/dozeAmount paths to the new KeyguardTransitionRepository
* will occur in stages. This is one stage of many to come. * will occur in stages. This is one stage of many to come.
*/ */
// TODO(b/255607168): Tracking Bug // TODO(b/255607168): Tracking Bug
@JvmField val DOZING_MIGRATION_1 = UnreleasedFlag(213) @JvmField val DOZING_MIGRATION_1 = unreleasedFlag(213, "dozing_migration_1")
@JvmField val NEW_ELLIPSE_DETECTION = unreleasedFlag(214, "new_ellipse_detection")
@JvmField val NEW_UDFPS_OVERLAY = unreleasedFlag(215, "new_udfps_overlay")
/** /**
* Whether to enable the code powering customizable lock screen quick affordances. * Whether to enable the code powering customizable lock screen quick affordances.
@@ -140,267 +156,256 @@ object Flags {
* Note that this flag does not enable individual implementations of quick affordances like the * Note that this flag does not enable individual implementations of quick affordances like the
* new camera quick affordance. Look for individual flags for those. * new camera quick affordance. Look for individual flags for those.
*/ */
@JvmField val CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES = UnreleasedFlag(214, teamfood = false) @JvmField
val CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES =
unreleasedFlag(216, "customizable_lock_screen_quick_affordances", teamfood = false)
// 300 - power menu // 300 - power menu
// TODO(b/254512600): Tracking Bug // TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = ReleasedFlag(300) @JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
// 400 - smartspace // 400 - smartspace
// TODO(b/254513100): Tracking Bug // TODO(b/254513100): Tracking Bug
val SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED = ReleasedFlag(401) val SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED =
val SMARTSPACE = ResourceBooleanFlag(402, R.bool.flag_smartspace) releasedFlag(401, "smartspace_shared_element_transition_enabled")
val SMARTSPACE = resourceBooleanFlag(402, R.bool.flag_smartspace, "smartspace")
// 500 - quick settings // 500 - quick settings
// TODO(b/254512321): Tracking Bug // TODO(b/254512321): Tracking Bug
@JvmField val COMBINED_QS_HEADERS = ReleasedFlag(501) @JvmField val COMBINED_QS_HEADERS = releasedFlag(501, "combined_qs_headers")
val PEOPLE_TILE = ResourceBooleanFlag(502, R.bool.flag_conversations) val PEOPLE_TILE = resourceBooleanFlag(502, R.bool.flag_conversations, "people_tile")
@JvmField @JvmField
val QS_USER_DETAIL_SHORTCUT = val QS_USER_DETAIL_SHORTCUT =
ResourceBooleanFlag(503, R.bool.flag_lockscreen_qs_user_detail_shortcut) resourceBooleanFlag(
503,
R.bool.flag_lockscreen_qs_user_detail_shortcut,
"qs_user_detail_shortcut"
)
// TODO(b/254512747): Tracking Bug // TODO(b/254512747): Tracking Bug
val NEW_HEADER = ReleasedFlag(505) val NEW_HEADER = releasedFlag(505, "new_header")
// TODO(b/254512383): Tracking Bug // TODO(b/254512383): Tracking Bug
@JvmField @JvmField
val FULL_SCREEN_USER_SWITCHER = val FULL_SCREEN_USER_SWITCHER =
ResourceBooleanFlag(506, R.bool.config_enableFullscreenUserSwitcher) resourceBooleanFlag(
506,
R.bool.config_enableFullscreenUserSwitcher,
"full_screen_user_switcher"
)
// TODO(b/254512678): Tracking Bug // TODO(b/254512678): Tracking Bug
@JvmField val NEW_FOOTER_ACTIONS = ReleasedFlag(507) @JvmField val NEW_FOOTER_ACTIONS = releasedFlag(507, "new_footer_actions")
// TODO(b/244064524): Tracking Bug
@JvmField
val QS_SECONDARY_DATA_SUB_INFO =
unreleasedFlag(508, "qs_secondary_data_sub_info", teamfood = true)
// 600- status bar // 600- status bar
// TODO(b/254513246): Tracking Bug // TODO(b/254513246): Tracking Bug
val STATUS_BAR_USER_SWITCHER = ResourceBooleanFlag(602, R.bool.flag_user_switcher_chip) val STATUS_BAR_USER_SWITCHER =
resourceBooleanFlag(602, R.bool.flag_user_switcher_chip, "status_bar_user_switcher")
// TODO(b/254512623): Tracking Bug // TODO(b/254512623): Tracking Bug
@Deprecated("Replaced by mobile and wifi specific flags.") @Deprecated("Replaced by mobile and wifi specific flags.")
val NEW_STATUS_BAR_PIPELINE_BACKEND = UnreleasedFlag(604, teamfood = false) val NEW_STATUS_BAR_PIPELINE_BACKEND =
unreleasedFlag(604, "new_status_bar_pipeline_backend", teamfood = false)
// TODO(b/254512660): Tracking Bug // TODO(b/254512660): Tracking Bug
@Deprecated("Replaced by mobile and wifi specific flags.") @Deprecated("Replaced by mobile and wifi specific flags.")
val NEW_STATUS_BAR_PIPELINE_FRONTEND = UnreleasedFlag(605, teamfood = false) val NEW_STATUS_BAR_PIPELINE_FRONTEND =
unreleasedFlag(605, "new_status_bar_pipeline_frontend", teamfood = false)
// TODO(b/256614753): Tracking Bug // TODO(b/256614753): Tracking Bug
val NEW_STATUS_BAR_MOBILE_ICONS = UnreleasedFlag(606) val NEW_STATUS_BAR_MOBILE_ICONS = unreleasedFlag(606, "new_status_bar_mobile_icons")
// TODO(b/256614210): Tracking Bug // TODO(b/256614210): Tracking Bug
val NEW_STATUS_BAR_WIFI_ICON = UnreleasedFlag(607) val NEW_STATUS_BAR_WIFI_ICON = unreleasedFlag(607, "new_status_bar_wifi_icon")
// TODO(b/256614751): Tracking Bug // TODO(b/256614751): Tracking Bug
val NEW_STATUS_BAR_MOBILE_ICONS_BACKEND = UnreleasedFlag(608) val NEW_STATUS_BAR_MOBILE_ICONS_BACKEND =
unreleasedFlag(608, "new_status_bar_mobile_icons_backend")
// TODO(b/256613548): Tracking Bug // TODO(b/256613548): Tracking Bug
val NEW_STATUS_BAR_WIFI_ICON_BACKEND = UnreleasedFlag(609) val NEW_STATUS_BAR_WIFI_ICON_BACKEND = unreleasedFlag(609, "new_status_bar_wifi_icon_backend")
// 700 - dialer/calls // 700 - dialer/calls
// TODO(b/254512734): Tracking Bug // TODO(b/254512734): Tracking Bug
val ONGOING_CALL_STATUS_BAR_CHIP = ReleasedFlag(700) val ONGOING_CALL_STATUS_BAR_CHIP = releasedFlag(700, "ongoing_call_status_bar_chip")
// TODO(b/254512681): Tracking Bug // TODO(b/254512681): Tracking Bug
val ONGOING_CALL_IN_IMMERSIVE = ReleasedFlag(701) val ONGOING_CALL_IN_IMMERSIVE = releasedFlag(701, "ongoing_call_in_immersive")
// TODO(b/254512753): Tracking Bug // TODO(b/254512753): Tracking Bug
val ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP = ReleasedFlag(702) val ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP = releasedFlag(702, "ongoing_call_in_immersive_chip_tap")
// 800 - general visual/theme // 800 - general visual/theme
@JvmField val MONET = ResourceBooleanFlag(800, R.bool.flag_monet) @JvmField val MONET = resourceBooleanFlag(800, R.bool.flag_monet, "monet")
// 801 - region sampling // 801 - region sampling
// TODO(b/254512848): Tracking Bug // TODO(b/254512848): Tracking Bug
val REGION_SAMPLING = UnreleasedFlag(801) val REGION_SAMPLING = unreleasedFlag(801, "region_sampling")
// 802 - wallpaper rendering // 802 - wallpaper rendering
// TODO(b/254512923): Tracking Bug // TODO(b/254512923): Tracking Bug
@JvmField val USE_CANVAS_RENDERER = ReleasedFlag(802) @JvmField val USE_CANVAS_RENDERER = unreleasedFlag(802, "use_canvas_renderer")
// 803 - screen contents translation // 803 - screen contents translation
// TODO(b/254513187): Tracking Bug // TODO(b/254513187): Tracking Bug
val SCREEN_CONTENTS_TRANSLATION = UnreleasedFlag(803) val SCREEN_CONTENTS_TRANSLATION = unreleasedFlag(803, "screen_contents_translation")
// 804 - monochromatic themes // 804 - monochromatic themes
@JvmField @JvmField
val MONOCHROMATIC_THEMES = SysPropBooleanFlag(804, "persist.sysui.monochromatic", false) val MONOCHROMATIC_THEMES =
sysPropBooleanFlag(804, "persist.sysui.monochromatic", default = false)
// 900 - media // 900 - media
// TODO(b/254512697): Tracking Bug // TODO(b/254512697): Tracking Bug
val MEDIA_TAP_TO_TRANSFER = ReleasedFlag(900) val MEDIA_TAP_TO_TRANSFER = releasedFlag(900, "media_tap_to_transfer")
// TODO(b/254512502): Tracking Bug // TODO(b/254512502): Tracking Bug
val MEDIA_SESSION_ACTIONS = UnreleasedFlag(901) val MEDIA_SESSION_ACTIONS = unreleasedFlag(901, "media_session_actions")
// TODO(b/254512726): Tracking Bug // TODO(b/254512726): Tracking Bug
val MEDIA_NEARBY_DEVICES = ReleasedFlag(903) val MEDIA_NEARBY_DEVICES = releasedFlag(903, "media_nearby_devices")
// TODO(b/254512695): Tracking Bug // TODO(b/254512695): Tracking Bug
val MEDIA_MUTE_AWAIT = ReleasedFlag(904) val MEDIA_MUTE_AWAIT = releasedFlag(904, "media_mute_await")
// TODO(b/254512654): Tracking Bug // TODO(b/254512654): Tracking Bug
@JvmField val DREAM_MEDIA_COMPLICATION = UnreleasedFlag(905) @JvmField val DREAM_MEDIA_COMPLICATION = unreleasedFlag(905, "dream_media_complication")
// TODO(b/254512673): Tracking Bug // TODO(b/254512673): Tracking Bug
@JvmField val DREAM_MEDIA_TAP_TO_OPEN = UnreleasedFlag(906) @JvmField val DREAM_MEDIA_TAP_TO_OPEN = unreleasedFlag(906, "dream_media_tap_to_open")
// TODO(b/254513168): Tracking Bug // TODO(b/254513168): Tracking Bug
@JvmField val UMO_SURFACE_RIPPLE = UnreleasedFlag(907) @JvmField val UMO_SURFACE_RIPPLE = unreleasedFlag(907, "umo_surface_ripple")
// 1000 - dock // 1000 - dock
val SIMULATE_DOCK_THROUGH_CHARGING = ReleasedFlag(1000) val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging")
// TODO(b/254512758): Tracking Bug // TODO(b/254512758): Tracking Bug
@JvmField val ROUNDED_BOX_RIPPLE = ReleasedFlag(1002) @JvmField val ROUNDED_BOX_RIPPLE = releasedFlag(1002, "rounded_box_ripple")
// 1100 - windowing // 1100 - windowing
@Keep @Keep
@JvmField @JvmField
val WM_ENABLE_SHELL_TRANSITIONS = val WM_ENABLE_SHELL_TRANSITIONS =
SysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", false) sysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", default = false)
// TODO(b/254513207): Tracking Bug // TODO(b/254513207): Tracking Bug
@Keep @Keep
@JvmField @JvmField
val WM_ENABLE_PARTIAL_SCREEN_SHARING = val WM_ENABLE_PARTIAL_SCREEN_SHARING =
DeviceConfigBooleanFlag( unreleasedFlag(
1102, 1102,
"record_task_content", name = "record_task_content",
DeviceConfig.NAMESPACE_WINDOW_MANAGER, namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER,
false,
teamfood = true teamfood = true
) )
// TODO(b/254512674): Tracking Bug // TODO(b/254512674): Tracking Bug
@Keep @Keep
@JvmField @JvmField
val HIDE_NAVBAR_WINDOW = SysPropBooleanFlag(1103, "persist.wm.debug.hide_navbar_window", false) val HIDE_NAVBAR_WINDOW =
sysPropBooleanFlag(1103, "persist.wm.debug.hide_navbar_window", default = false)
@Keep @Keep
@JvmField @JvmField
val WM_DESKTOP_WINDOWING = SysPropBooleanFlag(1104, "persist.wm.debug.desktop_mode", false) val WM_DESKTOP_WINDOWING =
sysPropBooleanFlag(1104, "persist.wm.debug.desktop_mode", default = false)
@Keep @Keep
@JvmField @JvmField
val WM_CAPTION_ON_SHELL = SysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", false) val WM_CAPTION_ON_SHELL =
sysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", default = false)
@Keep @Keep
@JvmField @JvmField
val ENABLE_FLING_TO_DISMISS_BUBBLE = val ENABLE_FLING_TO_DISMISS_BUBBLE =
SysPropBooleanFlag(1108, "persist.wm.debug.fling_to_dismiss_bubble", true) sysPropBooleanFlag(1108, "persist.wm.debug.fling_to_dismiss_bubble", default = true)
@Keep @Keep
@JvmField @JvmField
val ENABLE_FLING_TO_DISMISS_PIP = val ENABLE_FLING_TO_DISMISS_PIP =
SysPropBooleanFlag(1109, "persist.wm.debug.fling_to_dismiss_pip", true) sysPropBooleanFlag(1109, "persist.wm.debug.fling_to_dismiss_pip", default = true)
@Keep @Keep
@JvmField @JvmField
val ENABLE_PIP_KEEP_CLEAR_ALGORITHM = val ENABLE_PIP_KEEP_CLEAR_ALGORITHM =
SysPropBooleanFlag(1110, "persist.wm.debug.enable_pip_keep_clear_algorithm", false) sysPropBooleanFlag(
1110,
"persist.wm.debug.enable_pip_keep_clear_algorithm",
default = false
)
// TODO(b/256873975): Tracking Bug // TODO(b/256873975): Tracking Bug
@JvmField @Keep val WM_BUBBLE_BAR = UnreleasedFlag(1111) @JvmField @Keep val WM_BUBBLE_BAR = unreleasedFlag(1111, "wm_bubble_bar")
// 1200 - predictive back // 1200 - predictive back
@Keep @Keep
@JvmField @JvmField
val WM_ENABLE_PREDICTIVE_BACK = val WM_ENABLE_PREDICTIVE_BACK =
SysPropBooleanFlag(1200, "persist.wm.debug.predictive_back", true) sysPropBooleanFlag(1200, "persist.wm.debug.predictive_back", default = true)
@Keep @Keep
@JvmField @JvmField
val WM_ENABLE_PREDICTIVE_BACK_ANIM = val WM_ENABLE_PREDICTIVE_BACK_ANIM =
SysPropBooleanFlag(1201, "persist.wm.debug.predictive_back_anim", false) sysPropBooleanFlag(1201, "persist.wm.debug.predictive_back_anim", default = false)
@Keep @Keep
@JvmField @JvmField
val WM_ALWAYS_ENFORCE_PREDICTIVE_BACK = val WM_ALWAYS_ENFORCE_PREDICTIVE_BACK =
SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false) sysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", default = false)
// TODO(b/254512728): Tracking Bug // TODO(b/254512728): Tracking Bug
@JvmField val NEW_BACK_AFFORDANCE = UnreleasedFlag(1203, teamfood = false) @JvmField
val NEW_BACK_AFFORDANCE = unreleasedFlag(1203, "new_back_affordance", teamfood = false)
// 1300 - screenshots // 1300 - screenshots
// TODO(b/254512719): Tracking Bug // TODO(b/254512719): Tracking Bug
@JvmField val SCREENSHOT_REQUEST_PROCESSOR = UnreleasedFlag(1300, teamfood = true) @JvmField
val SCREENSHOT_REQUEST_PROCESSOR =
unreleasedFlag(1300, "screenshot_request_processor", teamfood = true)
// TODO(b/254513155): Tracking Bug // TODO(b/254513155): Tracking Bug
@JvmField val SCREENSHOT_WORK_PROFILE_POLICY = UnreleasedFlag(1301) @JvmField
val SCREENSHOT_WORK_PROFILE_POLICY = unreleasedFlag(1301, "screenshot_work_profile_policy")
// 1400 - columbus // 1400 - columbus
// TODO(b/254512756): Tracking Bug // TODO(b/254512756): Tracking Bug
val QUICK_TAP_IN_PCC = ReleasedFlag(1400) val QUICK_TAP_IN_PCC = releasedFlag(1400, "quick_tap_in_pcc")
// 1500 - chooser // 1500 - chooser
// TODO(b/254512507): Tracking Bug // TODO(b/254512507): Tracking Bug
val CHOOSER_UNBUNDLED = UnreleasedFlag(1500, teamfood = true) val CHOOSER_UNBUNDLED = unreleasedFlag(1500, "chooser_unbundled", teamfood = true)
// 1600 - accessibility
@JvmField
val A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS =
unreleasedFlag(1600, "a11y_floating_menu_fling_spring_animations")
// 1700 - clipboard // 1700 - clipboard
@JvmField val CLIPBOARD_OVERLAY_REFACTOR = UnreleasedFlag(1700, teamfood = true) @JvmField
@JvmField val CLIPBOARD_REMOTE_BEHAVIOR = UnreleasedFlag(1701) val CLIPBOARD_OVERLAY_REFACTOR =
unreleasedFlag(1700, "clipboard_overlay_refactor", teamfood = true)
@JvmField val CLIPBOARD_REMOTE_BEHAVIOR = unreleasedFlag(1701, "clipboard_remote_behavior")
// 1800 - shade container // 1800 - shade container
@JvmField val LEAVE_SHADE_OPEN_FOR_BUGREPORT = UnreleasedFlag(1800, teamfood = true) @JvmField
val LEAVE_SHADE_OPEN_FOR_BUGREPORT =
unreleasedFlag(1800, "leave_shade_open_for_bugreport", teamfood = true)
// 1900 - note task // 1900 - note task
@JvmField val NOTE_TASKS = SysPropBooleanFlag(1900, "persist.sysui.debug.note_tasks") @JvmField val NOTE_TASKS = sysPropBooleanFlag(1900, "persist.sysui.debug.note_tasks")
// 2000 - device controls // 2000 - device controls
@Keep @JvmField val USE_APP_PANELS = UnreleasedFlag(2000, teamfood = true) @Keep @JvmField val USE_APP_PANELS = unreleasedFlag(2000, "use_app_panels", teamfood = true)
// 2100 - Falsing Manager // 2100 - Falsing Manager
@JvmField val FALSING_FOR_LONG_TAPS = ReleasedFlag(2100) @JvmField val FALSING_FOR_LONG_TAPS = releasedFlag(2100, "falsing_for_long_taps")
// Pay no attention to the reflection behind the curtain.
// ========================== Curtain ==========================
// | |
// | . . . . . . . . . . . . . . . . . . . |
@JvmStatic
fun collectFlags(): Map<Int, Flag<*>> {
return flagFields.mapKeys { field -> field.value.id }
}
// | . . . . . . . . . . . . . . . . . . . |
@JvmStatic
val flagFields: Map<String, Flag<*>>
get() = collectFlagsInClass(Flags)
@VisibleForTesting
fun collectFlagsInClass(instance: Any): Map<String, Flag<*>> {
val cls = instance::class
val javaPropNames = cls.java.fields.map { it.name }
val props = cls.declaredMembers
val staticProps = cls.staticProperties
val staticPropNames = staticProps.map { it.name }
return props
.mapNotNull { property ->
if ((property.returnType.classifier as KClass<*>).isSubclassOf(Flag::class)) {
// Fields with @JvmStatic should be accessed via java mechanisms
if (javaPropNames.contains(property.name)) {
property.name to cls.java.getField(property.name)[null] as Flag<*>
// Fields with @Keep but not @JvmField. Don't do this.
} else if (staticPropNames.contains(property.name)) {
// The below code causes access violation exceptions. I don't know why.
// property.name to (property.call() as Flag<*>)
// property.name to (staticProps.find { it.name == property.name }!!
// .getter.call() as Flag<*>)
throw java.lang.RuntimeException(
"The {$property.name} flag needs @JvmField"
)
// Everything else. Skip the `get` prefixed fields that kotlin adds.
} else if (property.name.subSequence(0, 3) != "get") {
property.name to (property.call(instance) as Flag<*>)
} else {
null
}
} else {
null
}
}
.toMap()
}
// | |
// \_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/
} }

View File

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

View File

@@ -27,11 +27,10 @@ import javax.inject.Inject
interface ServerFlagReader { interface ServerFlagReader {
/** Returns true if there is a server-side setting stored. */ /** Returns true if there is a server-side setting stored. */
fun hasOverride(flagId: Int): Boolean fun hasOverride(namespace: String, name: String): Boolean
/** Returns any stored server-side setting or the default if not set. */ /** Returns any stored server-side setting or the default if not set. */
fun readServerOverride(flagId: Int, default: Boolean): Boolean fun readServerOverride(namespace: String, name: String, default: Boolean): Boolean
/** Register a listener for changes to any of the passed in flags. */ /** Register a listener for changes to any of the passed in flags. */
fun listenForChanges(values: Collection<Flag<*>>, listener: ChangeListener) fun listenForChanges(values: Collection<Flag<*>>, listener: ChangeListener)
@@ -68,19 +67,19 @@ class ServerFlagReaderImpl @Inject constructor(
} }
} }
override fun hasOverride(flagId: Int): Boolean = override fun hasOverride(namespace: String, name: String): Boolean =
deviceConfig.getProperty( !namespace.isBlank() && !name.isBlank() && deviceConfig.getProperty(
namespace, namespace,
getServerOverrideName(flagId) name
) != null ) != null
override fun readServerOverride(flagId: Int, default: Boolean): Boolean {
return deviceConfig.getBoolean( override fun readServerOverride(namespace: String, name: String, default: Boolean): Boolean =
!namespace.isBlank() && !name.isBlank() && deviceConfig.getBoolean(
namespace, namespace,
getServerOverrideName(flagId), name,
default default
) )
}
override fun listenForChanges( override fun listenForChanges(
flags: Collection<Flag<*>>, flags: Collection<Flag<*>>,
@@ -121,24 +120,24 @@ interface ServerFlagReaderModule {
} }
class ServerFlagReaderFake : ServerFlagReader { class ServerFlagReaderFake : ServerFlagReader {
private val flagMap: MutableMap<Int, Boolean> = mutableMapOf() private val flagMap: MutableMap<String, Boolean> = mutableMapOf()
private val listeners = private val listeners =
mutableListOf<Pair<ServerFlagReader.ChangeListener, Collection<Flag<*>>>>() mutableListOf<Pair<ServerFlagReader.ChangeListener, Collection<Flag<*>>>>()
override fun hasOverride(flagId: Int): Boolean { override fun hasOverride(namespace: String, name: String): Boolean {
return flagMap.containsKey(flagId) return flagMap.containsKey(name)
} }
override fun readServerOverride(flagId: Int, default: Boolean): Boolean { override fun readServerOverride(namespace: String, name: String, default: Boolean): Boolean {
return flagMap.getOrDefault(flagId, default) return flagMap.getOrDefault(name, default)
} }
fun setFlagValue(flagId: Int, value: Boolean) { fun setFlagValue(namespace: String, name: String, value: Boolean) {
flagMap.put(flagId, value) flagMap.put(name, value)
for ((listener, flags) in listeners) { for ((listener, flags) in listeners) {
flagLoop@ for (flag in flags) { flagLoop@ for (flag in flags) {
if (flagId == flag.id) { if (name == flag.name) {
listener.onChange() listener.onChange()
break@flagLoop break@flagLoop
} }
@@ -146,8 +145,8 @@ class ServerFlagReaderFake : ServerFlagReader {
} }
} }
fun eraseFlag(flagId: Int) { fun eraseFlag(namespace: String, name: String) {
flagMap.remove(flagId) flagMap.remove(name)
} }
override fun listenForChanges( override fun listenForChanges(

View File

@@ -20,7 +20,6 @@ import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import java.lang.IllegalStateException
import org.junit.Assert.fail import org.junit.Assert.fail
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@@ -29,12 +28,12 @@ import org.junit.runner.RunWith
@RunWith(AndroidTestingRunner::class) @RunWith(AndroidTestingRunner::class)
class FakeFeatureFlagsTest : SysuiTestCase() { class FakeFeatureFlagsTest : SysuiTestCase() {
private val unreleasedFlag = UnreleasedFlag(-1000) private val unreleasedFlag = UnreleasedFlag(-1000, "-1000", "test")
private val releasedFlag = ReleasedFlag(-1001) private val releasedFlag = ReleasedFlag(-1001, "-1001", "test")
private val stringFlag = StringFlag(-1002) private val stringFlag = StringFlag(-1002, "-1002", "test")
private val resourceBooleanFlag = ResourceBooleanFlag(-1003, resourceId = -1) private val resourceBooleanFlag = ResourceBooleanFlag(-1003, "-1003", "test", resourceId = -1)
private val resourceStringFlag = ResourceStringFlag(-1004, resourceId = -1) private val resourceStringFlag = ResourceStringFlag(-1004, "-1004", "test", resourceId = -1)
private val sysPropBooleanFlag = SysPropBooleanFlag(-1005, name = "test") private val sysPropBooleanFlag = SysPropBooleanFlag(-1005, "test", "test")
/** /**
* FakeFeatureFlags does not honor any default values. All flags which are accessed must be * FakeFeatureFlags does not honor any default values. All flags which are accessed must be
@@ -47,7 +46,7 @@ class FakeFeatureFlagsTest : SysuiTestCase() {
assertThat(flags.isEnabled(Flags.TEAMFOOD)).isFalse() assertThat(flags.isEnabled(Flags.TEAMFOOD)).isFalse()
fail("Expected an exception when accessing an unspecified flag.") fail("Expected an exception when accessing an unspecified flag.")
} catch (ex: IllegalStateException) { } catch (ex: IllegalStateException) {
assertThat(ex.message).contains("TEAMFOOD") assertThat(ex.message).contains("id=1")
} }
try { try {
assertThat(flags.isEnabled(unreleasedFlag)).isFalse() assertThat(flags.isEnabled(unreleasedFlag)).isFalse()

View File

@@ -31,10 +31,6 @@ import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.withArgCaptor import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.Serializable
import java.io.StringWriter
import java.util.function.Consumer
import org.junit.Assert import org.junit.Assert
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@@ -46,8 +42,12 @@ import org.mockito.Mockito.inOrder
import org.mockito.Mockito.times import org.mockito.Mockito.times
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
import java.io.PrintWriter
import java.io.Serializable
import java.io.StringWriter
import java.util.function.Consumer
import org.mockito.Mockito.`when` as whenever
/** /**
* NOTE: This test is for the version of FeatureFlagManager in src-debug, which allows overriding * NOTE: This test is for the version of FeatureFlagManager in src-debug, which allows overriding
@@ -57,21 +57,32 @@ import org.mockito.MockitoAnnotations
class FeatureFlagsDebugTest : SysuiTestCase() { class FeatureFlagsDebugTest : SysuiTestCase() {
private lateinit var mFeatureFlagsDebug: FeatureFlagsDebug private lateinit var mFeatureFlagsDebug: FeatureFlagsDebug
@Mock private lateinit var flagManager: FlagManager @Mock
@Mock private lateinit var mockContext: Context private lateinit var flagManager: FlagManager
@Mock private lateinit var secureSettings: SecureSettings @Mock
@Mock private lateinit var systemProperties: SystemPropertiesHelper private lateinit var mockContext: Context
@Mock private lateinit var resources: Resources @Mock
@Mock private lateinit var commandRegistry: CommandRegistry private lateinit var secureSettings: SecureSettings
@Mock private lateinit var restarter: Restarter @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<Int, Flag<*>>()
private lateinit var broadcastReceiver: BroadcastReceiver private lateinit var broadcastReceiver: BroadcastReceiver
private lateinit var clearCacheAction: Consumer<Int> private lateinit var clearCacheAction: Consumer<Int>
private val serverFlagReader = ServerFlagReaderFake() private val serverFlagReader = ServerFlagReaderFake()
private val deviceConfig = DeviceConfigProxyFake() private val deviceConfig = DeviceConfigProxyFake()
private val teamfoodableFlagA = UnreleasedFlag(500, true) private val teamfoodableFlagA = UnreleasedFlag(
private val teamfoodableFlagB = ReleasedFlag(501, true) 500, name = "a", namespace = "test", teamfood = true
)
private val teamfoodableFlagB = ReleasedFlag(
501, name = "b", namespace = "test", teamfood = true
)
@Before @Before
fun setup() { fun setup() {
@@ -84,7 +95,6 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
secureSettings, secureSettings,
systemProperties, systemProperties,
resources, resources,
deviceConfig,
serverFlagReader, serverFlagReader,
flagMap, flagMap,
restarter restarter
@@ -92,8 +102,10 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
mFeatureFlagsDebug.init() mFeatureFlagsDebug.init()
verify(flagManager).onSettingsChangedAction = any() verify(flagManager).onSettingsChangedAction = any()
broadcastReceiver = withArgCaptor { broadcastReceiver = withArgCaptor {
verify(mockContext).registerReceiver(capture(), any(), nullable(), nullable(), verify(mockContext).registerReceiver(
any()) capture(), any(), nullable(), nullable(),
any()
)
} }
clearCacheAction = withArgCaptor { clearCacheAction = withArgCaptor {
verify(flagManager).clearCacheAction = capture() verify(flagManager).clearCacheAction = capture()
@@ -107,10 +119,42 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true) whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq(4), any())).thenReturn(false) whenever(flagManager.readFlagValue<Boolean>(eq(4), any())).thenReturn(false)
assertThat(mFeatureFlagsDebug.isEnabled(ReleasedFlag(2))).isTrue() assertThat(
assertThat(mFeatureFlagsDebug.isEnabled(UnreleasedFlag(3))).isTrue() mFeatureFlagsDebug.isEnabled(
assertThat(mFeatureFlagsDebug.isEnabled(ReleasedFlag(4))).isFalse() ReleasedFlag(
assertThat(mFeatureFlagsDebug.isEnabled(UnreleasedFlag(5))).isFalse() 2,
name = "2",
namespace = "test"
)
)
).isTrue()
assertThat(
mFeatureFlagsDebug.isEnabled(
UnreleasedFlag(
3,
name = "3",
namespace = "test"
)
)
).isTrue()
assertThat(
mFeatureFlagsDebug.isEnabled(
ReleasedFlag(
4,
name = "3",
namespace = "test"
)
)
).isFalse()
assertThat(
mFeatureFlagsDebug.isEnabled(
UnreleasedFlag(
5,
name = "4",
namespace = "test"
)
)
).isFalse()
} }
@Test @Test
@@ -161,17 +205,26 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true) whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq(5), any())).thenReturn(false) whenever(flagManager.readFlagValue<Boolean>(eq(5), any())).thenReturn(false)
assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(1, 1001))).isFalse() assertThat(
assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(2, 1002))).isTrue() mFeatureFlagsDebug.isEnabled(
assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(3, 1003))).isTrue() ResourceBooleanFlag(
1,
"1",
"test",
1001
)
)
).isFalse()
assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(2, "2", "test", 1002))).isTrue()
assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(3, "3", "test", 1003))).isTrue()
Assert.assertThrows(NameNotFoundException::class.java) { Assert.assertThrows(NameNotFoundException::class.java) {
mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(4, 1004)) mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(4, "4", "test", 1004))
} }
// Test that resource is loaded (and validated) even when the setting is set. // Test that resource is loaded (and validated) even when the setting is set.
// This prevents developers from not noticing when they reference an invalid resource. // This prevents developers from not noticing when they reference an invalid resource.
Assert.assertThrows(NameNotFoundException::class.java) { Assert.assertThrows(NameNotFoundException::class.java) {
mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(5, 1005)) mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(5, "5", "test", 1005))
} }
} }
@@ -184,36 +237,30 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
return@thenAnswer it.getArgument(1) return@thenAnswer it.getArgument(1)
} }
assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(1, "a"))).isFalse() assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(1, "a", "test"))).isFalse()
assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(2, "b"))).isTrue() assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(2, "b", "test"))).isTrue()
assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(3, "c", true))).isTrue() assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(3, "c", "test", true))).isTrue()
assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(4, "d", false))).isFalse() assertThat(
assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(5, "e"))).isFalse() mFeatureFlagsDebug.isEnabled(
} SysPropBooleanFlag(
4,
@Test "d",
fun readDeviceConfigBooleanFlag() { "test",
val namespace = "test_namespace" false
deviceConfig.setProperty(namespace, "a", "true", false) )
deviceConfig.setProperty(namespace, "b", "false", false) )
deviceConfig.setProperty(namespace, "c", null, false) ).isFalse()
assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(5, "e", "test"))).isFalse()
assertThat(mFeatureFlagsDebug.isEnabled(DeviceConfigBooleanFlag(1, "a", namespace)))
.isTrue()
assertThat(mFeatureFlagsDebug.isEnabled(DeviceConfigBooleanFlag(2, "b", namespace)))
.isFalse()
assertThat(mFeatureFlagsDebug.isEnabled(DeviceConfigBooleanFlag(3, "c", namespace)))
.isFalse()
} }
@Test @Test
fun readStringFlag() { fun readStringFlag() {
whenever(flagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo") whenever(flagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo")
whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar") whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar")
assertThat(mFeatureFlagsDebug.getString(StringFlag(1, "biz"))).isEqualTo("biz") assertThat(mFeatureFlagsDebug.getString(StringFlag(1, "1", "test", "biz"))).isEqualTo("biz")
assertThat(mFeatureFlagsDebug.getString(StringFlag(2, "baz"))).isEqualTo("baz") assertThat(mFeatureFlagsDebug.getString(StringFlag(2, "2", "test", "baz"))).isEqualTo("baz")
assertThat(mFeatureFlagsDebug.getString(StringFlag(3, "buz"))).isEqualTo("foo") assertThat(mFeatureFlagsDebug.getString(StringFlag(3, "3", "test", "buz"))).isEqualTo("foo")
assertThat(mFeatureFlagsDebug.getString(StringFlag(4, "buz"))).isEqualTo("bar") assertThat(mFeatureFlagsDebug.getString(StringFlag(4, "4", "test", "buz"))).isEqualTo("bar")
} }
@Test @Test
@@ -229,20 +276,47 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("override4") whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("override4")
whenever(flagManager.readFlagValue<String>(eq(6), any())).thenReturn("override6") whenever(flagManager.readFlagValue<String>(eq(6), any())).thenReturn("override6")
assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(1, 1001))).isEqualTo("") assertThat(
assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(2, 1002))).isEqualTo("resource2") mFeatureFlagsDebug.getString(
assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(3, 1003))).isEqualTo("override3") ResourceStringFlag(
1,
"1",
"test",
1001
)
)
).isEqualTo("")
assertThat(
mFeatureFlagsDebug.getString(
ResourceStringFlag(
2,
"2",
"test",
1002
)
)
).isEqualTo("resource2")
assertThat(
mFeatureFlagsDebug.getString(
ResourceStringFlag(
3,
"3",
"test",
1003
)
)
).isEqualTo("override3")
Assert.assertThrows(NullPointerException::class.java) { Assert.assertThrows(NullPointerException::class.java) {
mFeatureFlagsDebug.getString(ResourceStringFlag(4, 1004)) mFeatureFlagsDebug.getString(ResourceStringFlag(4, "4", "test", 1004))
} }
Assert.assertThrows(NameNotFoundException::class.java) { Assert.assertThrows(NameNotFoundException::class.java) {
mFeatureFlagsDebug.getString(ResourceStringFlag(5, 1005)) mFeatureFlagsDebug.getString(ResourceStringFlag(5, "5", "test", 1005))
} }
// Test that resource is loaded (and validated) even when the setting is set. // Test that resource is loaded (and validated) even when the setting is set.
// This prevents developers from not noticing when they reference an invalid resource. // This prevents developers from not noticing when they reference an invalid resource.
Assert.assertThrows(NameNotFoundException::class.java) { Assert.assertThrows(NameNotFoundException::class.java) {
mFeatureFlagsDebug.getString(ResourceStringFlag(6, 1005)) mFeatureFlagsDebug.getString(ResourceStringFlag(6, "6", "test", 1005))
} }
} }
@@ -250,10 +324,10 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
fun readIntFlag() { fun readIntFlag() {
whenever(flagManager.readFlagValue<Int>(eq(3), any())).thenReturn(22) whenever(flagManager.readFlagValue<Int>(eq(3), any())).thenReturn(22)
whenever(flagManager.readFlagValue<Int>(eq(4), any())).thenReturn(48) whenever(flagManager.readFlagValue<Int>(eq(4), any())).thenReturn(48)
assertThat(mFeatureFlagsDebug.getInt(IntFlag(1, 12))).isEqualTo(12) assertThat(mFeatureFlagsDebug.getInt(IntFlag(1, "1", "test", 12))).isEqualTo(12)
assertThat(mFeatureFlagsDebug.getInt(IntFlag(2, 93))).isEqualTo(93) assertThat(mFeatureFlagsDebug.getInt(IntFlag(2, "2", "test", 93))).isEqualTo(93)
assertThat(mFeatureFlagsDebug.getInt(IntFlag(3, 8))).isEqualTo(22) assertThat(mFeatureFlagsDebug.getInt(IntFlag(3, "3", "test", 8))).isEqualTo(22)
assertThat(mFeatureFlagsDebug.getInt(IntFlag(4, 234))).isEqualTo(48) assertThat(mFeatureFlagsDebug.getInt(IntFlag(4, "4", "test", 234))).isEqualTo(48)
} }
@Test @Test
@@ -269,26 +343,26 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
whenever(flagManager.readFlagValue<Int>(eq(4), any())).thenReturn(500) whenever(flagManager.readFlagValue<Int>(eq(4), any())).thenReturn(500)
whenever(flagManager.readFlagValue<Int>(eq(5), any())).thenReturn(9519) whenever(flagManager.readFlagValue<Int>(eq(5), any())).thenReturn(9519)
assertThat(mFeatureFlagsDebug.getInt(ResourceIntFlag(1, 1001))).isEqualTo(88) assertThat(mFeatureFlagsDebug.getInt(ResourceIntFlag(1, "1", "test", 1001))).isEqualTo(88)
assertThat(mFeatureFlagsDebug.getInt(ResourceIntFlag(2, 1002))).isEqualTo(61) assertThat(mFeatureFlagsDebug.getInt(ResourceIntFlag(2, "2", "test", 1002))).isEqualTo(61)
assertThat(mFeatureFlagsDebug.getInt(ResourceIntFlag(3, 1003))).isEqualTo(20) assertThat(mFeatureFlagsDebug.getInt(ResourceIntFlag(3, "3", "test", 1003))).isEqualTo(20)
Assert.assertThrows(NotFoundException::class.java) { Assert.assertThrows(NotFoundException::class.java) {
mFeatureFlagsDebug.getInt(ResourceIntFlag(4, 1004)) mFeatureFlagsDebug.getInt(ResourceIntFlag(4, "4", "test", 1004))
} }
// Test that resource is loaded (and validated) even when the setting is set. // Test that resource is loaded (and validated) even when the setting is set.
// This prevents developers from not noticing when they reference an invalid resource. // This prevents developers from not noticing when they reference an invalid resource.
Assert.assertThrows(NotFoundException::class.java) { Assert.assertThrows(NotFoundException::class.java) {
mFeatureFlagsDebug.getInt(ResourceIntFlag(5, 1005)) mFeatureFlagsDebug.getInt(ResourceIntFlag(5, "5", "test", 1005))
} }
} }
@Test @Test
fun broadcastReceiver_IgnoresInvalidData() { fun broadcastReceiver_IgnoresInvalidData() {
addFlag(UnreleasedFlag(1)) addFlag(UnreleasedFlag(1, "1", "test"))
addFlag(ResourceBooleanFlag(2, 1002)) addFlag(ResourceBooleanFlag(2, "2", "test", 1002))
addFlag(StringFlag(3, "flag3")) addFlag(StringFlag(3, "3", "test", "flag3"))
addFlag(ResourceStringFlag(4, 1004)) addFlag(ResourceStringFlag(4, "4", "test", 1004))
broadcastReceiver.onReceive(mockContext, null) broadcastReceiver.onReceive(mockContext, null)
broadcastReceiver.onReceive(mockContext, Intent()) broadcastReceiver.onReceive(mockContext, Intent())
@@ -304,7 +378,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test @Test
fun intentWithId_NoValueKeyClears() { fun intentWithId_NoValueKeyClears() {
addFlag(UnreleasedFlag(1)) addFlag(UnreleasedFlag(1, name = "1", namespace = "test"))
// trying to erase an id not in the map does nothing // trying to erase an id not in the map does nothing
broadcastReceiver.onReceive( broadcastReceiver.onReceive(
@@ -323,10 +397,10 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test @Test
fun setBooleanFlag() { fun setBooleanFlag() {
addFlag(UnreleasedFlag(1)) addFlag(UnreleasedFlag(1, "1", "test"))
addFlag(UnreleasedFlag(2)) addFlag(UnreleasedFlag(2, "2", "test"))
addFlag(ResourceBooleanFlag(3, 1003)) addFlag(ResourceBooleanFlag(3, "3", "test", 1003))
addFlag(ResourceBooleanFlag(4, 1004)) addFlag(ResourceBooleanFlag(4, "4", "test", 1004))
setByBroadcast(1, false) setByBroadcast(1, false)
verifyPutData(1, "{\"type\":\"boolean\",\"value\":false}") verifyPutData(1, "{\"type\":\"boolean\",\"value\":false}")
@@ -343,8 +417,8 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test @Test
fun setStringFlag() { fun setStringFlag() {
addFlag(StringFlag(1, "flag1")) addFlag(StringFlag(1, "flag1", "1", "test"))
addFlag(ResourceStringFlag(2, 1002)) addFlag(ResourceStringFlag(2, "2", "test", 1002))
setByBroadcast(1, "override1") setByBroadcast(1, "override1")
verifyPutData(1, "{\"type\":\"string\",\"value\":\"override1\"}") verifyPutData(1, "{\"type\":\"string\",\"value\":\"override1\"}")
@@ -355,7 +429,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test @Test
fun setFlag_ClearsCache() { fun setFlag_ClearsCache() {
val flag1 = addFlag(StringFlag(1, "flag1")) 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 // gets the flag & cache it
@@ -377,31 +451,31 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test @Test
fun serverSide_Overrides_MakesFalse() { fun serverSide_Overrides_MakesFalse() {
val flag = ReleasedFlag(100) val flag = ReleasedFlag(100, "100", "test")
serverFlagReader.setFlagValue(flag.id, false) serverFlagReader.setFlagValue(flag.namespace, flag.name, false)
assertThat(mFeatureFlagsDebug.isEnabled(flag)).isFalse() assertThat(mFeatureFlagsDebug.isEnabled(flag)).isFalse()
} }
@Test @Test
fun serverSide_Overrides_MakesTrue() { fun serverSide_Overrides_MakesTrue() {
val flag = UnreleasedFlag(100) val flag = UnreleasedFlag(100, name = "100", namespace = "test")
serverFlagReader.setFlagValue(flag.id, true) serverFlagReader.setFlagValue(flag.namespace, flag.name, true)
assertThat(mFeatureFlagsDebug.isEnabled(flag)).isTrue() assertThat(mFeatureFlagsDebug.isEnabled(flag)).isTrue()
} }
@Test @Test
fun dumpFormat() { fun dumpFormat() {
val flag1 = ReleasedFlag(1) val flag1 = ReleasedFlag(1, "1", "test")
val flag2 = ResourceBooleanFlag(2, 1002) val flag2 = ResourceBooleanFlag(2, "2", "test", 1002)
val flag3 = UnreleasedFlag(3) val flag3 = UnreleasedFlag(3, "3", "test")
val flag4 = StringFlag(4, "") val flag4 = StringFlag(4, "4", "test", "")
val flag5 = StringFlag(5, "flag5default") val flag5 = StringFlag(5, "5", "test", "flag5default")
val flag6 = ResourceStringFlag(6, 1006) val flag6 = ResourceStringFlag(6, "6", "test", 1006)
val flag7 = ResourceStringFlag(7, 1007) val flag7 = ResourceStringFlag(7, "7", "test", 1007)
whenever(resources.getBoolean(1002)).thenReturn(true) whenever(resources.getBoolean(1002)).thenReturn(true)
whenever(resources.getString(1006)).thenReturn("resource1006") whenever(resources.getString(1006)).thenReturn("resource1006")

View File

@@ -25,8 +25,8 @@ import org.junit.Assert.assertThrows
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
/** /**
* NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow * NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
@@ -59,7 +59,9 @@ class FeatureFlagsReleaseTest : SysuiTestCase() {
fun testBooleanResourceFlag() { fun testBooleanResourceFlag() {
val flagId = 213 val flagId = 213
val flagResourceId = 3 val flagResourceId = 3
val flag = ResourceBooleanFlag(flagId, flagResourceId) val flagName = "213"
val flagNamespace = "test"
val flag = ResourceBooleanFlag(flagId, flagName, flagNamespace, flagResourceId)
whenever(mResources.getBoolean(flagResourceId)).thenReturn(true) whenever(mResources.getBoolean(flagResourceId)).thenReturn(true)
assertThat(mFeatureFlagsRelease.isEnabled(flag)).isTrue() assertThat(mFeatureFlagsRelease.isEnabled(flag)).isTrue()
} }
@@ -71,57 +73,45 @@ class FeatureFlagsReleaseTest : SysuiTestCase() {
whenever(mResources.getString(1003)).thenReturn(null) whenever(mResources.getString(1003)).thenReturn(null)
whenever(mResources.getString(1004)).thenAnswer { throw NameNotFoundException() } whenever(mResources.getString(1004)).thenAnswer { throw NameNotFoundException() }
assertThat(mFeatureFlagsRelease.getString(ResourceStringFlag(1, 1001))).isEqualTo("") assertThat(mFeatureFlagsRelease.getString(
assertThat(mFeatureFlagsRelease.getString(ResourceStringFlag(2, 1002))).isEqualTo("res2") ResourceStringFlag(1, "1", "test", 1001))).isEqualTo("")
assertThat(mFeatureFlagsRelease.getString(
ResourceStringFlag(2, "2", "test", 1002))).isEqualTo("res2")
assertThrows(NullPointerException::class.java) { assertThrows(NullPointerException::class.java) {
mFeatureFlagsRelease.getString(ResourceStringFlag(3, 1003)) mFeatureFlagsRelease.getString(ResourceStringFlag(3, "3", "test", 1003))
} }
assertThrows(NameNotFoundException::class.java) { assertThrows(NameNotFoundException::class.java) {
mFeatureFlagsRelease.getString(ResourceStringFlag(4, 1004)) mFeatureFlagsRelease.getString(ResourceStringFlag(4, "4", "test", 1004))
} }
} }
@Test
fun testReadDeviceConfigBooleanFlag() {
val namespace = "test_namespace"
deviceConfig.setProperty(namespace, "a", "true", false)
deviceConfig.setProperty(namespace, "b", "false", false)
deviceConfig.setProperty(namespace, "c", null, false)
assertThat(mFeatureFlagsRelease.isEnabled(DeviceConfigBooleanFlag(1, "a", namespace)))
.isTrue()
assertThat(mFeatureFlagsRelease.isEnabled(DeviceConfigBooleanFlag(2, "b", namespace)))
.isFalse()
assertThat(mFeatureFlagsRelease.isEnabled(DeviceConfigBooleanFlag(3, "c", namespace)))
.isFalse()
}
@Test @Test
fun testSysPropBooleanFlag() { fun testSysPropBooleanFlag() {
val flagId = 213 val flagId = 213
val flagName = "sys_prop_flag" val flagName = "sys_prop_flag"
val flagNamespace = "test"
val flagDefault = true val flagDefault = true
val flag = SysPropBooleanFlag(flagId, flagName, flagDefault) val flag = SysPropBooleanFlag(flagId, flagName, flagNamespace, flagDefault)
whenever(mSystemProperties.getBoolean(flagName, flagDefault)).thenReturn(flagDefault) whenever(mSystemProperties.getBoolean(flagName, flagDefault)).thenReturn(flagDefault)
assertThat(mFeatureFlagsRelease.isEnabled(flag)).isEqualTo(flagDefault) assertThat(mFeatureFlagsRelease.isEnabled(flag)).isEqualTo(flagDefault)
} }
@Test @Test
fun serverSide_OverridesReleased_MakesFalse() { fun serverSide_OverridesReleased_MakesFalse() {
val flag = ReleasedFlag(100) val flag = ReleasedFlag(100, "100", "test")
serverFlagReader.setFlagValue(flag.id, false) serverFlagReader.setFlagValue(flag.namespace, flag.name, false)
assertThat(mFeatureFlagsRelease.isEnabled(flag)).isFalse() assertThat(mFeatureFlagsRelease.isEnabled(flag)).isFalse()
} }
@Test @Test
fun serverSide_OverridesUnreleased_Ignored() { fun serverSide_OverridesUnreleased_Ignored() {
val flag = UnreleasedFlag(100) val flag = UnreleasedFlag(100, "100", "test")
serverFlagReader.setFlagValue(flag.id, true) serverFlagReader.setFlagValue(flag.namespace, flag.name, true)
assertThat(mFeatureFlagsRelease.isEnabled(flag)).isFalse() assertThat(mFeatureFlagsRelease.isEnabled(flag)).isFalse()
} }

View File

@@ -33,10 +33,10 @@ class FlagCommandTest : SysuiTestCase() {
@Mock private lateinit var featureFlags: FeatureFlagsDebug @Mock private lateinit var featureFlags: FeatureFlagsDebug
@Mock private lateinit var pw: PrintWriter @Mock private lateinit var pw: PrintWriter
private val flagMap = mutableMapOf<Int, Flag<*>>() private val flagMap = mutableMapOf<Int, Flag<*>>()
private val flagA = UnreleasedFlag(500) private val flagA = UnreleasedFlag(500, "500", "test")
private val flagB = ReleasedFlag(501) private val flagB = ReleasedFlag(501, "501", "test")
private val stringFlag = StringFlag(502, "abracadabra") private val stringFlag = StringFlag(502, "502", "test", "abracadabra")
private val intFlag = IntFlag(503, 12) private val intFlag = IntFlag(503, "503", "test", 12)
private lateinit var cmd: FlagCommand private lateinit var cmd: FlagCommand

View File

@@ -64,14 +64,14 @@ class FlagManagerTest : SysuiTestCase() {
verifyNoMoreInteractions(mFlagSettingsHelper) verifyNoMoreInteractions(mFlagSettingsHelper)
// adding the first listener registers the observer // adding the first listener registers the observer
mFlagManager.addListener(ReleasedFlag(1), listener1) mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener1)
val observer = withArgCaptor<ContentObserver> { val observer = withArgCaptor<ContentObserver> {
verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture()) verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
} }
verifyNoMoreInteractions(mFlagSettingsHelper) verifyNoMoreInteractions(mFlagSettingsHelper)
// adding another listener does nothing // adding another listener does nothing
mFlagManager.addListener(ReleasedFlag(2), listener2) mFlagManager.addListener(ReleasedFlag(2, "2", "test"), listener2)
verifyNoMoreInteractions(mFlagSettingsHelper) verifyNoMoreInteractions(mFlagSettingsHelper)
// removing the original listener does nothing with second one still present // removing the original listener does nothing with second one still present
@@ -89,7 +89,7 @@ class FlagManagerTest : SysuiTestCase() {
val listener = mock<FlagListenable.Listener>() val listener = mock<FlagListenable.Listener>()
val clearCacheAction = mock<Consumer<Int>>() val clearCacheAction = mock<Consumer<Int>>()
mFlagManager.clearCacheAction = clearCacheAction mFlagManager.clearCacheAction = clearCacheAction
mFlagManager.addListener(ReleasedFlag(1), listener) mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener)
val observer = withArgCaptor<ContentObserver> { val observer = withArgCaptor<ContentObserver> {
verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture()) verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
} }
@@ -101,8 +101,8 @@ class FlagManagerTest : SysuiTestCase() {
fun testObserverInvokesListeners() { fun testObserverInvokesListeners() {
val listener1 = mock<FlagListenable.Listener>() val listener1 = mock<FlagListenable.Listener>()
val listener10 = mock<FlagListenable.Listener>() val listener10 = mock<FlagListenable.Listener>()
mFlagManager.addListener(ReleasedFlag(1), listener1) mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener1)
mFlagManager.addListener(ReleasedFlag(10), listener10) mFlagManager.addListener(ReleasedFlag(10, "10", "test"), listener10)
val observer = withArgCaptor<ContentObserver> { val observer = withArgCaptor<ContentObserver> {
verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture()) verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
} }
@@ -127,8 +127,8 @@ class FlagManagerTest : SysuiTestCase() {
fun testOnlySpecificFlagListenerIsInvoked() { fun testOnlySpecificFlagListenerIsInvoked() {
val listener1 = mock<FlagListenable.Listener>() val listener1 = mock<FlagListenable.Listener>()
val listener10 = mock<FlagListenable.Listener>() val listener10 = mock<FlagListenable.Listener>()
mFlagManager.addListener(ReleasedFlag(1), listener1) mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener1)
mFlagManager.addListener(ReleasedFlag(10), listener10) mFlagManager.addListener(ReleasedFlag(10, "10", "test"), listener10)
mFlagManager.dispatchListenersAndMaybeRestart(1, null) mFlagManager.dispatchListenersAndMaybeRestart(1, null)
val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> { val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
@@ -148,8 +148,8 @@ class FlagManagerTest : SysuiTestCase() {
@Test @Test
fun testSameListenerCanBeUsedForMultipleFlags() { fun testSameListenerCanBeUsedForMultipleFlags() {
val listener = mock<FlagListenable.Listener>() val listener = mock<FlagListenable.Listener>()
mFlagManager.addListener(ReleasedFlag(1), listener) mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener)
mFlagManager.addListener(ReleasedFlag(10), listener) mFlagManager.addListener(ReleasedFlag(10, "10", "test"), listener)
mFlagManager.dispatchListenersAndMaybeRestart(1, null) mFlagManager.dispatchListenersAndMaybeRestart(1, null)
val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> { val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
@@ -177,7 +177,7 @@ class FlagManagerTest : SysuiTestCase() {
@Test @Test
fun testListenerCanSuppressRestart() { fun testListenerCanSuppressRestart() {
val restartAction = mock<Consumer<Boolean>>() val restartAction = mock<Consumer<Boolean>>()
mFlagManager.addListener(ReleasedFlag(1)) { event -> mFlagManager.addListener(ReleasedFlag(1, "1", "test")) { event ->
event.requestNoRestart() event.requestNoRestart()
} }
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction) mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
@@ -188,7 +188,7 @@ class FlagManagerTest : SysuiTestCase() {
@Test @Test
fun testListenerOnlySuppressesRestartForOwnFlag() { fun testListenerOnlySuppressesRestartForOwnFlag() {
val restartAction = mock<Consumer<Boolean>>() val restartAction = mock<Consumer<Boolean>>()
mFlagManager.addListener(ReleasedFlag(10)) { event -> mFlagManager.addListener(ReleasedFlag(10, "10", "test")) { event ->
event.requestNoRestart() event.requestNoRestart()
} }
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction) mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
@@ -199,10 +199,10 @@ class FlagManagerTest : SysuiTestCase() {
@Test @Test
fun testRestartWhenNotAllListenersRequestSuppress() { fun testRestartWhenNotAllListenersRequestSuppress() {
val restartAction = mock<Consumer<Boolean>>() val restartAction = mock<Consumer<Boolean>>()
mFlagManager.addListener(ReleasedFlag(10)) { event -> mFlagManager.addListener(ReleasedFlag(10, "10", "test")) { event ->
event.requestNoRestart() event.requestNoRestart()
} }
mFlagManager.addListener(ReleasedFlag(10)) { mFlagManager.addListener(ReleasedFlag(10, "10", "test")) {
// do not request // do not request
} }
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction) mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)

View File

@@ -1,84 +0,0 @@
/*
* 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 androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth
import java.lang.StringBuilder
import java.util.ArrayList
import java.util.HashMap
import org.junit.Test
@SmallTest
class FlagsTest : SysuiTestCase() {
@Test
fun testDuplicateFlagIdCheckWorks() {
val flags = Flags.collectFlagsInClass(DuplicateFlagContainer)
val duplicates = groupDuplicateFlags(flags)
Truth.assertWithMessage(generateAssertionMessage(duplicates))
.that(duplicates.size)
.isEqualTo(2)
}
@Test
fun testNoDuplicateFlagIds() {
val flags = Flags.collectFlagsInClass(Flags)
val duplicates = groupDuplicateFlags(flags)
Truth.assertWithMessage(generateAssertionMessage(duplicates))
.that(duplicates.size)
.isEqualTo(0)
}
private fun generateAssertionMessage(duplicates: Map<Int, List<String>>): String {
val stringBuilder = StringBuilder()
stringBuilder.append("Duplicate flag keys found: {")
for (id in duplicates.keys) {
stringBuilder
.append(" ")
.append(id)
.append(": [")
.append(java.lang.String.join(", ", duplicates[id]))
.append("]")
}
stringBuilder.append(" }")
return stringBuilder.toString()
}
private fun groupDuplicateFlags(flags: Map<String, Flag<*>>): Map<Int, List<String>> {
val grouping: MutableMap<Int, MutableList<String>> = HashMap()
for (flag in flags) {
grouping.putIfAbsent(flag.value.id, ArrayList())
grouping[flag.value.id]!!.add(flag.key)
}
val result: MutableMap<Int, List<String>> = HashMap()
for (id in grouping.keys) {
if (grouping[id]!!.size > 1) {
result[id] = grouping[id]!!
}
}
return result
}
private object DuplicateFlagContainer {
val A_FLAG: BooleanFlag = UnreleasedFlag(0)
val B_FLAG: BooleanFlag = UnreleasedFlag(0)
val C_FLAG = StringFlag(0)
val D_FLAG: BooleanFlag = UnreleasedFlag(1)
val E_FLAG = DoubleFlag(3)
val F_FLAG = DoubleFlag(3)
}
}

View File

@@ -50,7 +50,7 @@ class ServerFlagReaderImplTest : SysuiTestCase() {
@Test @Test
fun testChange_alertsListener() { fun testChange_alertsListener() {
val flag = ReleasedFlag(1) val flag = ReleasedFlag(1, "1", "test")
serverFlagReader.listenForChanges(listOf(flag), changeListener) serverFlagReader.listenForChanges(listOf(flag), changeListener)
deviceConfig.setProperty(NAMESPACE, "flag_override_1", "1", false) deviceConfig.setProperty(NAMESPACE, "flag_override_1", "1", false)

View File

@@ -27,7 +27,7 @@ class FakeFeatureFlags : FeatureFlags {
private val listenerFlagIds = mutableMapOf<FlagListenable.Listener, MutableSet<Int>>() private val listenerFlagIds = mutableMapOf<FlagListenable.Listener, MutableSet<Int>>()
init { init {
Flags.flagFields.forEach { entry: Map.Entry<String, Flag<*>> -> FlagsFactory.knownFlags.forEach { entry: Map.Entry<String, Flag<*>> ->
knownFlagNames[entry.value.id] = entry.key knownFlagNames[entry.value.id] = entry.key
} }
} }
@@ -87,8 +87,6 @@ class FakeFeatureFlags : FeatureFlags {
override fun isEnabled(flag: ResourceBooleanFlag): Boolean = requireBooleanValue(flag.id) override fun isEnabled(flag: ResourceBooleanFlag): Boolean = requireBooleanValue(flag.id)
override fun isEnabled(flag: DeviceConfigBooleanFlag): Boolean = requireBooleanValue(flag.id)
override fun isEnabled(flag: SysPropBooleanFlag): Boolean = requireBooleanValue(flag.id) override fun isEnabled(flag: SysPropBooleanFlag): Boolean = requireBooleanValue(flag.id)
override fun getString(flag: StringFlag): String = requireStringValue(flag.id) override fun getString(flag: StringFlag): String = requireStringValue(flag.id)