Merge "Hookup active unlock running state to bouncer delay logic" into udc-dev

This commit is contained in:
Beverly Tai
2023-05-12 17:10:36 +00:00
committed by Android (Google) Code Review
12 changed files with 185 additions and 51 deletions

View File

@@ -4395,7 +4395,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mFingerprintListenBuffer.toList()
).printTableData(pw);
}
pw.println("ActiveUnlockRunning="
+ mTrustManager.isActiveUnlockRunning(KeyguardUpdateMonitor.getCurrentUser()));
new DumpsysTableLogger(
"KeyguardActiveUnlockTriggers",
KeyguardActiveUnlockModel.TABLE_HEADERS,

View File

@@ -17,6 +17,7 @@
package com.android.keyguard.logging
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.ActiveUnlockModel
import com.android.systemui.keyguard.shared.model.TrustManagedModel
import com.android.systemui.keyguard.shared.model.TrustModel
import com.android.systemui.log.LogBuffer
@@ -76,6 +77,18 @@ constructor(
)
}
fun activeUnlockModelEmitted(value: ActiveUnlockModel) {
logBuffer.log(
TAG,
LogLevel.DEBUG,
{
int1 = value.userId
bool1 = value.isRunning
},
{ "activeUnlockModel emitted: userId: $int1 isRunning: $bool1" }
)
}
fun isCurrentUserTrusted(isCurrentUserTrusted: Boolean) {
logBuffer.log(
TAG,
@@ -85,6 +98,15 @@ constructor(
)
}
fun isCurrentUserActiveUnlockRunning(isCurrentUserActiveUnlockRunning: Boolean) {
logBuffer.log(
TAG,
LogLevel.DEBUG,
{ bool1 = isCurrentUserActiveUnlockRunning },
{ "isCurrentUserActiveUnlockRunning emitted: $bool1" }
)
}
fun isCurrentUserTrustManaged(isTrustManaged: Boolean) {
logBuffer.log(TAG, DEBUG, { bool1 = isTrustManaged }, { "isTrustManaged emitted: $bool1" })
}

View File

@@ -22,6 +22,7 @@ import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLoggin
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.shared.model.ActiveUnlockModel
import com.android.systemui.keyguard.shared.model.TrustManagedModel
import com.android.systemui.keyguard.shared.model.TrustModel
import com.android.systemui.user.data.repository.UserRepository
@@ -29,7 +30,6 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
@@ -45,8 +45,8 @@ interface TrustRepository {
/** Flow representing whether the current user is trusted. */
val isCurrentUserTrusted: Flow<Boolean>
/** Flow representing whether active unlock is available for the current user. */
val isCurrentUserActiveUnlockAvailable: StateFlow<Boolean>
/** Flow representing whether active unlock is running for the current user. */
val isCurrentUserActiveUnlockRunning: Flow<Boolean>
/** Reports that whether trust is managed has changed for the current user. */
val isCurrentUserTrustManaged: StateFlow<Boolean>
@@ -62,6 +62,7 @@ constructor(
private val logger: TrustRepositoryLogger,
) : TrustRepository {
private val latestTrustModelForUser = mutableMapOf<Int, TrustModel>()
private val activeUnlockRunningForUser = mutableMapOf<Int, ActiveUnlockModel>()
private val trustManagedForUser = mutableMapOf<Int, TrustManagedModel>()
private val trust =
@@ -87,6 +88,17 @@ constructor(
override fun onEnabledTrustAgentsChanged(userId: Int) = Unit
override fun onIsActiveUnlockRunningChanged(
isRunning: Boolean,
userId: Int
) {
trySendWithFailureLogging(
ActiveUnlockModel(isRunning, userId),
TrustRepositoryLogger.TAG,
"onActiveUnlockRunningChanged"
)
}
override fun onTrustManagedChanged(isTrustManaged: Boolean, userId: Int) {
logger.onTrustManagedChanged(isTrustManaged, userId)
trySendWithFailureLogging(
@@ -95,11 +107,6 @@ constructor(
"onTrustManagedChanged"
)
}
override fun onIsActiveUnlockRunningChanged(
isRunning: Boolean,
userId: Int
) = Unit
}
trustManager.registerTrustListener(callback)
logger.trustListenerRegistered()
@@ -114,6 +121,10 @@ constructor(
latestTrustModelForUser[it.userId] = it
logger.trustModelEmitted(it)
}
is ActiveUnlockModel -> {
activeUnlockRunningForUser[it.userId] = it
logger.activeUnlockModelEmitted(it)
}
is TrustManagedModel -> {
trustManagedForUser[it.userId] = it
logger.trustManagedModelEmitted(it)
@@ -122,8 +133,17 @@ constructor(
}
.shareIn(applicationScope, started = SharingStarted.Eagerly, replay = 1)
// TODO: Implement based on TrustManager callback b/267322286
override val isCurrentUserActiveUnlockAvailable: StateFlow<Boolean> = MutableStateFlow(true)
override val isCurrentUserActiveUnlockRunning: Flow<Boolean> =
combine(trust, userRepository.selectedUserInfo, ::Pair)
.map { activeUnlockRunningForUser[it.second.id]?.isRunning ?: false }
.distinctUntilChanged()
.onEach { logger.isCurrentUserActiveUnlockRunning(it) }
.onStart {
emit(
activeUnlockRunningForUser[userRepository.getSelectedUserInfo().id]?.isRunning
?: false
)
}
override val isCurrentUserTrustManaged: StateFlow<Boolean>
get() =

View File

@@ -31,6 +31,7 @@ import com.android.systemui.DejankUtils
import com.android.systemui.R
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -43,11 +44,14 @@ import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.statusbar.policy.KeyguardStateController
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject
/**
@@ -58,18 +62,19 @@ import javax.inject.Inject
class PrimaryBouncerInteractor
@Inject
constructor(
private val repository: KeyguardBouncerRepository,
private val primaryBouncerView: BouncerView,
@Main private val mainHandler: Handler,
private val keyguardStateController: KeyguardStateController,
private val keyguardSecurityModel: KeyguardSecurityModel,
private val primaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor,
private val falsingCollector: FalsingCollector,
private val dismissCallbackRegistry: DismissCallbackRegistry,
private val context: Context,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val trustRepository: TrustRepository,
private val featureFlags: FeatureFlags,
private val repository: KeyguardBouncerRepository,
private val primaryBouncerView: BouncerView,
@Main private val mainHandler: Handler,
private val keyguardStateController: KeyguardStateController,
private val keyguardSecurityModel: KeyguardSecurityModel,
private val primaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor,
private val falsingCollector: FalsingCollector,
private val dismissCallbackRegistry: DismissCallbackRegistry,
private val context: Context,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val trustRepository: TrustRepository,
private val featureFlags: FeatureFlags,
@Application private val applicationScope: CoroutineScope,
) {
private val passiveAuthBouncerDelay = context.resources.getInteger(
R.integer.primary_bouncer_passive_auth_delay).toLong()
@@ -104,6 +109,7 @@ constructor(
/** Allow for interaction when just about fully visible */
val isInteractable: Flow<Boolean> = bouncerExpansion.map { it > 0.9 }
val sideFpsShowing: Flow<Boolean> = repository.sideFpsShowing
private var currentUserActiveUnlockRunning = false
/** This callback needs to be a class field so it does not get garbage collected. */
val keyguardUpdateMonitorCallback =
@@ -122,6 +128,13 @@ constructor(
init {
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
if (featureFlags.isEnabled(Flags.DELAY_BOUNCER)) {
applicationScope.launch {
trustRepository.isCurrentUserActiveUnlockRunning.collect {
currentUserActiveUnlockRunning = it
}
}
}
}
// TODO(b/243685699): Move isScrimmed logic to data layer.
@@ -377,8 +390,9 @@ constructor(
private fun usePrimaryBouncerPassiveAuthDelay(): Boolean {
val canRunFaceAuth = keyguardStateController.isFaceAuthEnabled &&
keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE)
val canRunActiveUnlock = trustRepository.isCurrentUserActiveUnlockAvailable.value &&
val canRunActiveUnlock = currentUserActiveUnlockRunning &&
keyguardUpdateMonitor.canTriggerActiveUnlockBasedOnDeviceState()
return featureFlags.isEnabled(Flags.DELAY_BOUNCER) &&
!needsFullscreenBouncer() &&
(canRunFaceAuth || canRunActiveUnlock)

View File

@@ -0,0 +1,25 @@
/*
* 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.keyguard.shared.model
/** Represents the active unlock state */
data class ActiveUnlockModel(
/** If true, the system believes active unlock is available and can be usd to unlock. */
val isRunning: Boolean,
/** The user, for which active unlock may be running. */
val userId: Int,
)

View File

@@ -24,13 +24,14 @@ import com.android.keyguard.KeyguardSecurityModel
import com.android.systemui.RoboPilotTest
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.BouncerView
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepositoryImpl
import com.android.systemui.keyguard.data.repository.TrustRepository
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
@@ -95,8 +96,9 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest :
mock(DismissCallbackRegistry::class.java),
context,
mKeyguardUpdateMonitor,
mock(TrustRepository::class.java),
FakeFeatureFlags(),
FakeTrustRepository(),
FakeFeatureFlags().apply { set(Flags.DELAY_BOUNCER, true) },
testScope.backgroundScope,
)
mAlternateBouncerInteractor =
AlternateBouncerInteractor(

View File

@@ -64,7 +64,6 @@ class TrustRepositoryTest : SysuiTestCase() {
testScope = TestScope()
userRepository = FakeUserRepository()
userRepository.setUserInfos(users)
val logger =
TrustRepositoryLogger(
LogBuffer("TestBuffer", 1, mock(LogcatEchoTracker::class.java), false)
@@ -224,4 +223,41 @@ class TrustRepositoryTest : SysuiTestCase() {
assertThat(isCurrentUserTrusted()).isTrue()
}
@Test
fun isCurrentUserActiveUnlockRunning_runningFirstBeforeUserInfoChanges_emitsCorrectValue() =
testScope.runTest {
runCurrent()
verify(trustManager).registerTrustListener(listener.capture())
val isCurrentUserActiveUnlockRunning by
collectLastValue(underTest.isCurrentUserActiveUnlockRunning)
userRepository.setSelectedUserInfo(users[1])
// active unlock running = true for users[0].id, but not the current user
listener.value.onIsActiveUnlockRunningChanged(true, users[0].id)
assertThat(isCurrentUserActiveUnlockRunning).isFalse()
// current user is now users[0].id
userRepository.setSelectedUserInfo(users[0])
assertThat(isCurrentUserActiveUnlockRunning).isTrue()
}
@Test
fun isCurrentUserActiveUnlockRunning_whenActiveUnlockRunningForCurrentUser_emitsNewValue() =
testScope.runTest {
runCurrent()
verify(trustManager).registerTrustListener(listener.capture())
val isCurrentUserActiveUnlockRunning by
collectLastValue(underTest.isCurrentUserActiveUnlockRunning)
userRepository.setSelectedUserInfo(users[0])
listener.value.onIsActiveUnlockRunningChanged(true, users[0].id)
assertThat(isCurrentUserActiveUnlockRunning).isTrue()
listener.value.onIsActiveUnlockRunningChanged(false, users[0].id)
assertThat(isCurrentUserActiveUnlockRunning).isFalse()
listener.value.onIsActiveUnlockRunningChanged(true, users[0].id)
assertThat(isCurrentUserActiveUnlockRunning).isTrue()
}
}

View File

@@ -35,7 +35,7 @@ import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepo
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.TrustRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -100,8 +100,9 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
mock(DismissCallbackRegistry::class.java),
context,
keyguardUpdateMonitor,
mock(TrustRepository::class.java),
FakeFeatureFlags(),
FakeTrustRepository(),
FakeFeatureFlags().apply { set(Flags.DELAY_BOUNCER, true) },
testScope.backgroundScope,
),
AlternateBouncerInteractor(
mock(StatusBarStateController::class.java),

View File

@@ -44,6 +44,8 @@ import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.android.systemui.utils.os.FakeHandler
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -75,6 +77,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
private lateinit var resources: TestableResources
private lateinit var trustRepository: FakeTrustRepository
private lateinit var featureFlags: FakeFeatureFlags
private lateinit var testScope: TestScope
@Before
fun setUp() {
@@ -83,9 +86,10 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
.thenReturn(KeyguardSecurityModel.SecurityMode.PIN)
DejankUtils.setImmediate(true)
testScope = TestScope()
mainHandler = FakeHandler(android.os.Looper.getMainLooper())
trustRepository = FakeTrustRepository()
featureFlags = FakeFeatureFlags().apply { set(Flags.DELAY_BOUNCER, false) }
featureFlags = FakeFeatureFlags().apply { set(Flags.DELAY_BOUNCER, true) }
underTest =
PrimaryBouncerInteractor(
repository,
@@ -100,6 +104,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
keyguardUpdateMonitor,
trustRepository,
featureFlags,
testScope.backgroundScope,
)
whenever(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
whenever(repository.primaryBouncerShow.value).thenReturn(false)
@@ -398,7 +403,6 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
mainHandler.setMode(FakeHandler.Mode.QUEUEING)
// GIVEN bouncer should be delayed due to face auth
featureFlags.apply { set(Flags.DELAY_BOUNCER, true) }
whenever(keyguardStateController.isFaceAuthEnabled).thenReturn(true)
whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE))
.thenReturn(true)
@@ -420,26 +424,29 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
@Test
fun delayBouncerWhenActiveUnlockPossible() {
mainHandler.setMode(FakeHandler.Mode.QUEUEING)
testScope.run {
mainHandler.setMode(FakeHandler.Mode.QUEUEING)
// GIVEN bouncer should be delayed due to active unlock
featureFlags.apply { set(Flags.DELAY_BOUNCER, true) }
trustRepository.setCurrentUserActiveUnlockAvailable(true)
whenever(keyguardUpdateMonitor.canTriggerActiveUnlockBasedOnDeviceState()).thenReturn(true)
// GIVEN bouncer should be delayed due to active unlock
trustRepository.setCurrentUserActiveUnlockAvailable(true)
whenever(keyguardUpdateMonitor.canTriggerActiveUnlockBasedOnDeviceState())
.thenReturn(true)
runCurrent()
// WHEN bouncer show is requested
underTest.show(true)
// WHEN bouncer show is requested
underTest.show(true)
// THEN primary show & primary showing soon were scheduled to update
verify(repository, never()).setPrimaryShow(true)
verify(repository, never()).setPrimaryShowingSoon(false)
// THEN primary show & primary showing soon were scheduled to update
verify(repository, never()).setPrimaryShow(true)
verify(repository, never()).setPrimaryShowingSoon(false)
// WHEN all queued messages are dispatched
mainHandler.dispatchQueuedMessages()
// WHEN all queued messages are dispatched
mainHandler.dispatchQueuedMessages()
// THEN primary show & primary showing soon are updated
verify(repository).setPrimaryShow(true)
verify(repository).setPrimaryShowingSoon(false)
// THEN primary show & primary showing soon are updated
verify(repository).setPrimaryShow(true)
verify(repository).setPrimaryShowingSoon(false)
}
}
private fun updateSideFpsVisibilityParameters(

View File

@@ -26,6 +26,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.BouncerView
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
@@ -34,6 +35,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.utils.os.FakeHandler
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -75,7 +77,8 @@ class PrimaryBouncerInteractorWithCoroutinesTest : SysuiTestCase() {
context,
keyguardUpdateMonitor,
Mockito.mock(TrustRepository::class.java),
FakeFeatureFlags(),
FakeFeatureFlags().apply { set(Flags.DELAY_BOUNCER, true) },
TestScope().backgroundScope,
)
}

View File

@@ -25,6 +25,7 @@ import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.BouncerView
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
@@ -38,6 +39,7 @@ import com.android.systemui.utils.os.FakeHandler
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -81,7 +83,8 @@ class KeyguardBouncerViewModelTest : SysuiTestCase() {
context,
keyguardUpdateMonitor,
Mockito.mock(TrustRepository::class.java),
FakeFeatureFlags(),
FakeFeatureFlags().apply { set(Flags.DELAY_BOUNCER, true) },
TestScope().backgroundScope,
)
underTest = KeyguardBouncerViewModel(bouncerView, bouncerInteractor)
}

View File

@@ -28,7 +28,7 @@ class FakeTrustRepository : TrustRepository {
get() = _isCurrentUserTrusted
private val _isCurrentUserActiveUnlockAvailable = MutableStateFlow(false)
override val isCurrentUserActiveUnlockAvailable: StateFlow<Boolean> =
override val isCurrentUserActiveUnlockRunning: StateFlow<Boolean> =
_isCurrentUserActiveUnlockAvailable.asStateFlow()
private val _isCurrentUserTrustManaged = MutableStateFlow(false)