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