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();