Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cd1af6a4 authored by Dave Mankoff's avatar Dave Mankoff
Browse files

Step 1 of Removing Ids from Flags.

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

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

Bug: 265188950
Test: manually built before and after cl to ensure overrides persist.
Change-Id: I0faac671b43a0d24262e78ccdb4e23e44f73eeea
parent 90023480
Loading
Loading
Loading
Loading
+12 −31
Original line number Diff line number Diff line
@@ -47,10 +47,6 @@ interface ResourceFlag<T> : Flag<T> {
    val resourceId: Int
}

interface DeviceConfigFlag<T> : Flag<T> {
    val default: T
}

interface SysPropFlag<T> : Flag<T> {
    val default: T
}
@@ -80,8 +76,8 @@ abstract class BooleanFlag constructor(

    private constructor(parcel: Parcel) : this(
        id = parcel.readInt(),
        name = parcel.readString(),
        namespace = parcel.readString(),
        name = parcel.readString() ?: "",
        namespace = parcel.readString() ?: "",
        default = parcel.readBoolean(),
        teamfood = parcel.readBoolean(),
        overridden = parcel.readBoolean()
@@ -136,21 +132,6 @@ data class ResourceBooleanFlag constructor(
    override val teamfood: Boolean = false
) : ResourceFlag<Boolean>

/**
 * A Flag that can reads its overrides from DeviceConfig.
 *
 * This is generally useful for flags that come from or are used _outside_ of SystemUI.
 *
 * Prefer [UnreleasedFlag] and [ReleasedFlag].
 */
data class DeviceConfigBooleanFlag constructor(
    override val id: Int,
    override val name: String,
    override val namespace: String,
    override val default: Boolean = false,
    override val teamfood: Boolean = false
) : DeviceConfigFlag<Boolean>

/**
 * A Flag that can reads its overrides from System Properties.
 *
@@ -186,8 +167,8 @@ data class StringFlag constructor(

    private constructor(parcel: Parcel) : this(
        id = parcel.readInt(),
        name = parcel.readString(),
        namespace = parcel.readString(),
        name = parcel.readString() ?: "",
        namespace = parcel.readString() ?: "",
        default = parcel.readString() ?: ""
    )

@@ -226,8 +207,8 @@ data class IntFlag constructor(

    private constructor(parcel: Parcel) : this(
        id = parcel.readInt(),
        name = parcel.readString(),
        namespace = parcel.readString(),
        name = parcel.readString() ?: "",
        namespace = parcel.readString() ?: "",
        default = parcel.readInt()
    )

@@ -266,8 +247,8 @@ data class LongFlag constructor(

    private constructor(parcel: Parcel) : this(
        id = parcel.readInt(),
        name = parcel.readString(),
        namespace = parcel.readString(),
        name = parcel.readString() ?: "",
        namespace = parcel.readString() ?: "",
        default = parcel.readLong()
    )

@@ -298,8 +279,8 @@ data class FloatFlag constructor(

    private constructor(parcel: Parcel) : this(
        id = parcel.readInt(),
        name = parcel.readString(),
        namespace = parcel.readString(),
        name = parcel.readString() ?: "",
        namespace = parcel.readString() ?: "",
        default = parcel.readFloat()
    )

@@ -338,8 +319,8 @@ data class DoubleFlag constructor(

    private constructor(parcel: Parcel) : this(
        id = parcel.readInt(),
        name = parcel.readString(),
        namespace = parcel.readString(),
        name = parcel.readString() ?: "",
        namespace = parcel.readString() ?: "",
        default = parcel.readDouble()
    )

+1 −1
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@ interface FlagListenable {
    /** An event representing the change */
    interface FlagEvent {
        /** the id of the flag which changed */
        val flagId: Int
        val flagName: String
        /** if all listeners alerted invoke this method, the restart will be skipped */
        fun requestNoRestart()
    }
+30 −23
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@ class FlagManager constructor(
        const val ACTION_GET_FLAGS = "com.android.systemui.action.GET_FLAGS"
        const val FLAGS_PERMISSION = "com.android.systemui.permission.FLAGS"
        const val ACTION_SYSUI_STARTED = "com.android.systemui.STARTED"
        const val EXTRA_ID = "id"
        const val EXTRA_NAME = "name"
        const val EXTRA_VALUE = "value"
        const val EXTRA_FLAGS = "flags"
        private const val SETTINGS_PREFIX = "systemui/flags"
@@ -56,7 +56,7 @@ class FlagManager constructor(
     * that the restart be suppressed
     */
    var onSettingsChangedAction: Consumer<Boolean>? = null
    var clearCacheAction: Consumer<Int>? = null
    var clearCacheAction: Consumer<String>? = null
    private val listeners: MutableSet<PerFlagListener> = mutableSetOf()
    private val settingsObserver: ContentObserver = SettingsObserver()

@@ -96,35 +96,42 @@ class FlagManager constructor(
     * Returns the stored value or null if not set.
     * This API is used by TheFlippinApp.
     */
    fun isEnabled(id: Int): Boolean? = readFlagValue(id, BooleanFlagSerializer)
    fun isEnabled(name: String): Boolean? = readFlagValue(name, BooleanFlagSerializer)

    /**
     * Sets the value of a boolean flag.
     * This API is used by TheFlippinApp.
     */
    fun setFlagValue(id: Int, enabled: Boolean) {
        val intent = createIntent(id)
    fun setFlagValue(name: String, enabled: Boolean) {
        val intent = createIntent(name)
        intent.putExtra(EXTRA_VALUE, enabled)

        context.sendBroadcast(intent)
    }

    fun eraseFlag(id: Int) {
        val intent = createIntent(id)
    fun eraseFlag(name: String) {
        val intent = createIntent(name)

        context.sendBroadcast(intent)
    }

    /** Returns the stored value or null if not set.  */
    // TODO(b/265188950): Remove method this once ids are fully deprecated.
    fun <T> readFlagValue(id: Int, serializer: FlagSerializer<T>): T? {
        val data = settings.getString(idToSettingsKey(id))
        val data = settings.getStringFromSecure(idToSettingsKey(id))
        return serializer.fromSettingsData(data)
    }

    /** Returns the stored value or null if not set.  */
    fun <T> readFlagValue(name: String, serializer: FlagSerializer<T>): T? {
        val data = settings.getString(nameToSettingsKey(name))
        return serializer.fromSettingsData(data)
    }

    override fun addListener(flag: Flag<*>, listener: FlagListenable.Listener) {
        synchronized(listeners) {
            val registerNeeded = listeners.isEmpty()
            listeners.add(PerFlagListener(flag.id, listener))
            listeners.add(PerFlagListener(flag.name, listener))
            if (registerNeeded) {
                settings.registerContentObserver(SETTINGS_PREFIX, true, settingsObserver)
            }
@@ -143,38 +150,38 @@ class FlagManager constructor(
        }
    }

    private fun createIntent(id: Int): Intent {
    private fun createIntent(name: String): Intent {
        val intent = Intent(ACTION_SET_FLAG)
        intent.setPackage(RECEIVING_PACKAGE)
        intent.putExtra(EXTRA_ID, id)
        intent.putExtra(EXTRA_NAME, name)

        return intent
    }

    // TODO(b/265188950): Remove method this once ids are fully deprecated.
    fun idToSettingsKey(id: Int): String {
        return "$SETTINGS_PREFIX/$id"
    }

    fun nameToSettingsKey(name: String): String {
        return "$SETTINGS_PREFIX/$name"
    }

    inner class SettingsObserver : ContentObserver(handler) {
        override fun onChange(selfChange: Boolean, uri: Uri?) {
            if (uri == null) {
                return
            }
            val parts = uri.pathSegments
            val idStr = parts[parts.size - 1]
            val id = try {
                idStr.toInt()
            } catch (e: NumberFormatException) {
                return
            }
            clearCacheAction?.accept(id)
            dispatchListenersAndMaybeRestart(id, onSettingsChangedAction)
            val name = parts[parts.size - 1]
            clearCacheAction?.accept(name)
            dispatchListenersAndMaybeRestart(name, onSettingsChangedAction)
        }
    }

    fun dispatchListenersAndMaybeRestart(id: Int, restartAction: Consumer<Boolean>?) {
    fun dispatchListenersAndMaybeRestart(name: String, restartAction: Consumer<Boolean>?) {
        val filteredListeners: List<FlagListenable.Listener> = synchronized(listeners) {
            listeners.mapNotNull { if (it.id == id) it.listener else null }
            listeners.mapNotNull { if (it.name == name) it.listener else null }
        }
        // If there are no listeners, there's nothing to dispatch to, and nothing to suppress it.
        if (filteredListeners.isEmpty()) {
@@ -185,7 +192,7 @@ class FlagManager constructor(
        val suppressRestartList: List<Boolean> = filteredListeners.map { listener ->
            var didRequestNoRestart = false
            val event = object : FlagListenable.FlagEvent {
                override val flagId = id
                override val flagName = name
                override fun requestNoRestart() {
                    didRequestNoRestart = true
                }
@@ -198,7 +205,7 @@ class FlagManager constructor(
        restartAction?.accept(suppressRestart)
    }

    private data class PerFlagListener(val id: Int, val listener: FlagListenable.Listener)
    private data class PerFlagListener(val name: String, val listener: FlagListenable.Listener)
}

class NoFlagResultsException : Exception(
+4 −1
Original line number Diff line number Diff line
@@ -22,7 +22,10 @@ import android.provider.Settings

class FlagSettingsHelper(private val contentResolver: ContentResolver) {

    fun getString(key: String): String? = Settings.Secure.getString(contentResolver, key)
    // TODO(b/265188950): Remove method this once ids are fully deprecated.
    fun getStringFromSecure(key: String): String? = Settings.Secure.getString(contentResolver, key)

    fun getString(key: String): String? = Settings.Global.getString(contentResolver, key)

    fun registerContentObserver(
        name: String,
+5 −10
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@ object FlagsFactory {
        teamfood: Boolean = false
    ): UnreleasedFlag {
        val flag = UnreleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
        FlagsFactory.checkForDupesAndAdd(flag)
        checkForDupesAndAdd(flag)
        return flag
    }

@@ -46,7 +46,7 @@ object FlagsFactory {
        teamfood: Boolean = false
    ): ReleasedFlag {
        val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
        FlagsFactory.checkForDupesAndAdd(flag)
        checkForDupesAndAdd(flag)
        return flag
    }

@@ -65,7 +65,7 @@ object FlagsFactory {
                resourceId = resourceId,
                teamfood = teamfood
            )
        FlagsFactory.checkForDupesAndAdd(flag)
        checkForDupesAndAdd(flag)
        return flag
    }

@@ -77,18 +77,13 @@ object FlagsFactory {
    ): SysPropBooleanFlag {
        val flag =
            SysPropBooleanFlag(id = id, name = name, namespace = "systemui", default = default)
        FlagsFactory.checkForDupesAndAdd(flag)
        checkForDupesAndAdd(flag)
        return flag
    }

    private fun checkForDupesAndAdd(flag: Flag<*>) {
        if (flagMap.containsKey(flag.name)) {
            throw IllegalArgumentException("Name {flag.name} is already registered")
        }
        flagMap.forEach {
            if (it.value.id == flag.id) {
                throw IllegalArgumentException("Name {flag.id} is already registered")
            }
            throw IllegalArgumentException("Name {$flag.name} is already registered")
        }
        flagMap[flag.name] = flag
    }
Loading