Add a TableLog for hydrator
This adds an optional TableLog for hydrator to allow logging of changing
states.
As a first use, add it to QSFragmentComposeViewModel.
Flag: EXEMPT logging
Fixes: 379669380
Test: manual, dump QSFragmentComposeViewModel
Change-Id: Ie103fa01d36d54a9f02ad2f25dd521ab25183ae2
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt
index 881228d..93ecae3 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt
@@ -21,11 +21,11 @@
import androidx.compose.runtime.snapshots.StateFactoryMarker
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.coroutines.traceCoroutine
+import com.android.systemui.log.table.TableLogBuffer
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Keeps snapshot/Compose [State]s up-to-date.
@@ -47,6 +47,12 @@
* concatenation or templating.
*/
private val traceName: String,
+ /**
+ * An optional [TableLogBuffer] to log emissions to the states. [traceName] will be used as the
+ * prefix for the columns logged by this [Hydrator], allowing to aggregate multiple hydrators in
+ * the same table.
+ */
+ private val tableLogBuffer: TableLogBuffer? = null,
) : ExclusiveActivatable() {
private val children = mutableListOf<NamedActivatable>()
@@ -62,15 +68,8 @@
* automatically set on the returned [State].
*/
@StateFactoryMarker
- fun <T> hydratedStateOf(
- traceName: String,
- source: StateFlow<T>,
- ): State<T> {
- return hydratedStateOf(
- traceName = traceName,
- initialValue = source.value,
- source = source,
- )
+ fun <T> hydratedStateOf(traceName: String, source: StateFlow<T>): State<T> {
+ return hydratedStateOf(traceName = traceName, initialValue = source.value, source = source)
}
/**
@@ -81,26 +80,44 @@
* performance findings with actual code. One recommendation: prefer whole string literals
* instead of some complex concatenation or templating scheme. Use `null` to disable
* performance tracing for this state.
+ *
+ * If a [TableLogBuffer] was provided, every emission to the flow will be logged using the
+ * [traceName] as the column name. For this to work correctly, all the states in the same
+ * hydrator should have different [traceName]. Use `null` to disable logging for this state.
+ *
* @param initialValue The first value to place on the [State]
* @param source The upstream [Flow] to collect from; values emitted to it will be automatically
* set on the returned [State].
*/
@StateFactoryMarker
- fun <T> hydratedStateOf(
- traceName: String?,
- initialValue: T,
- source: Flow<T>,
- ): State<T> {
+ fun <T> hydratedStateOf(traceName: String?, initialValue: T, source: Flow<T>): State<T> {
check(!isActive) { "Cannot call hydratedStateOf after Hydrator is already active." }
val mutableState = mutableStateOf(initialValue)
+ traceName?.let { name ->
+ tableLogBuffer?.logChange(
+ prefix = this.traceName,
+ columnName = name,
+ value = initialValue?.toString(),
+ isInitial = true,
+ )
+ }
children.add(
NamedActivatable(
traceName = traceName,
activatable =
object : ExclusiveActivatable() {
override suspend fun onActivated(): Nothing {
- source.collect { mutableState.value = it }
+ source.collect {
+ traceName?.let { name ->
+ tableLogBuffer?.logChange(
+ prefix = this@Hydrator.traceName,
+ columnName = name,
+ value = it?.toString(),
+ )
+ }
+ mutableState.value = it
+ }
awaitCancellation()
}
},
@@ -122,8 +139,5 @@
}
}
- private data class NamedActivatable(
- val traceName: String?,
- val activatable: Activatable,
- )
+ private data class NamedActivatable(val traceName: String?, val activatable: Activatable)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeLog.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeLog.kt
new file mode 100644
index 0000000..5f151eb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeLog.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.qs.composefragment.dagger
+
+import javax.inject.Qualifier
+
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class QSFragmentComposeLog
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt
index 2ec7292..bea0d14 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt
@@ -19,6 +19,8 @@
import android.content.Context
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.qs.flags.QSComposeFragment
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.util.Utils
@@ -38,5 +40,14 @@
fun providesUsingMedia(@ShadeDisplayAware context: Context): Boolean {
return QSComposeFragment.isEnabled && Utils.useQsMediaPlayer(context)
}
+
+ @Provides
+ @SysUISingleton
+ @QSFragmentComposeLog
+ fun providesQSFragmentComposeViewModelTableLog(
+ factory: TableLogBufferFactory
+ ): TableLogBuffer {
+ return factory.create("QSFragmentComposeViewModel", 200)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
index e3de6d5..5c582ba 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
@@ -39,6 +39,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QQS
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS
@@ -48,6 +49,7 @@
import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.FooterActionsController
+import com.android.systemui.qs.composefragment.dagger.QSFragmentComposeLog
import com.android.systemui.qs.composefragment.dagger.QSFragmentComposeModule
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.qs.panels.domain.interactor.TileSquishinessInteractor
@@ -101,6 +103,7 @@
private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
private val squishinessInteractor: TileSquishinessInteractor,
private val inFirstPageViewModel: InFirstPageViewModel,
+ @QSFragmentComposeLog private val tableLogBuffer: TableLogBuffer,
mediaInRowInLandscapeViewModelFactory: MediaInRowInLandscapeViewModel.Factory,
@Named(QUICK_QS_PANEL) val qqsMediaHost: MediaHost,
@Named(QS_PANEL) val qsMediaHost: MediaHost,
@@ -112,7 +115,7 @@
private val qqsMediaInRowViewModel = mediaInRowInLandscapeViewModelFactory.create(LOCATION_QQS)
private val qsMediaInRowViewModel = mediaInRowInLandscapeViewModelFactory.create(LOCATION_QS)
- private val hydrator = Hydrator("QSFragmentComposeViewModel.hydrator")
+ private val hydrator = Hydrator("QSFragmentComposeViewModel.hydrator", tableLogBuffer)
val footerActionsViewModel =
footerActionsViewModelFactory.create(lifecycleScope).also {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
index 45d5b38..80db0ae 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
@@ -22,6 +22,7 @@
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.media.controls.ui.view.qqsMediaHost
import com.android.systemui.media.controls.ui.view.qsMediaHost
import com.android.systemui.qs.composefragment.dagger.usingMediaInComposeFragment
@@ -58,6 +59,7 @@
largeScreenHeaderHelper,
tileSquishinessInteractor,
inFirstPageViewModel,
+ logcatTableLogBuffer(this@Fixture),
mediaInRowInLandscapeViewModelFactory,
qqsMediaHost,
qsMediaHost,