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

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

Communicate flags from systemui async

Flags are no longer statically compiled into the shared library.

A "GET_FLAGS" api has been added to SystemUI that returns a list
of Flag objects, as defined in SystemUI.

Communication happens via a simple "ordered" broadcast.

FlagManager#getFlagsFuture() returns a ListenableFuture pointing at
the list of flags. Be sure not to call Future#get on the main
thread - the call will block indefinitely as the broadcast receiver
is handled on the main thread.

Instead, add a listener to the future or call #get on a separate
thread.

Bug: 203548827
Test: manual
Change-Id: I8720e0905662a6371e7aabf04341df46b8943a9c
parent 7c1c6a69
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ java_library {
    srcs: [
        "src/com/android/systemui/flags/Flag.kt",
    ],
    include_srcs: true,
    static_kotlin_stdlib: false,
    java_version: "1.8",
    min_sdk_version: "current",
@@ -74,11 +75,11 @@ java_library {
    ],
    static_kotlin_stdlib: false,
    libs: [
        "SystemUI-flags",
        "androidx.concurrent_concurrent-futures",
    ],
    static_libs: [
        "SystemUI-flag-types",
        "SystemUI-flags",
    ],
    java_version: "1.8",
    min_sdk_version: "current",
+127 −7
Original line number Diff line number Diff line
@@ -16,37 +16,157 @@

package com.android.systemui.flags

interface Flag<T> {
import android.os.Parcel
import android.os.Parcelable

interface Flag<T> : Parcelable {
    val id: Int
    val default: T

    override fun describeContents() = 0
}

// Consider using the "parcelize" kotlin library.

data class BooleanFlag @JvmOverloads constructor(
    override val id: Int,
    override val default: Boolean = false
) : Flag<Boolean>
) : Flag<Boolean> {

    companion object {
        @JvmField
        val CREATOR = object : Parcelable.Creator<BooleanFlag> {
            override fun createFromParcel(parcel: Parcel) = BooleanFlag(parcel)
            override fun newArray(size: Int) = arrayOfNulls<BooleanFlag>(size)
        }
    }

    private constructor(parcel: Parcel) : this(
        id = parcel.readInt(),
        default = parcel.readBoolean()
    )

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeInt(id)
        parcel.writeBoolean(default)
    }
}

data class StringFlag @JvmOverloads constructor(
    override val id: Int,
    override val default: String = ""
) : Flag<String>
) : Flag<String> {
    companion object {
        @JvmField
        val CREATOR = object : Parcelable.Creator<StringFlag> {
            override fun createFromParcel(parcel: Parcel) = StringFlag(parcel)
            override fun newArray(size: Int) = arrayOfNulls<StringFlag>(size)
        }
    }

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

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeInt(id)
        parcel.writeString(default)
    }
}

data class IntFlag @JvmOverloads constructor(
    override val id: Int,
    override val default: Int = 0
) : Flag<Int>
) : Flag<Int> {

    companion object {
        @JvmField
        val CREATOR = object : Parcelable.Creator<IntFlag> {
            override fun createFromParcel(parcel: Parcel) = IntFlag(parcel)
            override fun newArray(size: Int) = arrayOfNulls<IntFlag>(size)
        }
    }

    private constructor(parcel: Parcel) : this(
        id = parcel.readInt(),
        default = parcel.readInt()
    )

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeInt(id)
        parcel.writeInt(default)
    }
}

data class LongFlag @JvmOverloads constructor(
    override val id: Int,
    override val default: Long = 0
) : Flag<Long>
) : Flag<Long> {

    companion object {
        @JvmField
        val CREATOR = object : Parcelable.Creator<LongFlag> {
            override fun createFromParcel(parcel: Parcel) = LongFlag(parcel)
            override fun newArray(size: Int) = arrayOfNulls<LongFlag>(size)
        }
    }

    private constructor(parcel: Parcel) : this(
        id = parcel.readInt(),
        default = parcel.readLong()
    )

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeInt(id)
        parcel.writeLong(default)
    }
}

data class FloatFlag @JvmOverloads constructor(
    override val id: Int,
    override val default: Float = 0f
) : Flag<Float>
) : Flag<Float> {

    companion object {
        @JvmField
        val CREATOR = object : Parcelable.Creator<FloatFlag> {
            override fun createFromParcel(parcel: Parcel) = FloatFlag(parcel)
            override fun newArray(size: Int) = arrayOfNulls<FloatFlag>(size)
        }
    }

    private constructor(parcel: Parcel) : this(
        id = parcel.readInt(),
        default = parcel.readFloat()
    )

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeInt(id)
        parcel.writeFloat(default)
    }
}

data class DoubleFlag @JvmOverloads constructor(
    override val id: Int,
    override val default: Double = 0.0
) : Flag<Double>
 No newline at end of file
) : Flag<Double> {

    companion object {
        @JvmField
        val CREATOR = object : Parcelable.Creator<DoubleFlag> {
            override fun createFromParcel(parcel: Parcel) = DoubleFlag(parcel)
            override fun newArray(size: Int) = arrayOfNulls<DoubleFlag>(size)
        }
    }

    private constructor(parcel: Parcel) : this(
        id = parcel.readInt(),
        default = parcel.readDouble()
    )

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeInt(id)
        parcel.writeDouble(default)
    }
}
 No newline at end of file
+29 −9
Original line number Diff line number Diff line
@@ -16,10 +16,13 @@

package com.android.systemui.flags

import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.database.ContentObserver
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.provider.Settings
import androidx.concurrent.futures.CallbackToFutureAdapter
@@ -34,10 +37,12 @@ class FlagManager constructor(
    companion object {
        const val RECEIVING_PACKAGE = "com.android.systemui"
        const val ACTION_SET_FLAG = "com.android.systemui.action.SET_FLAG"
        const val ACTION_GET_FLAGS = "com.android.systemui.action.GET_FLAGS"
        const val FLAGS_PERMISSION = "com.android.systemui.permission.FLAGS"
        const val FIELD_ID = "id"
        const val FIELD_VALUE = "value"
        const val FIELD_TYPE = "type"
        const val FIELD_FLAGS = "flags"
        const val TYPE_BOOLEAN = "boolean"
        private const val SETTINGS_PREFIX = "systemui/flags"
    }
@@ -46,14 +51,26 @@ class FlagManager constructor(
    private val settingsObserver: ContentObserver = SettingsObserver()

    fun getFlagsFuture(): ListenableFuture<Collection<Flag<*>>> {
        val knownFlagMap = Flags.collectFlags()
        // Possible todo in the future: query systemui async to actually get the known flag ids.
        return CallbackToFutureAdapter.getFuture(
            CallbackToFutureAdapter.Resolver {
                completer: CallbackToFutureAdapter.Completer<Collection<Flag<*>>> ->
                completer.set(knownFlagMap.values as Collection<Flag<*>>)
                "Retrieving Flags"
            })
        val intent = Intent(ACTION_GET_FLAGS)
        intent.setPackage(RECEIVING_PACKAGE)

        return CallbackToFutureAdapter.getFuture {
            completer: CallbackToFutureAdapter.Completer<Any?> ->
                context.sendOrderedBroadcast(intent, null,
                    object : BroadcastReceiver() {
                        override fun onReceive(context: Context, intent: Intent) {
                            val extras: Bundle? = getResultExtras(false)
                            val listOfFlags: java.util.ArrayList<Flag<*>>? =
                                extras?.getParcelableArrayList(FIELD_FLAGS)
                            if (listOfFlags != null) {
                                completer.set(listOfFlags)
                            } else {
                                completer.setException(NoFlagResultsException())
                            }
                        }
                    }, null, Activity.RESULT_OK, "extra data", null)
            "QueryingFlags"
        } as ListenableFuture<Collection<Flag<*>>>
    }

    fun setFlagValue(id: Int, enabled: Boolean) {
@@ -150,3 +167,6 @@ class FlagManager constructor(
}

class InvalidFlagStorageException : Exception("Data found but is invalid")

class NoFlagResultsException : Exception(
    "SystemUI failed to communicate its flags back successfully")
 No newline at end of file
+13 −4
Original line number Diff line number Diff line
@@ -16,10 +16,11 @@

package com.android.systemui.flags;

import static com.android.systemui.flags.FlagManager.ACTION_GET_FLAGS;
import static com.android.systemui.flags.FlagManager.ACTION_SET_FLAG;
import static com.android.systemui.flags.FlagManager.FIELD_FLAGS;
import static com.android.systemui.flags.FlagManager.FIELD_ID;
import static com.android.systemui.flags.FlagManager.FIELD_VALUE;
import static com.android.systemui.flags.FlagManager.FLAGS_PERMISSION;

import android.content.BroadcastReceiver;
import android.content.Context;
@@ -69,8 +70,10 @@ public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable {
            DumpManager dumpManager) {
        mFlagManager = flagManager;
        mSecureSettings = secureSettings;
        IntentFilter filter = new IntentFilter(ACTION_SET_FLAG);
        context.registerReceiver(mReceiver, filter, FLAGS_PERMISSION, null);
        IntentFilter filter = new IntentFilter();
        filter.addAction(ACTION_SET_FLAG);
        filter.addAction(ACTION_GET_FLAGS);
        context.registerReceiver(mReceiver, filter, null, null);
        dumpManager.registerDumpable(TAG, this);
    }

@@ -151,9 +154,15 @@ public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable {
            if (action == null) {
                return;
            }

            if (ACTION_SET_FLAG.equals(action)) {
                handleSetFlag(intent.getExtras());
            } else if (ACTION_GET_FLAGS.equals(action)) {
                Map<Integer, Flag<?>> knownFlagMap = Flags.collectFlags();
                ArrayList<Flag<?>> flags = new ArrayList<>(knownFlagMap.values());
                Bundle extras =  getResultExtras(true);
                if (extras != null) {
                    extras.putParcelableArrayList(FIELD_FLAGS, flags);
                }
            }
        }