Merge "[ToA] Add the root uid for CTS" into main
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
index 98a5a67..b646da4 100644
--- a/packages/PackageInstaller/Android.bp
+++ b/packages/PackageInstaller/Android.bp
@@ -59,69 +59,5 @@
     lint: {
         error_checks: ["Recycle"],
     },
-}
-
-android_app {
-    name: "PackageInstaller_tablet",
-    defaults: ["platform_app_defaults"],
-
-    srcs: [
-        "src/**/*.java",
-        "src/**/*.kt",
-    ],
-
-    certificate: "platform",
-    privileged: true,
-    platform_apis: false,
-    sdk_version: "system_current",
-    rename_resources_package: false,
-    overrides: ["PackageInstaller"],
-
-    static_libs: [
-        "xz-java",
-        "androidx.leanback_leanback",
-        "androidx.fragment_fragment",
-        "androidx.lifecycle_lifecycle-livedata",
-        "androidx.lifecycle_lifecycle-extensions",
-        "android.content.pm.flags-aconfig-java",
-        "android.os.flags-aconfig-java",
-    ],
-    aaptflags: ["--product tablet"],
-
-    lint: {
-        error_checks: ["Recycle"],
-    },
-}
-
-android_app {
-    name: "PackageInstaller_tv",
-    defaults: ["platform_app_defaults"],
-
-    srcs: [
-        "src/**/*.java",
-        "src/**/*.kt",
-    ],
-
-    certificate: "platform",
-    privileged: true,
-    platform_apis: false,
-    sdk_version: "system_current",
-    rename_resources_package: false,
-    overrides: ["PackageInstaller"],
-
-    static_libs: [
-        "xz-java",
-        "androidx.leanback_leanback",
-        "androidx.annotation_annotation",
-        "androidx.fragment_fragment",
-        "androidx.lifecycle_lifecycle-livedata",
-        "androidx.lifecycle_lifecycle-extensions",
-        "android.content.pm.flags-aconfig-java",
-        "android.os.flags-aconfig-java",
-    ],
-    aaptflags: ["--product tv"],
-
-    lint: {
-        error_checks: ["Recycle"],
-    },
+    generate_product_characteristics_rro: true,
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
index 7150d69e..9876fe4 100644
--- a/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
@@ -222,13 +222,18 @@
             val buffers = dumpManager.getLogBuffers()
             val tableBuffers = dumpManager.getTableLogBuffers()
 
-            targets.forEach { target ->
-                findTargetInCollection(target, dumpables, buffers, tableBuffers)?.dump(pw, args)
-            }
+            val matches =
+                if (args.matchAll) {
+                    findAllMatchesInCollection(targets, dumpables, buffers, tableBuffers)
+                } else {
+                    findBestMatchesInCollection(targets, dumpables, buffers, tableBuffers)
+                }
+            matches.forEach { it.dump(pw, args) }
         } else {
             if (args.listOnly) {
                 val dumpables = dumpManager.getDumpables()
                 val buffers = dumpManager.getLogBuffers()
+                val tableBuffers = dumpManager.getTableLogBuffers()
 
                 pw.println("Dumpables:")
                 listTargetNames(dumpables, pw)
@@ -236,18 +241,23 @@
 
                 pw.println("Buffers:")
                 listTargetNames(buffers, pw)
+                pw.println()
+
+                pw.println("TableBuffers:")
+                listTargetNames(tableBuffers, pw)
             } else {
                 pw.println("Nothing to dump :(")
             }
         }
     }
 
+    /** Finds the best match for a particular target */
     private fun findTargetInCollection(
         target: String,
         dumpables: Collection<DumpableEntry>,
         logBuffers: Collection<LogBufferEntry>,
         tableBuffers: Collection<TableLogBufferEntry>,
-    ) =
+    ): DumpsysEntry? =
         sequence {
                 findBestTargetMatch(dumpables, target)?.let { yield(it) }
                 findBestTargetMatch(logBuffers, target)?.let { yield(it) }
@@ -256,6 +266,31 @@
             .sortedBy { it.name }
             .minByOrNull { it.name.length }
 
+    /** Finds the best match for each target, if any, in the order of the targets */
+    private fun findBestMatchesInCollection(
+        targets: List<String>,
+        dumpables: Collection<DumpableEntry>,
+        logBuffers: Collection<LogBufferEntry>,
+        tableBuffers: Collection<TableLogBufferEntry>,
+    ): List<DumpsysEntry> =
+        targets.mapNotNull { target ->
+            findTargetInCollection(target, dumpables, logBuffers, tableBuffers)
+        }
+
+    /** Finds all matches for any target, returning in the --list order. */
+    private fun findAllMatchesInCollection(
+        targets: List<String>,
+        dumpables: Collection<DumpableEntry>,
+        logBuffers: Collection<LogBufferEntry>,
+        tableBuffers: Collection<TableLogBufferEntry>,
+    ): List<DumpsysEntry> =
+        sequence {
+                yieldAll(dumpables.filter { it.matchesAny(targets) })
+                yieldAll(logBuffers.filter { it.matchesAny(targets) })
+                yieldAll(tableBuffers.filter { it.matchesAny(targets) })
+            }
+            .sortedBy { it.name }.toList()
+
     private fun dumpConfig(pw: PrintWriter) {
         config.dump(pw, arrayOf())
     }
@@ -272,6 +307,11 @@
         pw.println("etc.")
         pw.println()
 
+        pw.println("Print all matches, instead of the best match:")
+        pw.println("$ <invocation> --all <targets>")
+        pw.println("$ <invocation> --all Log")
+        pw.println()
+
         pw.println("Special commands:")
         pw.println("$ <invocation> dumpables")
         pw.println("$ <invocation> buffers")
@@ -325,9 +365,10 @@
                     "--help" -> {
                         pArgs.command = "help"
                     }
-                    // This flag is passed as part of the proto dump in Bug reports, we can ignore
-                    // it because this is our default behavior.
-                    "-a" -> {}
+                    "-a",
+                    "--all" -> {
+                        pArgs.matchAll = true
+                    }
                     else -> {
                         throw ArgParseException("Unknown flag: $arg")
                     }
@@ -386,15 +427,19 @@
         const val DUMPSYS_DUMPABLE_DIVIDER =
             "----------------------------------------------------------------------------"
 
+        private fun DumpsysEntry.matches(target: String) = name.endsWith(target)
+        private fun DumpsysEntry.matchesAny(targets: Collection<String>) =
+            targets.any { matches(it) }
+
         private fun findBestTargetMatch(c: Collection<DumpsysEntry>, target: String) =
-            c.asSequence().filter { it.name.endsWith(target) }.minByOrNull { it.name.length }
+            c.asSequence().filter { it.matches(target) }.minByOrNull { it.name.length }
 
         private fun findBestProtoTargetMatch(
             c: Collection<DumpableEntry>,
             target: String
         ): ProtoDumpable? =
             c.asSequence()
-                .filter { it.name.endsWith(target) }
+                .filter { it.matches(target) }
                 .filter { it.dumpable is ProtoDumpable }
                 .minByOrNull { it.name.length }
                 ?.dumpable as? ProtoDumpable
@@ -440,40 +485,34 @@
         }
 
         /**
-         * Utility to write a [DumpableEntry] to the given [PrintWriter] in a
-         * dumpsys-appropriate format.
+         * Utility to write a [DumpableEntry] to the given [PrintWriter] in a dumpsys-appropriate
+         * format.
          */
         private fun dumpDumpable(
-                entry: DumpableEntry,
-                pw: PrintWriter,
-                args: Array<String> = arrayOf(),
-        ) = pw.wrapSection(entry) {
-            entry.dumpable.dump(pw, args)
-        }
+            entry: DumpableEntry,
+            pw: PrintWriter,
+            args: Array<String> = arrayOf(),
+        ) = pw.wrapSection(entry) { entry.dumpable.dump(pw, args) }
 
         /**
-         * Utility to write a [LogBufferEntry] to the given [PrintWriter] in a
-         * dumpsys-appropriate format.
+         * Utility to write a [LogBufferEntry] to the given [PrintWriter] in a dumpsys-appropriate
+         * format.
          */
         private fun dumpBuffer(
-                entry: LogBufferEntry,
-                pw: PrintWriter,
-                tailLength: Int = 0,
-        ) = pw.wrapSection(entry) {
-            entry.buffer.dump(pw, tailLength)
-        }
+            entry: LogBufferEntry,
+            pw: PrintWriter,
+            tailLength: Int = 0,
+        ) = pw.wrapSection(entry) { entry.buffer.dump(pw, tailLength) }
 
         /**
          * Utility to write a [TableLogBufferEntry] to the given [PrintWriter] in a
          * dumpsys-appropriate format.
          */
         private fun dumpTableBuffer(
-                entry: TableLogBufferEntry,
-                pw: PrintWriter,
-                args: Array<String> = arrayOf(),
-        ) = pw.wrapSection(entry) {
-            entry.table.dump(pw, args)
-        }
+            entry: TableLogBufferEntry,
+            pw: PrintWriter,
+            args: Array<String> = arrayOf(),
+        ) = pw.wrapSection(entry) { entry.table.dump(pw, args) }
 
         /**
          * Zero-arg utility to write a [DumpsysEntry] to the given [PrintWriter] in a
@@ -513,6 +552,7 @@
     var tailLength: Int = 0
     var command: String? = null
     var listOnly = false
+    var matchAll = false
     var proto = false
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt
new file mode 100644
index 0000000..e17274c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2024 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.util.kotlin
+
+import android.util.IndentingPrintWriter
+import com.android.systemui.Dumpable
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.asIndenting
+import com.android.systemui.util.printCollection
+import java.io.PrintWriter
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.atomic.AtomicBoolean
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flow
+
+/**
+ * An interface which gives the implementing type flow extension functions which will register a
+ * given flow as a field in the Dumpable.
+ */
+interface FlowDumper : Dumpable {
+    /**
+     * Include the last emitted value of this Flow whenever it is being collected. Remove its value
+     * when collection ends.
+     *
+     * @param dumpName the name to use for this field in the dump output
+     */
+    fun <T> Flow<T>.dumpWhileCollecting(dumpName: String): Flow<T>
+
+    /**
+     * Include the [SharedFlow.replayCache] for this Flow in the dump.
+     *
+     * @param dumpName the name to use for this field in the dump output
+     */
+    fun <T, F : SharedFlow<T>> F.dumpReplayCache(dumpName: String): F
+
+    /**
+     * Include the [StateFlow.value] for this Flow in the dump.
+     *
+     * @param dumpName the name to use for this field in the dump output
+     */
+    fun <T, F : StateFlow<T>> F.dumpValue(dumpName: String): F
+
+    /** The default [Dumpable.dump] implementation which just calls [dumpFlows] */
+    override fun dump(pw: PrintWriter, args: Array<out String>) = dumpFlows(pw.asIndenting())
+
+    /** Dump all the values from any registered / active Flows. */
+    fun dumpFlows(pw: IndentingPrintWriter)
+}
+
+/**
+ * An implementation of [FlowDumper]. This be extended directly, or can be used to implement
+ * [FlowDumper] by delegation.
+ *
+ * @param dumpManager if provided, this will be used by the [FlowDumperImpl] to register and
+ *   unregister itself when there is something to dump.
+ * @param tag a static name by which this [FlowDumperImpl] is registered. If not provided, this
+ *   class's name will be used. If you're implementing by delegation, you probably want to provide
+ *   this tag to get a meaningful dumpable name.
+ */
+open class FlowDumperImpl(private val dumpManager: DumpManager?, tag: String? = null) : FlowDumper {
+    private val stateFlowMap = ConcurrentHashMap<String, StateFlow<*>>()
+    private val sharedFlowMap = ConcurrentHashMap<String, SharedFlow<*>>()
+    private val flowCollectionMap = ConcurrentHashMap<Pair<String, String>, Any>()
+    override fun dumpFlows(pw: IndentingPrintWriter) {
+        pw.printCollection("StateFlow (value)", stateFlowMap.toSortedMap().entries) { (key, flow) ->
+            append(key).append('=').println(flow.value)
+        }
+        pw.printCollection("SharedFlow (replayCache)", sharedFlowMap.toSortedMap().entries) {
+            (key, flow) ->
+            append(key).append('=').println(flow.replayCache)
+        }
+        val comparator = compareBy<Pair<String, String>> { it.first }.thenBy { it.second }
+        pw.printCollection("Flow (latest)", flowCollectionMap.toSortedMap(comparator).entries) {
+            (pair, value) ->
+            append(pair.first).append('=').println(value)
+        }
+    }
+
+    private val Any.idString: String
+        get() = Integer.toHexString(System.identityHashCode(this))
+
+    override fun <T> Flow<T>.dumpWhileCollecting(dumpName: String): Flow<T> = flow {
+        val mapKey = dumpName to idString
+        try {
+            collect {
+                flowCollectionMap[mapKey] = it ?: "null"
+                updateRegistration(required = true)
+                emit(it)
+            }
+        } finally {
+            flowCollectionMap.remove(mapKey)
+            updateRegistration(required = false)
+        }
+    }
+
+    override fun <T, F : StateFlow<T>> F.dumpValue(dumpName: String): F {
+        stateFlowMap[dumpName] = this
+        return this
+    }
+
+    override fun <T, F : SharedFlow<T>> F.dumpReplayCache(dumpName: String): F {
+        sharedFlowMap[dumpName] = this
+        return this
+    }
+
+    private val dumpManagerName = tag ?: "[$idString] ${javaClass.simpleName}"
+    private var registered = AtomicBoolean(false)
+    private fun updateRegistration(required: Boolean) {
+        if (dumpManager == null) return
+        if (required && registered.get()) return
+        synchronized(registered) {
+            val shouldRegister =
+                stateFlowMap.isNotEmpty() ||
+                    sharedFlowMap.isNotEmpty() ||
+                    flowCollectionMap.isNotEmpty()
+            val wasRegistered = registered.getAndSet(shouldRegister)
+            if (wasRegistered != shouldRegister) {
+                if (shouldRegister) {
+                    dumpManager.registerCriticalDumpable(dumpManagerName, this)
+                } else {
+                    dumpManager.unregisterDumpable(dumpManagerName)
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index ab5e51c..59fe813 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -207,7 +207,7 @@
     @Test
     fun testDragDownHelperCalledWhenDraggingDown() =
         testScope.runTest {
-            mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+            mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
             whenever(dragDownHelper.isDraggingDown).thenReturn(true)
             val now = SystemClock.elapsedRealtime()
             val ev = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, 0f, 0f, 0 /* meta */)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
index 0cda3fd..81d0e06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -401,7 +401,7 @@
 
     @Test
     fun testSplitShadeLayout_isAlignedToGuideline() {
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
         enableSplitShade()
         underTest.updateResources()
         assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd).isEqualTo(R.id.qs_edge_guideline)
@@ -411,7 +411,7 @@
 
     @Test
     fun testSinglePaneLayout_childrenHaveEqualMargins() {
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
         disableSplitShade()
         underTest.updateResources()
         val qsStartMargin = getConstraintSetLayout(R.id.qs_frame).startMargin
@@ -428,7 +428,7 @@
 
     @Test
     fun testSplitShadeLayout_childrenHaveInsideMarginsOfZero() {
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
         enableSplitShade()
         underTest.updateResources()
         assertThat(getConstraintSetLayout(R.id.qs_frame).endMargin).isEqualTo(0)
@@ -446,8 +446,8 @@
 
     @Test
     fun testLargeScreenLayout_refactorFlagOff_qsAndNotifsTopMarginIsOfHeaderHeightResource() {
+        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
         mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
         setLargeScreen()
         val largeScreenHeaderResourceHeight = 100
         val largeScreenHeaderHelperHeight = 200
@@ -469,8 +469,8 @@
 
     @Test
     fun testLargeScreenLayout_refactorFlagOn_qsAndNotifsTopMarginIsOfHeaderHeightHelper() {
+        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
         mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
         setLargeScreen()
         val largeScreenHeaderResourceHeight = 100
         val largeScreenHeaderHelperHeight = 200
@@ -492,7 +492,7 @@
 
     @Test
     fun testSmallScreenLayout_qsAndNotifsTopMarginIsZero() {
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
         setSmallScreen()
         underTest.updateResources()
         assertThat(getConstraintSetLayout(R.id.qs_frame).topMargin).isEqualTo(0)
@@ -513,7 +513,7 @@
 
     @Test
     fun testSinglePaneShadeLayout_isAlignedToParent() {
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
         disableSplitShade()
         underTest.updateResources()
         assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
index 41b959e..9d53b9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
@@ -119,7 +119,7 @@
 
     @Test
     public void testAppearResetsTranslation() {
-        mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL);
+        mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
         mController.setupAodIcons(mAodIcons);
         when(mDozeParameters.shouldControlScreenOff()).thenReturn(false);
         mController.appearAodIcons();