diff options
7 files changed, 221 insertions, 4 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt index 783f752cbd20..90f3c7d88c8f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt @@ -16,23 +16,36 @@ package com.android.systemui.keyguard.data.repository -import com.android.keyguard.KeyguardUpdateMonitor +import android.os.Build import com.android.keyguard.ViewMediatorCallback import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel +import com.android.systemui.log.dagger.BouncerLog +import com.android.systemui.log.table.TableLogBuffer +import com.android.systemui.log.table.logDiffsForTable import com.android.systemui.statusbar.phone.KeyguardBouncer import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map -/** Encapsulates app state for the lock screen primary and alternate bouncer. */ +/** + * Encapsulates app state for the lock screen primary and alternate bouncer. + * + * Make sure to add newly added flows to the logger. + */ @SysUISingleton class KeyguardBouncerRepository @Inject constructor( private val viewMediatorCallback: ViewMediatorCallback, - keyguardUpdateMonitor: KeyguardUpdateMonitor, + @Application private val applicationScope: CoroutineScope, + @BouncerLog private val buffer: TableLogBuffer, ) { /** Values associated with the PrimaryBouncer (pin/pattern/password) input. */ private val _primaryBouncerVisible = MutableStateFlow(false) @@ -77,6 +90,10 @@ constructor( val bouncerErrorMessage: CharSequence? get() = viewMediatorCallback.consumeCustomMessage() + init { + setUpLogging() + } + fun setPrimaryScrimmed(isScrimmed: Boolean) { _primaryBouncerScrimmed.value = isScrimmed } @@ -132,4 +149,57 @@ constructor( fun setOnScreenTurnedOff(onScreenTurnedOff: Boolean) { _onScreenTurnedOff.value = onScreenTurnedOff } + + /** Sets up logs for state flows. */ + private fun setUpLogging() { + if (!Build.IS_DEBUGGABLE) { + return + } + + primaryBouncerVisible + .logDiffsForTable(buffer, "", "PrimaryBouncerVisible", false) + .launchIn(applicationScope) + primaryBouncerShow + .map { it != null } + .logDiffsForTable(buffer, "", "PrimaryBouncerShow", false) + .launchIn(applicationScope) + primaryBouncerShowingSoon + .logDiffsForTable(buffer, "", "PrimaryBouncerShowingSoon", false) + .launchIn(applicationScope) + primaryBouncerHide + .logDiffsForTable(buffer, "", "PrimaryBouncerHide", false) + .launchIn(applicationScope) + primaryBouncerStartingToHide + .logDiffsForTable(buffer, "", "PrimaryBouncerStartingToHide", false) + .launchIn(applicationScope) + primaryBouncerStartingDisappearAnimation + .map { it != null } + .logDiffsForTable(buffer, "", "PrimaryBouncerStartingDisappearAnimation", false) + .launchIn(applicationScope) + primaryBouncerScrimmed + .logDiffsForTable(buffer, "", "PrimaryBouncerScrimmed", false) + .launchIn(applicationScope) + panelExpansionAmount + .map { (it * 1000).toInt() } + .logDiffsForTable(buffer, "", "PanelExpansionAmountMillis", -1) + .launchIn(applicationScope) + keyguardPosition + .map { it.toInt() } + .logDiffsForTable(buffer, "", "KeyguardPosition", -1) + .launchIn(applicationScope) + onScreenTurnedOff + .logDiffsForTable(buffer, "", "OnScreenTurnedOff", false) + .launchIn(applicationScope) + isBackButtonEnabled + .filterNotNull() + .logDiffsForTable(buffer, "", "IsBackButtonEnabled", false) + .launchIn(applicationScope) + showMessage + .map { it?.message } + .logDiffsForTable(buffer, "", "ShowMessage", null) + .launchIn(applicationScope) + resourceUpdateRequests + .logDiffsForTable(buffer, "", "ResourceUpdateRequests", false) + .launchIn(applicationScope) + } } diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/BouncerLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/BouncerLog.kt new file mode 100644 index 000000000000..2251a7b243aa --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/BouncerLog.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 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 java.lang.annotation.Documented +import java.lang.annotation.Retention +import java.lang.annotation.RetentionPolicy +import javax.inject.Qualifier + +/** Logger for the primary and alternative bouncers. */ +@Qualifier @Documented @Retention(RetentionPolicy.RUNTIME) annotation class BouncerLog 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 74d50433b9a7..ec2e340762a3 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -23,6 +23,8 @@ import android.os.Looper; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.log.LogBufferFactory; +import com.android.systemui.log.table.TableLogBuffer; +import com.android.systemui.log.table.TableLogBufferFactory; import com.android.systemui.plugins.log.LogBuffer; import com.android.systemui.plugins.log.LogcatEchoTracker; import com.android.systemui.plugins.log.LogcatEchoTrackerDebug; @@ -345,6 +347,14 @@ public class LogModule { return factory.create("BluetoothLog", 50); } + /** Provides a logging buffer for the primary bouncer. */ + @Provides + @SysUISingleton + @BouncerLog + public static TableLogBuffer provideBouncerLogBuffer(TableLogBufferFactory factory) { + return factory.create("BouncerLog", 250); + } + /** * Provides a {@link LogBuffer} for general keyguard-related logs. */ diff --git a/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt b/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt index bb04b6b41a33..348d941d22cf 100644 --- a/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt +++ b/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt @@ -100,3 +100,46 @@ fun Flow<Boolean>.logDiffsForTable( newVal } } +/** + * Each time the Int flow is updated with a new value that's different from the previous value, logs + * the new value to the given [tableLogBuffer]. + */ +fun Flow<Int>.logDiffsForTable( + tableLogBuffer: TableLogBuffer, + columnPrefix: String, + columnName: String, + initialValue: Int, +): Flow<Int> { + val initialValueFun = { + tableLogBuffer.logChange(columnPrefix, columnName, initialValue) + initialValue + } + return this.pairwiseBy(initialValueFun) { prevVal, newVal: Int -> + if (prevVal != newVal) { + tableLogBuffer.logChange(columnPrefix, columnName, newVal) + } + newVal + } +} + +/** + * Each time the String? flow is updated with a new value that's different from the previous value, + * logs the new value to the given [tableLogBuffer]. + */ +fun Flow<String?>.logDiffsForTable( + tableLogBuffer: TableLogBuffer, + columnPrefix: String, + columnName: String, + initialValue: String?, +): Flow<String?> { + val initialValueFun = { + tableLogBuffer.logChange(columnPrefix, columnName, initialValue) + initialValue + } + return this.pairwiseBy(initialValueFun) { prevVal, newVal: String? -> + if (prevVal != newVal) { + tableLogBuffer.logChange(columnPrefix, columnName, newVal) + } + newVal + } +} 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 9d0b833d26cc..2c299d67022d 100644 --- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt +++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt @@ -127,11 +127,21 @@ class TableLogBuffer( rowInitializer(row) } + /** Logs a String? change. */ + fun logChange(prefix: String, columnName: String, value: String?) { + logChange(systemClock.currentTimeMillis(), prefix, columnName, value) + } + /** Logs a boolean change. */ fun logChange(prefix: String, columnName: String, value: Boolean) { logChange(systemClock.currentTimeMillis(), prefix, columnName, value) } + /** Logs a Int change. */ + fun logChange(prefix: String, columnName: String, value: Int) { + logChange(systemClock.currentTimeMillis(), prefix, columnName, value) + } + // Keep these individual [logChange] methods private (don't let clients give us their own // timestamps.) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt index 517e27a3ce2f..2d412dcaa909 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt @@ -27,16 +27,19 @@ import com.android.systemui.keyguard.data.BouncerView import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor +import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.phone.KeyguardBouncer import com.android.systemui.statusbar.phone.KeyguardBypassController import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.TestCoroutineScope import kotlinx.coroutines.yield import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mock import org.mockito.Mockito.mock import org.mockito.MockitoAnnotations @@ -45,6 +48,7 @@ import org.mockito.MockitoAnnotations @TestableLooper.RunWithLooper class UdfpsKeyguardViewControllerWithCoroutinesTest : UdfpsKeyguardViewControllerBaseTest() { lateinit var keyguardBouncerRepository: KeyguardBouncerRepository + @Mock private lateinit var bouncerLogger: TableLogBuffer @Before override fun setUp() { @@ -53,7 +57,8 @@ class UdfpsKeyguardViewControllerWithCoroutinesTest : UdfpsKeyguardViewControlle keyguardBouncerRepository = KeyguardBouncerRepository( mock(com.android.keyguard.ViewMediatorCallback::class.java), - mKeyguardUpdateMonitor + TestCoroutineScope(), + bouncerLogger, ) super.setUp() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt new file mode 100644 index 000000000000..9970a6796ed6 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 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.keyguard.data.repository + +import androidx.test.filters.SmallTest +import com.android.keyguard.ViewMediatorCallback +import com.android.systemui.SysuiTestCase +import com.android.systemui.log.table.TableLogBuffer +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.TestCoroutineScope +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(JUnit4::class) +class KeyguardBouncerRepositoryTest : SysuiTestCase() { + + @Mock private lateinit var viewMediatorCallback: ViewMediatorCallback + @Mock private lateinit var bouncerLogger: TableLogBuffer + lateinit var underTest: KeyguardBouncerRepository + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + val testCoroutineScope = TestCoroutineScope() + underTest = + KeyguardBouncerRepository(viewMediatorCallback, testCoroutineScope, bouncerLogger) + } + + @Test + fun changingFlowValueTriggersLogging() = runBlocking { + underTest.setPrimaryHide(true) + verify(bouncerLogger).logChange("", "PrimaryBouncerHide", false) + } +} |