diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/TableLogBufferBase.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/TableLogBufferBase.kt new file mode 100644 index 0000000000000..50b3f78a49bcf --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/TableLogBufferBase.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 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.plugins.log + +/** + * Base interface for a logger that logs changes in table format. + * + * This is a plugin interface for classes outside of SystemUI core. + */ +interface TableLogBufferBase { + /** + * Logs a String? change. + * + * For Java overloading. + */ + fun logChange(prefix: String, columnName: String, value: String?) { + logChange(prefix, columnName, value, isInitial = false) + } + + /** Logs a String? change. */ + fun logChange(prefix: String, columnName: String, value: String?, isInitial: Boolean) + + /** + * Logs a Boolean change. + * + * For Java overloading. + */ + fun logChange(prefix: String, columnName: String, value: Boolean) { + logChange(prefix, columnName, value, isInitial = false) + } + + /** Logs a Boolean change. */ + fun logChange(prefix: String, columnName: String, value: Boolean, isInitial: Boolean) + + /** + * Logs an Int? change. + * + * For Java overloading. + */ + fun logChange(prefix: String, columnName: String, value: Int?) { + logChange(prefix, columnName, value, isInitial = false) + } + + /** Logs an Int? change. */ + fun logChange(prefix: String, columnName: String, value: Int?, isInitial: Boolean) +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.java b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.java index 6bf1ce57b01e0..9bead9410a510 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.java @@ -234,9 +234,26 @@ public abstract class Condition { } protected final String getTag() { + if (isOverridingCondition()) { + return mTag + "[OVRD]"; + } + return mTag; } + /** + * Returns the state of the condition. + * - "Invalid", condition hasn't been set / not monitored + * - "True", condition has been met + * - "False", condition has not been met + */ + protected final String getState() { + if (!isConditionSet()) { + return "Invalid"; + } + return isConditionMet() ? "True" : "False"; + } + /** * Creates a new condition which will only be true when both this condition and all the provided * conditions are true. diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java index 43df08df9f454..1f61c64dd057a 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java @@ -22,6 +22,7 @@ import android.util.Log; import androidx.annotation.NonNull; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.log.TableLogBufferBase; import java.util.ArrayList; import java.util.Collections; @@ -41,6 +42,7 @@ public class Monitor { private final String mTag = getClass().getSimpleName(); private final Executor mExecutor; private final Set mPreconditions; + private final TableLogBufferBase mLogBuffer; private final HashMap> mConditions = new HashMap<>(); private final HashMap mSubscriptions = new HashMap<>(); @@ -160,11 +162,23 @@ public class Monitor { * Main constructor, allowing specifying preconditions. */ public Monitor(Executor executor, Set preconditions) { + this(executor, preconditions, null); + } + + /** + * Main constructor, allowing specifying preconditions and a log buffer for logging. + */ + public Monitor(Executor executor, Set preconditions, TableLogBufferBase logBuffer) { mExecutor = executor; mPreconditions = preconditions; + mLogBuffer = logBuffer; } private void updateConditionMetState(Condition condition) { + if (mLogBuffer != null) { + mLogBuffer.logChange(/* prefix= */ "", condition.getTag(), condition.getState()); + } + final ArraySet subscriptions = mConditions.get(condition); // It's possible the condition was removed between the time the callback occurred and diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 70c859ebe02b5..5bcf32a9ebdba 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -55,6 +55,8 @@ import com.android.systemui.flags.FlagsModule; import com.android.systemui.keyboard.KeyboardModule; import com.android.systemui.keyguard.data.BouncerViewModule; import com.android.systemui.log.dagger.LogModule; +import com.android.systemui.log.dagger.MonitorLog; +import com.android.systemui.log.table.TableLogBuffer; import com.android.systemui.mediaprojection.appselector.MediaProjectionModule; import com.android.systemui.model.SysUiState; import com.android.systemui.motiontool.MotionToolModule; @@ -250,8 +252,8 @@ public abstract class SystemUIModule { @Provides @SystemUser static Monitor provideSystemUserMonitor(@Main Executor executor, - SystemProcessCondition systemProcessCondition) { - return new Monitor(executor, Collections.singleton(systemProcessCondition)); + SystemProcessCondition systemProcessCondition, @MonitorLog TableLogBuffer logBuffer) { + return new Monitor(executor, Collections.singleton(systemProcessCondition), logBuffer); } @BindsOptionalOf diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index 9be18ace79fac..b6fcd82fdff66 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -421,6 +421,14 @@ public class LogModule { return factory.create("BouncerLog", 250); } + /** Provides a table logging buffer for the Monitor. */ + @Provides + @SysUISingleton + @MonitorLog + public static TableLogBuffer provideMonitorTableLogBuffer(TableLogBufferFactory factory) { + return factory.create("MonitorLog", 250); + } + /** * Provides a {@link LogBuffer} for Udfps logs. */ diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MonitorLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/MonitorLog.kt new file mode 100644 index 0000000000000..b6a4ec935d14f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MonitorLog.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 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.log.dagger + +import javax.inject.Qualifier +import kotlin.annotation.Retention + +/** Logger for Monitor. */ +@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class MonitorLog diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt index cabe319d317da..8d622ae1ca035 100644 --- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt +++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt @@ -23,6 +23,7 @@ import com.android.systemui.common.buffer.RingBuffer import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.log.LogLevel import com.android.systemui.log.LogcatEchoTracker +import com.android.systemui.plugins.log.TableLogBufferBase import com.android.systemui.util.time.SystemClock import java.io.PrintWriter import java.util.Locale @@ -84,7 +85,7 @@ class TableLogBuffer( @Background private val bgDispatcher: CoroutineDispatcher, private val coroutineScope: CoroutineScope, private val localLogcat: LogProxy = LogProxyDefault(), -) : Dumpable { +) : Dumpable, TableLogBufferBase { init { if (maxSize <= 0) { throw IllegalArgumentException("maxSize must be > 0") @@ -177,7 +178,7 @@ class TableLogBuffer( * * @param isInitial see [TableLogBuffer.logChange(String, Boolean, (TableRowLogger) -> Unit]. */ - fun logChange(prefix: String, columnName: String, value: String?, isInitial: Boolean = false) { + override fun logChange(prefix: String, columnName: String, value: String?, isInitial: Boolean) { logChange(systemClock.currentTimeMillis(), prefix, columnName, value, isInitial) } @@ -186,7 +187,7 @@ class TableLogBuffer( * * @param isInitial see [TableLogBuffer.logChange(String, Boolean, (TableRowLogger) -> Unit]. */ - fun logChange(prefix: String, columnName: String, value: Boolean, isInitial: Boolean = false) { + override fun logChange(prefix: String, columnName: String, value: Boolean, isInitial: Boolean) { logChange(systemClock.currentTimeMillis(), prefix, columnName, value, isInitial) } @@ -195,7 +196,7 @@ class TableLogBuffer( * * @param isInitial see [TableLogBuffer.logChange(String, Boolean, (TableRowLogger) -> Unit]. */ - fun logChange(prefix: String, columnName: String, value: Int?, isInitial: Boolean = false) { + override fun logChange(prefix: String, columnName: String, value: Int?, isInitial: Boolean) { logChange(systemClock.currentTimeMillis(), prefix, columnName, value, isInitial) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java index de5824d1f463f..b7241759ca246 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java @@ -32,6 +32,7 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.log.TableLogBufferBase; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -44,6 +45,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import kotlinx.coroutines.CoroutineScope; @@ -59,6 +61,8 @@ public class ConditionMonitorTest extends SysuiTestCase { @Mock private CoroutineScope mScope; + @Mock + private TableLogBufferBase mLogBuffer; private Monitor mConditionMonitor; @@ -630,4 +634,42 @@ public class ConditionMonitorTest extends SysuiTestCase { verify(callback).onActiveChanged(eq(true)); verify(callback).onConditionsChanged(eq(true)); } + + @Test + public void testLoggingCallback() { + final Monitor monitor = new Monitor(mExecutor, Collections.emptySet(), mLogBuffer); + + final FakeCondition condition = new FakeCondition(mScope); + final FakeCondition overridingCondition = new FakeCondition( + mScope, + /* initialValue= */ false, + /* overriding= */ true); + + final Monitor.Callback callback = mock(Monitor.Callback.class); + monitor.addSubscription(getDefaultBuilder(callback) + .addCondition(condition) + .addCondition(overridingCondition) + .build()); + mExecutor.runAllReady(); + + // condition set to true + condition.fakeUpdateCondition(true); + mExecutor.runAllReady(); + verify(mLogBuffer).logChange("", "FakeCondition", "True"); + + // condition set to false + condition.fakeUpdateCondition(false); + mExecutor.runAllReady(); + verify(mLogBuffer).logChange("", "FakeCondition", "False"); + + // condition unset + condition.fakeClearCondition(); + mExecutor.runAllReady(); + verify(mLogBuffer).logChange("", "FakeCondition", "Invalid"); + + // overriding condition set to true + overridingCondition.fakeUpdateCondition(true); + mExecutor.runAllReady(); + verify(mLogBuffer).logChange("", "FakeCondition[OVRD]", "True"); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/FakeCondition.java b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/FakeCondition.java index a325cbf25ffe5..5416536305fc2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/FakeCondition.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/FakeCondition.java @@ -47,4 +47,8 @@ public class FakeCondition extends Condition { public void fakeUpdateCondition(boolean isConditionMet) { updateCondition(isConditionMet); } + + public void fakeClearCondition() { + clearCondition(); + } }