Merge "[flexiglass] Hooks up real app state and logic in keyguard (2)" into udc-qpr-dev

This commit is contained in:
Ale Nijamkin
2023-06-26 16:37:10 +00:00
committed by Android (Google) Code Review
20 changed files with 216 additions and 360 deletions

View File

@@ -19,17 +19,22 @@ package com.android.systemui.authentication.data.repository
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardSecurityModel
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.user.data.repository.UserRepository
import dagger.Binds
import dagger.Module
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.withContext
import java.util.function.Function
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
/** Defines interface for classes that can access authentication-related application state. */
interface AuthenticationRepository {
@@ -64,9 +69,6 @@ interface AuthenticationRepository {
*/
suspend fun getAuthenticationMethod(): AuthenticationMethodModel
/** See [isUnlocked]. */
fun setUnlocked(isUnlocked: Boolean)
/** See [isBypassEnabled]. */
fun setBypassEnabled(isBypassEnabled: Boolean)
@@ -77,14 +79,20 @@ interface AuthenticationRepository {
class AuthenticationRepositoryImpl
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
private val getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val userRepository: UserRepository,
private val lockPatternUtils: LockPatternUtils,
keyguardRepository: KeyguardRepository,
) : AuthenticationRepository {
private val _isUnlocked = MutableStateFlow(false)
override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow()
override val isUnlocked: StateFlow<Boolean> =
keyguardRepository.isKeyguardUnlocked.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
initialValue = false,
)
private val _isBypassEnabled = MutableStateFlow(false)
override val isBypassEnabled: StateFlow<Boolean> = _isBypassEnabled.asStateFlow()
@@ -128,10 +136,6 @@ constructor(
}
}
override fun setUnlocked(isUnlocked: Boolean) {
_isUnlocked.value = isUnlocked
}
override fun setBypassEnabled(isBypassEnabled: Boolean) {
_isBypassEnabled.value = isBypassEnabled
}

View File

@@ -89,22 +89,6 @@ constructor(
return !isUnlocked.value && getAuthenticationMethod().isSecure
}
/**
* Unlocks the device, assuming that the authentication challenge has been completed
* successfully.
*/
fun unlockDevice() {
repository.setUnlocked(true)
}
/**
* Locks the device. From now on, the device will remain locked until [authenticate] is called
* with the correct input.
*/
fun lockDevice() {
repository.setUnlocked(false)
}
/**
* Attempts to authenticate the user and unlock the device.
*
@@ -146,7 +130,6 @@ constructor(
if (isSuccessful) {
repository.setFailedAuthenticationAttempts(0)
repository.setUnlocked(true)
} else {
repository.setFailedAuthenticationAttempts(
repository.failedAuthenticationAttempts.value + 1
@@ -156,12 +139,6 @@ constructor(
return isSuccessful
}
/** Triggers a biometric-powered unlock of the device. */
fun biometricUnlock() {
// TODO(b/280883900): only allow this if the biometric is enabled and there's a match.
repository.setUnlocked(true)
}
/** See [isBypassEnabled]. */
fun toggleBypassEnabled() {
repository.setBypassEnabled(!repository.isBypassEnabled.value)

View File

@@ -35,6 +35,9 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -72,24 +75,28 @@ constructor(
val throttling: StateFlow<AuthenticationThrottledModel?> = repository.throttling
init {
// UNLOCKING SHOWS Gone.
//
// Move to the gone scene if the device becomes unlocked while on the bouncer scene.
applicationScope.launch {
sceneInteractor.currentScene(containerName).collect { currentScene ->
if (currentScene.key == SceneKey.Bouncer) {
when (getAuthenticationMethod()) {
is AuthenticationMethodModel.None ->
sceneInteractor.setCurrentScene(
containerName,
SceneModel(SceneKey.Gone),
)
is AuthenticationMethodModel.Swipe ->
sceneInteractor.setCurrentScene(
containerName,
SceneModel(SceneKey.Lockscreen),
)
else -> Unit
sceneInteractor
.currentScene(containerName)
.flatMapLatest { currentScene ->
if (currentScene.key == SceneKey.Bouncer) {
authenticationInteractor.isUnlocked
} else {
flowOf(false)
}
}
.distinctUntilChanged()
.collect { isUnlocked ->
if (isUnlocked) {
sceneInteractor.setCurrentScene(
containerName = containerName,
scene = SceneModel(SceneKey.Gone),
)
}
}
}
}
}
@@ -119,7 +126,6 @@ constructor(
scene = SceneModel(SceneKey.Bouncer),
)
} else {
authenticationInteractor.unlockDevice()
sceneInteractor.setCurrentScene(
containerName = containerName,
scene = SceneModel(SceneKey.Gone),

View File

@@ -22,17 +22,19 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.bouncer.shared.model.AuthenticationThrottledModel
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.util.kotlin.pairwise
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -83,25 +85,30 @@ constructor(
}
/** View-model for the current UI, based on the current authentication method. */
val authMethod: StateFlow<AuthMethodBouncerViewModel?>
get() =
flow {
emit(null)
emit(interactor.getAuthenticationMethod())
}
.map { authMethod ->
when (authMethod) {
is AuthenticationMethodModel.Pin -> pin
is AuthenticationMethodModel.Password -> password
is AuthenticationMethodModel.Pattern -> pattern
else -> null
private val _authMethod =
MutableSharedFlow<AuthMethodBouncerViewModel?>(
replay = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST,
)
val authMethod: StateFlow<AuthMethodBouncerViewModel?> =
_authMethod.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
initialValue = null,
)
init {
applicationScope.launch {
_authMethod.subscriptionCount
.pairwise()
.map { (previousCount, currentCount) -> currentCount > previousCount }
.collect { subscriberAdded ->
if (subscriberAdded) {
reloadAuthMethod()
}
}
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
initialValue = null,
)
}
}
/** The user-facing message to show in the bouncer. */
val message: StateFlow<MessageViewModel> =
@@ -184,6 +191,17 @@ constructor(
)
}
private suspend fun reloadAuthMethod() {
_authMethod.tryEmit(
when (interactor.getAuthenticationMethod()) {
is AuthenticationMethodModel.Pin -> pin
is AuthenticationMethodModel.Password -> password
is AuthenticationMethodModel.Pattern -> pattern
else -> null
}
)
}
data class MessageViewModel(
val text: String,

View File

@@ -42,10 +42,7 @@ class PinBouncerViewModel(
/** The length of the hinted PIN, or `null` if pin length hint should not be shown. */
val hintedPinLength: StateFlow<Int?> =
flow {
emit(null)
emit(interactor.getAuthenticationMethod())
}
flow { emit(interactor.getAuthenticationMethod()) }
.map { authMethod ->
// Hinting is enabled for 6-digit codes only
autoConfirmPinLength(authMethod).takeIf { it == HINTING_PASSCODE_LENGTH }

View File

@@ -302,7 +302,15 @@ constructor(
trySendWithFailureLogging(
keyguardStateController.isUnlocked,
TAG,
"updated isKeyguardUnlocked"
"updated isKeyguardUnlocked due to onUnlockedChanged"
)
}
override fun onKeyguardShowingChanged() {
trySendWithFailureLogging(
keyguardStateController.isUnlocked,
TAG,
"updated isKeyguardUnlocked due to onKeyguardShowingChanged"
)
}
}

View File

@@ -23,7 +23,6 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.util.kotlin.pairwise
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -111,24 +110,6 @@ constructor(
}
}
}
// SWIPE TO DISMISS Lockscreen.
//
// If switched from the lockscreen to the gone scene and the auth method was a swipe,
// unlocks the device.
applicationScope.launch {
sceneInteractor.currentScene(containerName).pairwise().collect {
(previousScene, currentScene) ->
if (
authenticationInteractor.getAuthenticationMethod() is
AuthenticationMethodModel.Swipe &&
previousScene.key == SceneKey.Lockscreen &&
currentScene.key == SceneKey.Gone
) {
authenticationInteractor.unlockDevice()
}
}
}
}
/** Attempts to dismiss the lockscreen. This will cause the bouncer to show, if needed. */

View File

@@ -65,30 +65,6 @@ class AuthenticationInteractorTest : SysuiTestCase() {
assertThat(isUnlocked).isTrue()
}
@Test
fun unlockDevice() =
testScope.runTest {
val isUnlocked by collectLastValue(underTest.isUnlocked)
assertThat(isUnlocked).isFalse()
underTest.unlockDevice()
runCurrent()
assertThat(isUnlocked).isTrue()
}
@Test
fun biometricUnlock() =
testScope.runTest {
val isUnlocked by collectLastValue(underTest.isUnlocked)
assertThat(isUnlocked).isFalse()
underTest.biometricUnlock()
runCurrent()
assertThat(isUnlocked).isTrue()
}
@Test
fun toggleBypassEnabled() =
testScope.runTest {
@@ -105,7 +81,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
@Test
fun isAuthenticationRequired_lockedAndSecured_true() =
testScope.runTest {
underTest.lockDevice()
utils.authenticationRepository.setUnlocked(false)
runCurrent()
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
@@ -117,7 +93,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
@Test
fun isAuthenticationRequired_lockedAndNotSecured_false() =
testScope.runTest {
underTest.lockDevice()
utils.authenticationRepository.setUnlocked(false)
runCurrent()
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
@@ -127,7 +103,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
@Test
fun isAuthenticationRequired_unlockedAndSecured_false() =
testScope.runTest {
underTest.unlockDevice()
utils.authenticationRepository.setUnlocked(true)
runCurrent()
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
@@ -139,7 +115,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
@Test
fun isAuthenticationRequired_unlockedAndNotSecured_false() =
testScope.runTest {
underTest.unlockDevice()
utils.authenticationRepository.setUnlocked(true)
runCurrent()
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
@@ -147,67 +123,55 @@ class AuthenticationInteractorTest : SysuiTestCase() {
}
@Test
fun authenticate_withCorrectPin_returnsTrueAndUnlocksDevice() =
fun authenticate_withCorrectPin_returnsTrue() =
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isTrue()
assertThat(isUnlocked).isTrue()
assertThat(failedAttemptCount).isEqualTo(0)
}
@Test
fun authenticate_withIncorrectPin_returnsFalseAndDoesNotUnlockDevice() =
fun authenticate_withIncorrectPin_returnsFalse() =
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(listOf(9, 8, 7))).isFalse()
assertThat(isUnlocked).isFalse()
assertThat(failedAttemptCount).isEqualTo(1)
}
@Test
fun authenticate_withEmptyPin_returnsFalseAndDoesNotUnlockDevice() =
fun authenticate_withEmptyPin_returnsFalse() =
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(listOf())).isFalse()
assertThat(isUnlocked).isFalse()
assertThat(failedAttemptCount).isEqualTo(1)
}
@Test
fun authenticate_withCorrectMaxLengthPin_returnsTrueAndUnlocksDevice() =
fun authenticate_withCorrectMaxLengthPin_returnsTrue() =
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(9999999999999999)
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(List(16) { 9 })).isTrue()
assertThat(isUnlocked).isTrue()
assertThat(failedAttemptCount).isEqualTo(0)
}
@Test
fun authenticate_withCorrectTooLongPin_returnsFalseAndDoesNotUnlockDevice() =
fun authenticate_withCorrectTooLongPin_returnsFalse() =
testScope.runTest {
// Max pin length is 16 digits. To avoid issues with overflows, this test ensures
// that all pins > 16 decimal digits are rejected.
@@ -216,52 +180,42 @@ class AuthenticationInteractorTest : SysuiTestCase() {
assertThat(DevicePolicyManager.MAX_PASSWORD_LENGTH).isLessThan(17)
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(99999999999999999)
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(List(17) { 9 })).isFalse()
assertThat(isUnlocked).isFalse()
assertThat(failedAttemptCount).isEqualTo(1)
}
@Test
fun authenticate_withCorrectPassword_returnsTrueAndUnlocksDevice() =
fun authenticate_withCorrectPassword_returnsTrue() =
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate("password".toList())).isTrue()
assertThat(isUnlocked).isTrue()
assertThat(failedAttemptCount).isEqualTo(0)
}
@Test
fun authenticate_withIncorrectPassword_returnsFalseAndDoesNotUnlockDevice() =
fun authenticate_withIncorrectPassword_returnsFalse() =
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate("alohomora".toList())).isFalse()
assertThat(isUnlocked).isFalse()
assertThat(failedAttemptCount).isEqualTo(1)
}
@Test
fun authenticate_withCorrectPattern_returnsTrueAndUnlocksDevice() =
fun authenticate_withCorrectPattern_returnsTrue() =
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern(
listOf(
@@ -280,7 +234,6 @@ class AuthenticationInteractorTest : SysuiTestCase() {
)
)
)
assertThat(isUnlocked).isFalse()
assertThat(
underTest.authenticate(
@@ -301,15 +254,13 @@ class AuthenticationInteractorTest : SysuiTestCase() {
)
)
.isTrue()
assertThat(isUnlocked).isTrue()
assertThat(failedAttemptCount).isEqualTo(0)
}
@Test
fun authenticate_withIncorrectPattern_returnsFalseAndDoesNotUnlockDevice() =
fun authenticate_withIncorrectPattern_returnsFalse() =
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern(
listOf(
@@ -328,7 +279,6 @@ class AuthenticationInteractorTest : SysuiTestCase() {
)
)
)
assertThat(isUnlocked).isFalse()
assertThat(
underTest.authenticate(
@@ -349,22 +299,18 @@ class AuthenticationInteractorTest : SysuiTestCase() {
)
)
.isFalse()
assertThat(isUnlocked).isFalse()
assertThat(failedAttemptCount).isEqualTo(1)
}
@Test
fun tryAutoConfirm_withAutoConfirmPinAndEmptyInput_returnsNullAndHasNoEffect() =
fun tryAutoConfirm_withAutoConfirmPinAndEmptyInput_returnsNull() =
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = true)
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(listOf(), tryAutoConfirm = true)).isNull()
assertThat(isUnlocked).isFalse()
assertThat(failedAttemptCount).isEqualTo(0)
}
@@ -372,14 +318,11 @@ class AuthenticationInteractorTest : SysuiTestCase() {
fun tryAutoConfirm_withAutoConfirmPinAndShorterPin_returnsNullAndHasNoEffect() =
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = true)
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(listOf(1, 2, 3), tryAutoConfirm = true)).isNull()
assertThat(isUnlocked).isFalse()
assertThat(failedAttemptCount).isEqualTo(0)
}
@@ -387,14 +330,11 @@ class AuthenticationInteractorTest : SysuiTestCase() {
fun tryAutoConfirm_withAutoConfirmWrongPinCorrectLength_returnsFalseAndDoesNotUnlockDevice() =
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = true)
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(listOf(1, 2, 4, 4), tryAutoConfirm = true)).isFalse()
assertThat(isUnlocked).isFalse()
assertThat(failedAttemptCount).isEqualTo(1)
}
@@ -402,15 +342,12 @@ class AuthenticationInteractorTest : SysuiTestCase() {
fun tryAutoConfirm_withAutoConfirmLongerPin_returnsFalseAndDoesNotUnlockDevice() =
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = true)
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(listOf(1, 2, 3, 4, 5), tryAutoConfirm = true))
.isFalse()
assertThat(isUnlocked).isFalse()
assertThat(failedAttemptCount).isEqualTo(1)
}
@@ -418,14 +355,11 @@ class AuthenticationInteractorTest : SysuiTestCase() {
fun tryAutoConfirm_withAutoConfirmCorrectPin_returnsTrueAndUnlocksDevice() =
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = true)
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(listOf(1, 2, 4, 4), tryAutoConfirm = true)).isFalse()
assertThat(isUnlocked).isFalse()
assertThat(failedAttemptCount).isEqualTo(1)
}
@@ -433,14 +367,11 @@ class AuthenticationInteractorTest : SysuiTestCase() {
fun tryAutoConfirm_withoutAutoConfirmButCorrectPin_returnsNullAndHasNoEffects() =
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = false)
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(listOf(1, 2, 3, 4), tryAutoConfirm = true)).isNull()
assertThat(isUnlocked).isFalse()
assertThat(failedAttemptCount).isEqualTo(0)
}
@@ -448,14 +379,11 @@ class AuthenticationInteractorTest : SysuiTestCase() {
fun tryAutoConfirm_withoutCorrectPassword_returnsNullAndHasNoEffects() =
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate("password".toList(), tryAutoConfirm = true)).isNull()
assertThat(isUnlocked).isFalse()
assertThat(failedAttemptCount).isEqualTo(0)
}
}

View File

@@ -71,7 +71,7 @@ class BouncerInteractorTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
underTest.showOrUnlockDevice("container1")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
@@ -104,7 +104,7 @@ class BouncerInteractorTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = true)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
underTest.showOrUnlockDevice("container1")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
@@ -134,7 +134,7 @@ class BouncerInteractorTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = false)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
underTest.showOrUnlockDevice("container1")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.clearMessage()
@@ -158,7 +158,7 @@ class BouncerInteractorTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
underTest.showOrUnlockDevice("container1")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD)
@@ -190,7 +190,7 @@ class BouncerInteractorTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern(emptyList())
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
underTest.showOrUnlockDevice("container1")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)
@@ -226,7 +226,7 @@ class BouncerInteractorTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.unlockDevice()
utils.authenticationRepository.setUnlocked(true)
runCurrent()
underTest.showOrUnlockDevice("container1")
@@ -239,7 +239,7 @@ class BouncerInteractorTest : SysuiTestCase() {
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene("container1"))
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
underTest.showOrUnlockDevice("container1")
@@ -254,7 +254,7 @@ class BouncerInteractorTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
val customMessage = "Hello there!"
underTest.showOrUnlockDevice("container1", customMessage)
@@ -268,13 +268,17 @@ class BouncerInteractorTest : SysuiTestCase() {
testScope.runTest {
val throttling by collectLastValue(underTest.throttling)
val message by collectLastValue(underTest.message)
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by
collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
runCurrent()
underTest.showOrUnlockDevice(SceneTestUtils.CONTAINER_1)
runCurrent()
assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer)
assertThat(throttling).isNull()
assertThat(message).isEqualTo("")
assertThat(isUnlocked).isFalse()
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
repeat(BouncerInteractor.THROTTLE_EVERY) { times ->
// Wrong PIN.
assertThat(underTest.authenticate(listOf(6, 7, 8, 9))).isFalse()
@@ -285,9 +289,9 @@ class BouncerInteractorTest : SysuiTestCase() {
assertThat(throttling).isNotNull()
assertTryAgainMessage(message, BouncerInteractor.THROTTLE_DURATION_SEC)
// Correct PIN, but throttled, so doesn't unlock:
// Correct PIN, but throttled, so doesn't change away from the bouncer scene:
assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isFalse()
assertThat(isUnlocked).isFalse()
assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer)
assertTryAgainMessage(message, BouncerInteractor.THROTTLE_DURATION_SEC)
throttling?.totalDurationSec?.let { seconds ->
@@ -301,11 +305,28 @@ class BouncerInteractorTest : SysuiTestCase() {
}
assertThat(message).isEqualTo("")
assertThat(throttling).isNull()
assertThat(isUnlocked).isFalse()
assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer)
// Correct PIN and no longer throttled so unlocks:
// Correct PIN and no longer throttled so changes to the Gone scene:
assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isTrue()
assertThat(isUnlocked).isTrue()
assertThat(currentScene?.key).isEqualTo(SceneKey.Gone)
}
@Test
fun switchesToGone_whenUnlocked() =
testScope.runTest {
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(
SceneTestUtils.CONTAINER_1,
SceneModel(SceneKey.Bouncer)
)
val currentScene by
collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1))
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
utils.authenticationRepository.setUnlocked(true)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
private fun assertTryAgainMessage(

View File

@@ -23,10 +23,14 @@ import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -53,16 +57,26 @@ class BouncerViewModelTest : SysuiTestCase() {
@Test
fun authMethod_nonNullForSecureMethods_nullForNotSecureMethods() =
testScope.runTest {
var authMethodViewModel: AuthMethodBouncerViewModel? = null
authMethodsToTest().forEach { authMethod ->
utils.authenticationRepository.setAuthenticationMethod(authMethod)
val job = underTest.authMethod.onEach { authMethodViewModel = it }.launchIn(this)
runCurrent()
val authMethodViewModel: AuthMethodBouncerViewModel? by
collectLastValue(underTest.authMethod)
if (authMethod.isSecure) {
assertThat(authMethodViewModel).isNotNull()
assertWithMessage("View-model unexpectedly null for auth method $authMethod")
.that(authMethodViewModel)
.isNotNull()
} else {
assertThat(authMethodViewModel).isNull()
assertWithMessage(
"View-model unexpectedly non-null for auth method $authMethod"
)
.that(authMethodViewModel)
.isNull()
}
job.cancel()
}
}

View File

@@ -72,39 +72,34 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
@Test
fun onShown() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val password by collectLastValue(underTest.password)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
assertThat(message?.text).isEqualTo(ENTER_YOUR_PASSWORD)
assertThat(password).isEqualTo("")
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
fun onPasswordInputChanged() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val password by collectLastValue(underTest.password)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
runCurrent()
@@ -113,44 +108,38 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
assertThat(message?.text).isEmpty()
assertThat(password).isEqualTo("password")
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
fun onAuthenticateKeyPressed_whenCorrect() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onPasswordInputChanged("password")
underTest.onAuthenticateKeyPressed()
assertThat(isUnlocked).isTrue()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@Test
fun onAuthenticateKeyPressed_whenWrong() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val password by collectLastValue(underTest.password)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onPasswordInputChanged("wrong")
@@ -159,30 +148,26 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
assertThat(password).isEqualTo("")
assertThat(message?.text).isEqualTo(WRONG_PASSWORD)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
fun onAuthenticateKeyPressed_correctAfterWrong() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val password by collectLastValue(underTest.password)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onPasswordInputChanged("wrong")
underTest.onAuthenticateKeyPressed()
assertThat(password).isEqualTo("")
assertThat(message?.text).isEqualTo(WRONG_PASSWORD)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
// Enter the correct password:
@@ -191,7 +176,6 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
underTest.onAuthenticateKeyPressed()
assertThat(isUnlocked).isTrue()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}

View File

@@ -74,7 +74,6 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
@Test
fun onShown() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val selectedDots by collectLastValue(underTest.selectedDots)
@@ -82,9 +81,8 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern(CORRECT_PATTERN)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
@@ -92,14 +90,12 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
assertThat(message?.text).isEqualTo(ENTER_YOUR_PATTERN)
assertThat(selectedDots).isEmpty()
assertThat(currentDot).isNull()
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
fun onDragStart() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val selectedDots by collectLastValue(underTest.selectedDots)
@@ -107,9 +103,8 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern(CORRECT_PATTERN)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
runCurrent()
@@ -119,23 +114,20 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
assertThat(message?.text).isEmpty()
assertThat(selectedDots).isEmpty()
assertThat(currentDot).isNull()
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
fun onDragEnd_whenCorrect() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val selectedDots by collectLastValue(underTest.selectedDots)
val currentDot by collectLastValue(underTest.currentDot)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern(CORRECT_PATTERN)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onDragStart()
@@ -169,14 +161,12 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
underTest.onDragEnd()
assertThat(isUnlocked).isTrue()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@Test
fun onDragEnd_whenWrong() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val selectedDots by collectLastValue(underTest.selectedDots)
@@ -184,9 +174,8 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern(CORRECT_PATTERN)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onDragStart()
@@ -204,14 +193,12 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
assertThat(selectedDots).isEmpty()
assertThat(currentDot).isNull()
assertThat(message?.text).isEqualTo(WRONG_PATTERN)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
fun onDragEnd_correctAfterWrong() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val selectedDots by collectLastValue(underTest.selectedDots)
@@ -219,9 +206,8 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern(CORRECT_PATTERN)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onDragStart()
@@ -237,7 +223,6 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
assertThat(selectedDots).isEmpty()
assertThat(currentDot).isNull()
assertThat(message?.text).isEqualTo(WRONG_PATTERN)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
// Enter the correct pattern:
@@ -252,7 +237,6 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
underTest.onDragEnd()
assertThat(isUnlocked).isTrue()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}

View File

@@ -81,36 +81,31 @@ class PinBouncerViewModelTest : SysuiTestCase() {
@Test
fun onShown() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val entries by collectLastValue(underTest.pinEntries)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
assertThat(message?.text).isEqualTo(ENTER_YOUR_PIN)
assertThat(entries).hasSize(0)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
fun onPinButtonClicked() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val entries by collectLastValue(underTest.pinEntries)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
runCurrent()
@@ -120,23 +115,20 @@ class PinBouncerViewModelTest : SysuiTestCase() {
assertThat(message?.text).isEmpty()
assertThat(entries).hasSize(1)
assertThat(entries?.map { it.input }).containsExactly(1)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
fun onBackspaceButtonClicked() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val entries by collectLastValue(underTest.pinEntries)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
runCurrent()
@@ -147,23 +139,19 @@ class PinBouncerViewModelTest : SysuiTestCase() {
assertThat(message?.text).isEmpty()
assertThat(entries).hasSize(0)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
fun onPinEdit() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val entries by collectLastValue(underTest.pinEntries)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
@@ -183,16 +171,14 @@ class PinBouncerViewModelTest : SysuiTestCase() {
@Test
fun onBackspaceButtonLongPressed() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val entries by collectLastValue(underTest.pinEntries)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
runCurrent()
@@ -205,21 +191,18 @@ class PinBouncerViewModelTest : SysuiTestCase() {
assertThat(message?.text).isEmpty()
assertThat(entries).hasSize(0)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
fun onAuthenticateButtonClicked_whenCorrect() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onPinButtonClicked(1)
@@ -229,23 +212,20 @@ class PinBouncerViewModelTest : SysuiTestCase() {
underTest.onAuthenticateButtonClicked()
assertThat(isUnlocked).isTrue()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@Test
fun onAuthenticateButtonClicked_whenWrong() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val entries by collectLastValue(underTest.pinEntries)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onPinButtonClicked(1)
@@ -258,23 +238,20 @@ class PinBouncerViewModelTest : SysuiTestCase() {
assertThat(entries).hasSize(0)
assertThat(message?.text).isEqualTo(WRONG_PIN)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
fun onAuthenticateButtonClicked_correctAfterWrong() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val entries by collectLastValue(underTest.pinEntries)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onPinButtonClicked(1)
@@ -285,7 +262,6 @@ class PinBouncerViewModelTest : SysuiTestCase() {
underTest.onAuthenticateButtonClicked()
assertThat(message?.text).isEqualTo(WRONG_PIN)
assertThat(entries).hasSize(0)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
// Enter the correct PIN:
@@ -297,21 +273,18 @@ class PinBouncerViewModelTest : SysuiTestCase() {
underTest.onAuthenticateButtonClicked()
assertThat(isUnlocked).isTrue()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@Test
fun onAutoConfirm_whenCorrect() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = true)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onPinButtonClicked(1)
@@ -319,23 +292,20 @@ class PinBouncerViewModelTest : SysuiTestCase() {
underTest.onPinButtonClicked(3)
underTest.onPinButtonClicked(4)
assertThat(isUnlocked).isTrue()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@Test
fun onAutoConfirm_whenWrong() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val entries by collectLastValue(underTest.pinEntries)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = true)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onPinButtonClicked(1)
@@ -345,7 +315,6 @@ class PinBouncerViewModelTest : SysuiTestCase() {
assertThat(entries).hasSize(0)
assertThat(message?.text).isEqualTo(WRONG_PIN)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}

View File

@@ -235,11 +235,10 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
fun isKeyguardUnlocked() =
testScope.runTest {
whenever(keyguardStateController.isUnlocked).thenReturn(false)
var latest: Boolean? = null
val job = underTest.isKeyguardUnlocked.onEach { latest = it }.launchIn(this)
val isKeyguardUnlocked by collectLastValue(underTest.isKeyguardUnlocked)
runCurrent()
assertThat(latest).isFalse()
assertThat(isKeyguardUnlocked).isFalse()
val captor = argumentCaptor<KeyguardStateController.Callback>()
verify(keyguardStateController).addCallback(captor.capture())
@@ -247,14 +246,12 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
whenever(keyguardStateController.isUnlocked).thenReturn(true)
captor.value.onUnlockedChanged()
runCurrent()
assertThat(latest).isTrue()
assertThat(isKeyguardUnlocked).isTrue()
whenever(keyguardStateController.isUnlocked).thenReturn(false)
captor.value.onUnlockedChanged()
captor.value.onKeyguardShowingChanged()
runCurrent()
assertThat(latest).isFalse()
job.cancel()
assertThat(isKeyguardUnlocked).isFalse()
}
@Test

View File

@@ -60,10 +60,10 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
testScope.runTest {
val isDeviceLocked by collectLastValue(underTest.isDeviceLocked)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
assertThat(isDeviceLocked).isTrue()
authenticationInteractor.unlockDevice()
utils.authenticationRepository.setUnlocked(true)
assertThat(isDeviceLocked).isFalse()
}
@@ -72,7 +72,7 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
testScope.runTest {
val isSwipeToDismissEnabled by collectLastValue(underTest.isSwipeToDismissEnabled)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
assertThat(isSwipeToDismissEnabled).isTrue()
@@ -83,7 +83,7 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
testScope.runTest {
val isSwipeToDismissEnabled by collectLastValue(underTest.isSwipeToDismissEnabled)
authenticationInteractor.unlockDevice()
utils.authenticationRepository.setUnlocked(true)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
assertThat(isSwipeToDismissEnabled).isFalse()
@@ -93,7 +93,7 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
fun dismissLockScreen_deviceLockedWithSecureAuthMethod_switchesToBouncer() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
@@ -108,7 +108,7 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
fun dismissLockScreen_deviceUnlocked_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.unlockDevice()
utils.authenticationRepository.setUnlocked(true)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
@@ -123,7 +123,7 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
fun dismissLockScreen_deviceLockedWithInsecureAuthMethod_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
@@ -139,60 +139,15 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
runCurrent()
sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Gone))
runCurrent()
authenticationInteractor.unlockDevice()
utils.authenticationRepository.setUnlocked(true)
runCurrent()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
}
@Test
fun deviceBiometricUnlockedInLockScreen_bypassEnabled_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Lockscreen))
if (!authenticationInteractor.isBypassEnabled.value) {
authenticationInteractor.toggleBypassEnabled()
}
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
authenticationInteractor.biometricUnlock()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@Test
fun deviceBiometricUnlockedInLockScreen_bypassNotEnabled_doesNotSwitch() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Lockscreen))
if (authenticationInteractor.isBypassEnabled.value) {
authenticationInteractor.toggleBypassEnabled()
}
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
authenticationInteractor.biometricUnlock()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
}
@Test
fun switchFromLockScreenToGone_authMethodSwipe_unlocksDevice() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Lockscreen))
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
assertThat(isUnlocked).isFalse()
sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Gone))
assertThat(isUnlocked).isTrue()
}
@Test
fun switchFromLockScreenToGone_authMethodNotSwipe_doesNotUnlockDevice() =
testScope.runTest {

View File

@@ -75,7 +75,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
assertThat((lockButtonIcon as? Icon.Resource)?.res)
.isEqualTo(R.drawable.ic_device_lock_on)
@@ -88,7 +88,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
authenticationInteractor.unlockDevice()
utils.authenticationRepository.setUnlocked(true)
assertThat((lockButtonIcon as? Icon.Resource)?.res)
.isEqualTo(R.drawable.ic_device_lock_off)
@@ -99,7 +99,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone)
}
@@ -111,7 +111,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer)
}
@@ -123,7 +123,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
runCurrent()
underTest.onLockButtonClicked()
@@ -138,7 +138,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.unlockDevice()
utils.authenticationRepository.setUnlocked(true)
runCurrent()
underTest.onContentClicked()
@@ -153,7 +153,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
runCurrent()
underTest.onContentClicked()
@@ -168,7 +168,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.unlockDevice()
utils.authenticationRepository.setUnlocked(true)
runCurrent()
underTest.onLockButtonClicked()

View File

@@ -72,7 +72,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.unlockDevice()
utils.authenticationRepository.setUnlocked(true)
runCurrent()
underTest.onContentClicked()
@@ -87,7 +87,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
runCurrent()
underTest.onContentClicked()

View File

@@ -73,7 +73,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Lockscreen)
}
@@ -85,7 +85,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.unlockDevice()
utils.authenticationRepository.setUnlocked(true)
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone)
}
@@ -97,7 +97,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.unlockDevice()
utils.authenticationRepository.setUnlocked(true)
runCurrent()
underTest.onContentClicked()
@@ -112,7 +112,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
utils.authenticationRepository.setUnlocked(false)
runCurrent()
underTest.onContentClicked()

View File

@@ -18,12 +18,18 @@ package com.android.systemui.authentication.data.repository
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
class FakeAuthenticationRepository(
private val delegate: AuthenticationRepository,
private val onSecurityModeChanged: (SecurityMode) -> Unit,
) : AuthenticationRepository by delegate {
private val _isUnlocked = MutableStateFlow(false)
override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow()
private var authenticationMethod: AuthenticationMethodModel = DEFAULT_AUTHENTICATION_METHOD
override suspend fun getAuthenticationMethod(): AuthenticationMethodModel {
@@ -35,6 +41,10 @@ class FakeAuthenticationRepository(
onSecurityModeChanged(authenticationMethod.toSecurityMode())
}
fun setUnlocked(isUnlocked: Boolean) {
_isUnlocked.value = isUnlocked
}
companion object {
val DEFAULT_AUTHENTICATION_METHOD =
AuthenticationMethodModel.Pin(listOf(1, 2, 3, 4), autoConfirm = false)

View File

@@ -26,6 +26,7 @@ import com.android.systemui.authentication.domain.interactor.AuthenticationInter
import com.android.systemui.bouncer.data.repository.BouncerRepository
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor
import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.domain.interactor.SceneInteractor
@@ -55,10 +56,12 @@ class SceneTestUtils(
FakeAuthenticationRepository(
delegate =
AuthenticationRepositoryImpl(
applicationScope = applicationScope(),
getSecurityMode = { securityMode },
backgroundDispatcher = testDispatcher,
userRepository = FakeUserRepository(),
lockPatternUtils = mock(),
keyguardRepository = FakeKeyguardRepository(),
),
onSecurityModeChanged = { securityMode = it },
)