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
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 25db478..2909043 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -62,6 +62,7 @@
     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 @@
     ],
     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",
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
index 68834bc..d9b6a34 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
@@ -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
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
index cbb942b..1dc555e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
@@ -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 @@
     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 @@
     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) {
@@ -149,4 +166,7 @@
     }
 }
 
-class InvalidFlagStorageException : Exception("Data found but is invalid")
\ No newline at end of file
+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