diff options
| author | 2023-09-30 18:33:41 -0700 | |
|---|---|---|
| committer | 2023-11-12 05:39:14 +0530 | |
| commit | 27cb2531e9fd9d061a7ba96c8d9a681c4aa59094 (patch) | |
| tree | a5a6a66e454745e9cfb113c65cfbc6e7de4efc43 | |
| parent | a4ee0ad0997e66c829b0611f8b59166b6ab4d2d3 (diff) | |
Move device unlock initiation to DeviceEntryInteractor
Scene transitions from bouncer are managed by DeviceEntryInteractor instead of the BouncerInteractor
Bug: 310005730
Test: all affected unit tests
Flag: ACONFIG com.android.systemui.scene_container DEVELOPMENT
Change-Id: Ia3beab0f183eec06289f15f75c91a67a51087b7e
26 files changed, 232 insertions, 274 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index f0915583f021..07359d1b446c 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -60,10 +60,10 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.AuthRippleController; import com.android.systemui.biometrics.UdfpsController; import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; -import com.android.systemui.bouncer.domain.interactor.BouncerInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; @@ -127,7 +127,7 @@ public class LockIconViewController implements Dumpable { @NonNull private final KeyguardTransitionInteractor mTransitionInteractor; @NonNull private final KeyguardInteractor mKeyguardInteractor; @NonNull private final View.AccessibilityDelegate mAccessibilityDelegate; - @NonNull private final Lazy<BouncerInteractor> mBouncerInteractor; + @NonNull private final Lazy<DeviceEntryInteractor> mDeviceEntryInteractor; @NonNull private final SceneContainerFlags mSceneContainerFlags; // Tracks the velocity of a touch to help filter out the touches that move too fast. @@ -205,7 +205,7 @@ public class LockIconViewController implements Dumpable { @NonNull FeatureFlags featureFlags, PrimaryBouncerInteractor primaryBouncerInteractor, Context context, - Lazy<BouncerInteractor> bouncerInteractor, + Lazy<DeviceEntryInteractor> deviceEntryInteractor, SceneContainerFlags sceneContainerFlags ) { mStatusBarStateController = statusBarStateController; @@ -232,7 +232,7 @@ public class LockIconViewController implements Dumpable { dumpManager.registerDumpable(TAG, this); mResources = resources; mContext = context; - mBouncerInteractor = bouncerInteractor; + mDeviceEntryInteractor = deviceEntryInteractor; mSceneContainerFlags = sceneContainerFlags; mAccessibilityDelegate = new View.AccessibilityDelegate() { @@ -747,7 +747,7 @@ public class LockIconViewController implements Dumpable { vibrateOnLongPress(); if (mSceneContainerFlags.isEnabled()) { - mBouncerInteractor.get().showOrUnlockDevice(null); + mDeviceEntryInteractor.get().attemptDeviceEntry(); } else { mKeyguardViewController.showPrimaryBouncer(/* scrim */ true); } diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt index e8b16db10da5..14cbf76b4883 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt @@ -45,7 +45,9 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine @@ -68,6 +70,12 @@ interface AuthenticationRepository { val isAutoConfirmFeatureEnabled: StateFlow<Boolean> /** + * Emits the result whenever a PIN/Pattern/Password security challenge is attempted by the user + * in order to unlock the device. + */ + val authenticationChallengeResult: SharedFlow<Boolean> + + /** * The exact length a PIN should be for us to enable PIN length hinting. * * A PIN that's shorter or longer than this is not eligible for the UI to render hints showing @@ -164,6 +172,7 @@ constructor( initialValue = false, getFreshValue = lockPatternUtils::isAutoPinConfirmEnabled, ) + override val authenticationChallengeResult = MutableSharedFlow<Boolean>() override val hintedPinLength: Int = 6 @@ -224,6 +233,7 @@ constructor( } else { lockPatternUtils.reportFailedPasswordAttempt(selectedUserId) } + authenticationChallengeResult.emit(isSuccessful) } } diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt index 22b44d56fe9b..321293f78117 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt @@ -16,7 +16,6 @@ package com.android.systemui.authentication.domain.interactor -import com.android.app.tracing.TraceUtils.Companion.async import com.android.app.tracing.TraceUtils.Companion.withContext import com.android.internal.widget.LockPatternView import com.android.internal.widget.LockscreenCredential @@ -40,6 +39,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.async import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine @@ -47,7 +47,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext /** * Hosts application business logic related to user authentication. @@ -143,6 +142,13 @@ constructor( /** Whether the pattern should be visible for the currently-selected user. */ val isPatternVisible: StateFlow<Boolean> = repository.isPatternVisible + /** + * Emits the outcome (successful or unsuccessful) whenever a PIN/Pattern/Password security + * challenge is attempted by the user in order to unlock the device. + */ + val authenticationChallengeResult: SharedFlow<Boolean> = + repository.authenticationChallengeResult + private var throttlingCountdownJob: Job? = null init { diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt index b9a913ea866f..94f1c2dd7c05 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt @@ -26,7 +26,6 @@ import com.android.systemui.classifier.FalsingClassifier import com.android.systemui.classifier.domain.interactor.FalsingInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlags @@ -51,7 +50,6 @@ constructor( @Application private val applicationScope: CoroutineScope, @Application private val applicationContext: Context, private val repository: BouncerRepository, - private val deviceEntryInteractor: DeviceEntryInteractor, private val authenticationInteractor: AuthenticationInteractor, private val sceneInteractor: SceneInteractor, flags: SceneContainerFlags, @@ -142,32 +140,6 @@ constructor( } /** - * Either shows the bouncer or unlocks the device, if the bouncer doesn't need to be shown. - * - * @param message An optional message to show to the user in the bouncer. - */ - fun showOrUnlockDevice( - message: String? = null, - ) { - applicationScope.launch { - if (deviceEntryInteractor.isAuthenticationRequired()) { - repository.setMessage( - message ?: promptMessage(authenticationInteractor.getAuthenticationMethod()) - ) - sceneInteractor.changeScene( - scene = SceneModel(SceneKey.Bouncer), - loggingReason = "request to unlock device while authentication required", - ) - } else { - sceneInteractor.changeScene( - scene = SceneModel(SceneKey.Gone), - loggingReason = "request to unlock device while authentication isn't required", - ) - } - } - } - - /** * Resets the user-facing message back to the default according to the current authentication * method. */ @@ -212,17 +184,11 @@ constructor( return applicationScope .async { val authResult = authenticationInteractor.authenticate(input, tryAutoConfirm) - when (authResult) { - // Authentication succeeded. - AuthenticationResult.SUCCEEDED -> - sceneInteractor.changeScene( - scene = SceneModel(SceneKey.Gone), - loggingReason = "successful authentication", - ) - // Authentication failed. - AuthenticationResult.FAILED -> showErrorMessage() - // Authentication skipped. - AuthenticationResult.SKIPPED -> if (!tryAutoConfirm) showErrorMessage() + if ( + authResult == AuthenticationResult.FAILED || + (authResult == AuthenticationResult.SKIPPED && !tryAutoConfirm) + ) { + showErrorMessage() } authResult } diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt index 1e29e1fa3197..64fd2b3c0baa 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt @@ -1,5 +1,6 @@ package com.android.systemui.deviceentry.data.repository +import android.util.Log import com.android.internal.widget.LockPatternUtils import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow @@ -15,9 +16,12 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext @@ -40,6 +44,9 @@ interface DeviceEntryRepository { */ suspend fun isInsecureLockscreenEnabled(): Boolean + /** Report successful authentication for device entry. */ + fun reportSuccessfulAuthentication() + /** * Whether lockscreen bypass is enabled. When enabled, the lockscreen will be automatically * dismissed once the authentication challenge is completed. @@ -67,7 +74,9 @@ constructor( keyguardStateController: KeyguardStateController, ) : DeviceEntryRepository { - override val isUnlocked = + private val _isUnlocked = MutableStateFlow(false) + + private val isUnlockedReportedByLegacyKeyguard = conflatedCallbackFlow { val callback = object : KeyguardStateController.Callback { @@ -99,12 +108,15 @@ constructor( awaitClose { keyguardStateController.removeCallback(callback) } } .distinctUntilChanged() + .onEach { _isUnlocked.value = it } .stateIn( applicationScope, SharingStarted.Eagerly, initialValue = false, ) + override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow() + override suspend fun isInsecureLockscreenEnabled(): Boolean { return withContext(backgroundDispatcher) { val selectedUserId = userRepository.getSelectedUserInfo().id @@ -112,6 +124,11 @@ constructor( } } + override fun reportSuccessfulAuthentication() { + Log.d(TAG, "Successful authentication reported.") + _isUnlocked.value = true + } + override val isBypassEnabled: StateFlow<Boolean> = conflatedCallbackFlow { val listener = diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt index e872d13bd913..2f9087b7cae5 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt @@ -24,16 +24,20 @@ import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.TrustRepository import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.SceneModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch /** * Hosts application business logic related to device entry. @@ -48,9 +52,10 @@ constructor( @Application private val applicationScope: CoroutineScope, repository: DeviceEntryRepository, private val authenticationInteractor: AuthenticationInteractor, - sceneInteractor: SceneInteractor, + private val sceneInteractor: SceneInteractor, deviceEntryFaceAuthRepository: DeviceEntryFaceAuthRepository, trustRepository: TrustRepository, + flags: SceneContainerFlags, ) { /** * Whether the device is unlocked. @@ -125,6 +130,32 @@ constructor( ) /** + * Attempt to enter the device and dismiss the lockscreen. If authentication is required to + * unlock the device it will transition to bouncer. + */ + fun attemptDeviceEntry() { + // TODO (b/307768356), + // 1. Check if the device is already authenticated by trust agent/passive biometrics + // 2. show SPFS/UDFPS bouncer if it is available AlternateBouncerInteractor.show + // 3. For face auth only setups trigger face auth, delay transitioning to bouncer for + // a small amount of time. + // 4. Transition to bouncer scene + applicationScope.launch { + if (isAuthenticationRequired()) { + sceneInteractor.changeScene( + scene = SceneModel(SceneKey.Bouncer), + loggingReason = "request to unlock device while authentication required", + ) + } else { + sceneInteractor.changeScene( + scene = SceneModel(SceneKey.Gone), + loggingReason = "request to unlock device while authentication isn't required", + ) + } + } + } + + /** * Returns `true` if the device currently requires authentication before entry is granted; * `false` if the device can be entered without authenticating first. */ @@ -139,4 +170,16 @@ constructor( * lock screen. */ val isBypassEnabled: StateFlow<Boolean> = repository.isBypassEnabled + + init { + if (flags.isEnabled()) { + applicationScope.launch { + authenticationInteractor.authenticationChallengeResult.collectLatest { successful -> + if (successful) { + repository.reportSuccessfulAuthentication() + } + } + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt index 9edd2c6cf927..5993cf104318 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt @@ -16,8 +16,8 @@ package com.android.systemui.qs.ui.viewmodel -import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel import javax.inject.Inject @@ -26,11 +26,9 @@ import javax.inject.Inject class QuickSettingsSceneViewModel @Inject constructor( - private val bouncerInteractor: BouncerInteractor, + private val deviceEntryInteractor: DeviceEntryInteractor, val shadeHeaderViewModel: ShadeHeaderViewModel, ) { /** Notifies that some content in quick settings was clicked. */ - fun onContentClicked() { - bouncerInteractor.showOrUnlockDevice() - } + fun onContentClicked() = deviceEntryInteractor.attemptDeviceEntry() } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt index 9c5a20189dd2..20b9edee2d70 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt @@ -16,7 +16,6 @@ package com.android.systemui.shade.ui.viewmodel -import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor @@ -34,8 +33,7 @@ class ShadeSceneViewModel @Inject constructor( @Application private val applicationScope: CoroutineScope, - deviceEntryInteractor: DeviceEntryInteractor, - private val bouncerInteractor: BouncerInteractor, + private val deviceEntryInteractor: DeviceEntryInteractor, val shadeHeaderViewModel: ShadeHeaderViewModel, ) { /** The key of the scene we should switch to when swiping up. */ @@ -60,9 +58,7 @@ constructor( ) /** Notifies that some content in the shade was clicked. */ - fun onContentClicked() { - bouncerInteractor.showOrUnlockDevice() - } + fun onContentClicked() = deviceEntryInteractor.attemptDeviceEntry() private fun upDestinationSceneKey( isUnlocked: Boolean, diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java index 1d4f2cbe6b64..d2f45ae8685a 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java @@ -42,8 +42,8 @@ import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.AuthRippleController; -import com.android.systemui.bouncer.domain.interactor.BouncerInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor; import com.android.systemui.doze.util.BurnInHelperKt; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; @@ -78,7 +78,7 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { protected MockitoSession mStaticMockSession; protected final SceneTestUtils mSceneTestUtils = new SceneTestUtils(this); - protected @Mock BouncerInteractor mBouncerInteractor; + protected @Mock DeviceEntryInteractor mDeviceEntryInteractor; protected @Mock LockIconView mLockIconView; protected @Mock AnimatedStateListDrawable mIconDrawable; protected @Mock Context mContext; @@ -176,7 +176,7 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { mFeatureFlags, mPrimaryBouncerInteractor, mContext, - () -> mBouncerInteractor, + () -> mDeviceEntryInteractor, mSceneTestUtils.getSceneContainerFlags() ); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java index adcec10f9172..93a5393b41cf 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java @@ -381,7 +381,7 @@ public class LockIconViewControllerTest extends LockIconViewControllerBaseTest { // THEN show primary bouncer via keyguard view controller, not scene container verify(mKeyguardViewController).showPrimaryBouncer(anyBoolean()); - verify(mBouncerInteractor, never()).showOrUnlockDevice(any()); + verify(mDeviceEntryInteractor, never()).attemptDeviceEntry(); } @Test @@ -395,7 +395,7 @@ public class LockIconViewControllerTest extends LockIconViewControllerBaseTest { // THEN show primary bouncer verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean()); - verify(mBouncerInteractor).showOrUnlockDevice(any()); + verify(mDeviceEntryInteractor).attemptDeviceEntry(); } @Test @@ -408,6 +408,7 @@ public class LockIconViewControllerTest extends LockIconViewControllerBaseTest { mUnderTest.onLongPress(); // THEN don't show primary bouncer - verify(mBouncerInteractor, never()).showOrUnlockDevice(any()); + verify(mDeviceEntryInteractor, never()).attemptDeviceEntry(); + verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt index 0c06808b10c9..bdb2eea37413 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt @@ -127,6 +127,22 @@ class AuthenticationRepositoryTest : SysuiTestCase() { assertThat(values.last()).isTrue() } + @Test + fun reportAuthenticationAttempt_emitsAuthenticationChallengeResult() = + testScope.runTest { + val authenticationChallengeResults by + collectValues(underTest.authenticationChallengeResult) + + runCurrent() + underTest.reportAuthenticationAttempt(true) + runCurrent() + underTest.reportAuthenticationAttempt(false) + runCurrent() + underTest.reportAuthenticationAttempt(true) + + assertThat(authenticationChallengeResults).isEqualTo(listOf(true, false, true)) + } + private fun setSecurityModeAndDispatchBroadcast( securityMode: KeyguardSecurityModel.SecurityMode, ) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt index 103f2b8ac313..ed059b562f34 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt @@ -211,7 +211,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { } @Test - fun tryAutoConfirm_withAutoConfirmPinAndShorterPin_returnsNullAndHasNoEffect() = + fun tryAutoConfirm_withAutoConfirmPinAndShorterPin_returnsNull() = testScope.runTest { val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled) val isThrottled by collectLastValue(underTest.isThrottled) @@ -234,7 +234,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { } @Test - fun tryAutoConfirm_withAutoConfirmWrongPinCorrectLength_returnsFalseAndDoesNotUnlockDevice() = + fun tryAutoConfirm_withAutoConfirmWrongPinCorrectLength_returnsFalse() = testScope.runTest { val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled) utils.authenticationRepository.apply { @@ -250,12 +250,10 @@ class AuthenticationInteractorTest : SysuiTestCase() { ) ) .isEqualTo(AuthenticationResult.FAILED) - val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked) - assertThat(isUnlocked).isFalse() } @Test - fun tryAutoConfirm_withAutoConfirmLongerPin_returnsFalseAndDoesNotUnlockDevice() = + fun tryAutoConfirm_withAutoConfirmLongerPin_returnsFalse() = testScope.runTest { val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled) utils.authenticationRepository.apply { @@ -271,12 +269,10 @@ class AuthenticationInteractorTest : SysuiTestCase() { ) ) .isEqualTo(AuthenticationResult.FAILED) - val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked) - assertThat(isUnlocked).isFalse() } @Test - fun tryAutoConfirm_withAutoConfirmCorrectPin_returnsTrueAndUnlocksDevice() = + fun tryAutoConfirm_withAutoConfirmCorrectPin_returnsTrue() = testScope.runTest { val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled) utils.authenticationRepository.apply { @@ -292,12 +288,10 @@ class AuthenticationInteractorTest : SysuiTestCase() { ) ) .isEqualTo(AuthenticationResult.SUCCEEDED) - val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked) - assertThat(isUnlocked).isTrue() } @Test - fun tryAutoConfirm_withAutoConfirmCorrectPinButDuringThrottling_returnsNullAndHasNoEffects() = + fun tryAutoConfirm_withAutoConfirmCorrectPinButDuringThrottling_returnsNull() = testScope.runTest { val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled) val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked) @@ -321,7 +315,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { } @Test - fun tryAutoConfirm_withoutAutoConfirmButCorrectPin_returnsNullAndHasNoEffects() = + fun tryAutoConfirm_withoutAutoConfirmButCorrectPin_returnsNull() = testScope.runTest { utils.authenticationRepository.apply { setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin) @@ -334,12 +328,10 @@ class AuthenticationInteractorTest : SysuiTestCase() { ) ) .isEqualTo(AuthenticationResult.SKIPPED) - val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked) - assertThat(isUnlocked).isFalse() } @Test - fun tryAutoConfirm_withoutCorrectPassword_returnsNullAndHasNoEffects() = + fun tryAutoConfirm_withoutCorrectPassword_returnsNull() = testScope.runTest { utils.authenticationRepository.setAuthenticationMethod( DataLayerAuthenticationMethodModel.Password @@ -347,40 +339,29 @@ class AuthenticationInteractorTest : SysuiTestCase() { assertThat(underTest.authenticate("password".toList(), tryAutoConfirm = true)) .isEqualTo(AuthenticationResult.SKIPPED) - val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked) - assertThat(isUnlocked).isFalse() } @Test fun throttling() = testScope.runTest { - val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked) val throttling by collectLastValue(underTest.throttling) val isThrottled by collectLastValue(underTest.isThrottled) utils.authenticationRepository.setAuthenticationMethod( DataLayerAuthenticationMethodModel.Pin ) underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN) - assertThat(isUnlocked).isTrue() - assertThat(isThrottled).isFalse() - assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) - - utils.deviceEntryRepository.setUnlocked(false) - assertThat(isUnlocked).isFalse() assertThat(isThrottled).isFalse() assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) // Make many wrong attempts, but just shy of what's needed to get throttled: repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING - 1) { underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN - assertThat(isUnlocked).isFalse() assertThat(isThrottled).isFalse() assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) } // Make one more wrong attempt, leading to throttling: underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN - assertThat(isUnlocked).isFalse() assertThat(isThrottled).isTrue() assertThat(throttling) .isEqualTo( @@ -394,7 +375,6 @@ class AuthenticationInteractorTest : SysuiTestCase() { // Correct PIN, but throttled, so doesn't attempt it: assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) .isEqualTo(AuthenticationResult.SKIPPED) - assertThat(isUnlocked).isFalse() assertThat(isThrottled).isTrue() assertThat(throttling) .isEqualTo( @@ -427,7 +407,6 @@ class AuthenticationInteractorTest : SysuiTestCase() { // Move the clock forward one more second, to completely finish the throttling period: advanceTimeBy(1000) - assertThat(isUnlocked).isFalse() assertThat(isThrottled).isFalse() assertThat(throttling) .isEqualTo( @@ -441,7 +420,6 @@ class AuthenticationInteractorTest : SysuiTestCase() { // Correct PIN and no longer throttled so unlocks successfully: assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) .isEqualTo(AuthenticationResult.SUCCEEDED) - assertThat(isUnlocked).isTrue() assertThat(isThrottled).isFalse() assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt index 577539620ee3..8a7c362b4114 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt @@ -43,20 +43,15 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) -class BouncerInteractorTest : SysuiTestCase() { +class +BouncerInteractorTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope private val authenticationInteractor = utils.authenticationInteractor() private val sceneInteractor = utils.sceneInteractor() - private val deviceEntryInteractor = - utils.deviceEntryInteractor( - authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, - ) private val underTest = utils.bouncerInteractor( - deviceEntryInteractor = deviceEntryInteractor, authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, ) @@ -79,11 +74,6 @@ class BouncerInteractorTest : SysuiTestCase() { utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) runCurrent() - utils.deviceEntryRepository.setUnlocked(false) - underTest.showOrUnlockDevice() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN) - underTest.clearMessage() assertThat(message).isEmpty() @@ -94,7 +84,6 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(underTest.authenticate(listOf(9, 8, 7))) .isEqualTo(AuthenticationResult.FAILED) assertThat(message).isEqualTo(MESSAGE_WRONG_PIN) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.resetMessage() assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN) @@ -102,37 +91,25 @@ class BouncerInteractorTest : SysuiTestCase() { // Correct input. assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) .isEqualTo(AuthenticationResult.SUCCEEDED) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) } @Test fun pinAuthMethod_tryAutoConfirm_withAutoConfirmPin() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.desiredScene) - val message by collectLastValue(underTest.message) val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) runCurrent() utils.authenticationRepository.setAutoConfirmFeatureEnabled(true) - utils.deviceEntryRepository.setUnlocked(false) - underTest.showOrUnlockDevice() assertThat(isAutoConfirmEnabled).isTrue() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN) - underTest.clearMessage() // Incomplete input. assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true)) .isEqualTo(AuthenticationResult.SKIPPED) - assertThat(message).isEmpty() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) // Wrong 6-digit pin assertThat(underTest.authenticate(listOf(1, 2, 3, 5, 5, 6), tryAutoConfirm = true)) .isEqualTo(AuthenticationResult.FAILED) - assertThat(message).isEqualTo(MESSAGE_WRONG_PIN) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) // Correct input. assertThat( @@ -142,7 +119,6 @@ class BouncerInteractorTest : SysuiTestCase() { ) ) .isEqualTo(AuthenticationResult.SUCCEEDED) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) } @Test @@ -153,16 +129,11 @@ class BouncerInteractorTest : SysuiTestCase() { utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) runCurrent() - utils.deviceEntryRepository.setUnlocked(false) - underTest.showOrUnlockDevice() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - underTest.clearMessage() // Incomplete input. assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true)) .isEqualTo(AuthenticationResult.SKIPPED) assertThat(message).isEmpty() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) // Correct input. assertThat( @@ -173,25 +144,16 @@ class BouncerInteractorTest : SysuiTestCase() { ) .isEqualTo(AuthenticationResult.SKIPPED) assertThat(message).isEmpty() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) } @Test fun passwordAuthMethod() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.desiredScene) val message by collectLastValue(underTest.message) utils.authenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.Password ) runCurrent() - utils.deviceEntryRepository.setUnlocked(false) - underTest.showOrUnlockDevice() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD) - - underTest.clearMessage() - assertThat(message).isEmpty() underTest.resetMessage() assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD) @@ -200,7 +162,6 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(underTest.authenticate("alohamora".toList())) .isEqualTo(AuthenticationResult.FAILED) assertThat(message).isEqualTo(MESSAGE_WRONG_PASSWORD) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.resetMessage() assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD) @@ -208,7 +169,6 @@ class BouncerInteractorTest : SysuiTestCase() { // Correct input. assertThat(underTest.authenticate("password".toList())) .isEqualTo(AuthenticationResult.SUCCEEDED) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) } @Test @@ -220,14 +180,6 @@ class BouncerInteractorTest : SysuiTestCase() { AuthenticationMethodModel.Pattern ) runCurrent() - utils.deviceEntryRepository.setUnlocked(false) - underTest.showOrUnlockDevice() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN) - - underTest.clearMessage() - assertThat(message).isEmpty() - underTest.resetMessage() assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN) @@ -243,7 +195,6 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(wrongPattern.size).isAtLeast(utils.authenticationRepository.minPatternLength) assertThat(underTest.authenticate(wrongPattern)).isEqualTo(AuthenticationResult.FAILED) assertThat(message).isEqualTo(MESSAGE_WRONG_PATTERN) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.resetMessage() assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN) @@ -257,7 +208,6 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(underTest.authenticate(tooShortPattern)) .isEqualTo(AuthenticationResult.SKIPPED) assertThat(message).isEqualTo(MESSAGE_WRONG_PATTERN) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.resetMessage() assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN) @@ -265,51 +215,6 @@ class BouncerInteractorTest : SysuiTestCase() { // Correct input. assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN)) .isEqualTo(AuthenticationResult.SUCCEEDED) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) - } - - @Test - fun showOrUnlockDevice_notLocked_switchesToGoneScene() = - testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.desiredScene) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.deviceEntryRepository.setUnlocked(true) - runCurrent() - - underTest.showOrUnlockDevice() - - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) - } - - @Test - fun showOrUnlockDevice_authMethodNotSecure_switchesToGoneScene() = - testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.desiredScene) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) - utils.deviceEntryRepository.setInsecureLockscreenEnabled(true) - utils.deviceEntryRepository.setUnlocked(false) - - underTest.showOrUnlockDevice() - - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) - } - - @Test - fun showOrUnlockDevice_customMessageShown() = - testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.desiredScene) - val message by collectLastValue(underTest.message) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password - ) - runCurrent() - utils.deviceEntryRepository.setUnlocked(false) - - val customMessage = "Hello there!" - underTest.showOrUnlockDevice(customMessage) - - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - assertThat(message).isEqualTo(customMessage) } @Test @@ -320,13 +225,8 @@ class BouncerInteractorTest : SysuiTestCase() { val message by collectLastValue(underTest.message) val currentScene by collectLastValue(sceneInteractor.desiredScene) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - runCurrent() - underTest.showOrUnlockDevice() - runCurrent() - assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer) assertThat(isThrottled).isFalse() assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) - assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN) repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) { times -> // Wrong PIN. assertThat(underTest.authenticate(listOf(6, 7, 8, 9))) @@ -355,7 +255,6 @@ class BouncerInteractorTest : SysuiTestCase() { // Correct PIN, but throttled, so doesn't change away from the bouncer scene: assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) .isEqualTo(AuthenticationResult.SKIPPED) - assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer) assertTryAgainMessage( message, FakeAuthenticationRepository.THROTTLE_DURATION_MS.milliseconds.inWholeSeconds @@ -381,12 +280,10 @@ class BouncerInteractorTest : SysuiTestCase() { FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING, ) ) - assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer) // Correct PIN and no longer throttled so changes to the Gone scene: assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) .isEqualTo(AuthenticationResult.SUCCEEDED) - assertThat(currentScene?.key).isEqualTo(SceneKey.Gone) assertThat(isThrottled).isFalse() assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt index 8e1f5ac58b68..db64ba214537 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt @@ -39,18 +39,12 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() { private val testScope = utils.testScope private val authenticationInteractor = utils.authenticationInteractor() private val sceneInteractor = utils.sceneInteractor() - private val deviceEntryInteractor = - utils.deviceEntryInteractor( - authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, - ) private val underTest = PinBouncerViewModel( applicationContext = context, viewModelScope = testScope.backgroundScope, interactor = utils.bouncerInteractor( - deviceEntryInteractor = deviceEntryInteractor, authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, ), diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt index 6357a1a703b5..382e2844a8b2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt @@ -48,14 +48,8 @@ class BouncerViewModelTest : SysuiTestCase() { private val testScope = utils.testScope private val authenticationInteractor = utils.authenticationInteractor() private val actionButtonInteractor = utils.bouncerActionButtonInteractor() - private val deviceEntryInteractor = - utils.deviceEntryInteractor( - authenticationInteractor = authenticationInteractor, - sceneInteractor = utils.sceneInteractor(), - ) private val bouncerInteractor = utils.bouncerInteractor( - deviceEntryInteractor = deviceEntryInteractor, authenticationInteractor = authenticationInteractor, sceneInteractor = utils.sceneInteractor(), ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt index 390742031381..23eba82ab5bb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt @@ -46,14 +46,8 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { private val testScope = utils.testScope private val authenticationInteractor = utils.authenticationInteractor() private val sceneInteractor = utils.sceneInteractor() - private val deviceEntryInteractor = - utils.deviceEntryInteractor( - authenticationInteractor = authenticationInteractor, - sceneInteractor = utils.sceneInteractor(), - ) private val bouncerInteractor = utils.bouncerInteractor( - deviceEntryInteractor = deviceEntryInteractor, authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, ) @@ -110,19 +104,19 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { @Test fun onAuthenticateKeyPressed_whenCorrect() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.desiredScene) + val authResult by + collectLastValue(authenticationInteractor.authenticationChallengeResult) lockDeviceAndOpenPasswordBouncer() underTest.onPasswordInputChanged("password") underTest.onAuthenticateKeyPressed() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) + assertThat(authResult).isTrue() } @Test fun onAuthenticateKeyPressed_whenWrong() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.desiredScene) val message by collectLastValue(bouncerViewModel.message) val password by collectLastValue(underTest.password) lockDeviceAndOpenPasswordBouncer() @@ -132,13 +126,11 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { assertThat(password).isEqualTo("") assertThat(message?.text).isEqualTo(WRONG_PASSWORD) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) } @Test fun onAuthenticateKeyPressed_whenEmpty() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.desiredScene) val message by collectLastValue(bouncerViewModel.message) val password by collectLastValue(underTest.password) utils.authenticationRepository.setAuthenticationMethod( @@ -147,7 +139,6 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { utils.deviceEntryRepository.setUnlocked(false) sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() // Enter nothing. @@ -155,13 +146,13 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { assertThat(password).isEqualTo("") assertThat(message?.text).isEqualTo(ENTER_YOUR_PASSWORD) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) } @Test fun onAuthenticateKeyPressed_correctAfterWrong() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.desiredScene) + val authResult by + collectLastValue(authenticationInteractor.authenticationChallengeResult) val message by collectLastValue(bouncerViewModel.message) val password by collectLastValue(underTest.password) lockDeviceAndOpenPasswordBouncer() @@ -171,7 +162,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { underTest.onAuthenticateKeyPressed() assertThat(password).isEqualTo("") assertThat(message?.text).isEqualTo(WRONG_PASSWORD) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + assertThat(authResult).isFalse() // Enter the correct password: underTest.onPasswordInputChanged("password") @@ -179,7 +170,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { underTest.onAuthenticateKeyPressed() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) + assertThat(authResult).isTrue() } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt index 47db4f8faeca..b106d556b78b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt @@ -49,14 +49,8 @@ class PatternBouncerViewModelTest : SysuiTestCase() { private val testScope = utils.testScope private val authenticationInteractor = utils.authenticationInteractor() private val sceneInteractor = utils.sceneInteractor() - private val deviceEntryInteractor = - utils.deviceEntryInteractor( - authenticationInteractor = authenticationInteractor, - sceneInteractor = utils.sceneInteractor(), - ) private val bouncerInteractor = utils.bouncerInteractor( - deviceEntryInteractor = deviceEntryInteractor, authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, ) @@ -120,7 +114,8 @@ class PatternBouncerViewModelTest : SysuiTestCase() { @Test fun onDragEnd_whenCorrect() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.desiredScene) + val authResult by + collectLastValue(authenticationInteractor.authenticationChallengeResult) val selectedDots by collectLastValue(underTest.selectedDots) val currentDot by collectLastValue(underTest.currentDot) lockDeviceAndOpenPatternBouncer() @@ -150,7 +145,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { underTest.onDragEnd() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) + assertThat(authResult).isTrue() } @Test @@ -344,7 +339,8 @@ class PatternBouncerViewModelTest : SysuiTestCase() { @Test fun onDragEnd_correctAfterWrong() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.desiredScene) + val authResult by + collectLastValue(authenticationInteractor.authenticationChallengeResult) val message by collectLastValue(bouncerViewModel.message) val selectedDots by collectLastValue(underTest.selectedDots) val currentDot by collectLastValue(underTest.currentDot) @@ -356,14 +352,14 @@ class PatternBouncerViewModelTest : SysuiTestCase() { assertThat(selectedDots).isEmpty() assertThat(currentDot).isNull() assertThat(message?.text).isEqualTo(WRONG_PATTERN) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + assertThat(authResult).isFalse() // Enter the correct pattern: CORRECT_PATTERN.forEach(::dragToCoordinate) underTest.onDragEnd() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) + assertThat(authResult).isTrue() } private fun dragOverCoordinates(vararg coordinatesDragged: Point) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt index e07c0b87bc79..69f855d57a9d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt @@ -48,14 +48,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { private val testScope = utils.testScope private val sceneInteractor = utils.sceneInteractor() private val authenticationInteractor = utils.authenticationInteractor() - private val deviceEntryInteractor = - utils.deviceEntryInteractor( - authenticationInteractor = authenticationInteractor, - sceneInteractor = utils.sceneInteractor(), - ) private val bouncerInteractor = utils.bouncerInteractor( - deviceEntryInteractor = deviceEntryInteractor, authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, ) @@ -181,7 +175,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { @Test fun onAuthenticateButtonClicked_whenCorrect() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.desiredScene) + val authResult by + collectLastValue(authenticationInteractor.authenticationChallengeResult) lockDeviceAndOpenPinBouncer() FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit -> @@ -190,7 +185,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { underTest.onAuthenticateButtonClicked() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) + assertThat(authResult).isTrue() } @Test @@ -217,7 +212,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { @Test fun onAuthenticateButtonClicked_correctAfterWrong() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.desiredScene) + val authResult by + collectLastValue(authenticationInteractor.authenticationChallengeResult) val message by collectLastValue(bouncerViewModel.message) val pin by collectLastValue(underTest.pinInput.map { it.getPin() }) lockDeviceAndOpenPinBouncer() @@ -230,7 +226,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { underTest.onAuthenticateButtonClicked() assertThat(message?.text).ignoringCase().isEqualTo(WRONG_PIN) assertThat(pin).isEmpty() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + assertThat(authResult).isFalse() // Enter the correct PIN: FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit -> @@ -240,21 +236,22 @@ class PinBouncerViewModelTest : SysuiTestCase() { underTest.onAuthenticateButtonClicked() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) + assertThat(authResult).isTrue() } @Test fun onAutoConfirm_whenCorrect() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.desiredScene) utils.authenticationRepository.setAutoConfirmFeatureEnabled(true) + val authResult by + collectLastValue(authenticationInteractor.authenticationChallengeResult) lockDeviceAndOpenPinBouncer() FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit -> underTest.onPinButtonClicked(digit) } - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) + assertThat(authResult).isTrue() } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt index 2c80035873f0..0eb8982342d6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt @@ -96,6 +96,17 @@ class DeviceEntryRepositoryTest : SysuiTestCase() { } @Test + fun reportSuccessfulAuthentication_shouldUpdateIsUnlocked() = + testScope.runTest { + val isUnlocked by collectLastValue(underTest.isUnlocked) + assertThat(isUnlocked).isFalse() + + underTest.reportSuccessfulAuthentication() + + assertThat(isUnlocked).isTrue() + } + + @Test fun isBypassEnabled_disabledInController() = testScope.runTest { whenever(keyguardBypassController.isBypassEnabled).thenAnswer { false } diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt index aebadc5b5730..81251d630a44 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt @@ -278,12 +278,68 @@ class DeviceEntryInteractorTest : SysuiTestCase() { } @Test + fun showOrUnlockDevice_notLocked_switchesToGoneScene() = + testScope.runTest { + val currentScene by collectLastValue(sceneInteractor.desiredScene) + switchToScene(SceneKey.Lockscreen) + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen)) + + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.deviceEntryRepository.setUnlocked(true) + runCurrent() + + underTest.attemptDeviceEntry() + + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) + } + + @Test + fun showOrUnlockDevice_authMethodNotSecure_switchesToGoneScene() = + testScope.runTest { + val currentScene by collectLastValue(sceneInteractor.desiredScene) + switchToScene(SceneKey.Lockscreen) + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen)) + + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + + underTest.attemptDeviceEntry() + + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) + } + + @Test + fun showOrUnlockDevice_authMethodSwipe_switchesToGoneScene() = + testScope.runTest { + val currentScene by collectLastValue(sceneInteractor.desiredScene) + switchToScene(SceneKey.Lockscreen) + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen)) + + utils.deviceEntryRepository.setInsecureLockscreenEnabled(true) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + + underTest.attemptDeviceEntry() + + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) + } + + @Test fun isBypassEnabled_disabledInRepository_false() = testScope.runTest { utils.deviceEntryRepository.setBypassEnabled(false) assertThat(underTest.isBypassEnabled.value).isFalse() } + @Test + fun successfulAuthenticationChallengeAttempt_updatedIsUnlockedState() = + testScope.runTest { + val isUnlocked by collectLastValue(underTest.isUnlocked) + assertThat(isUnlocked).isFalse() + + utils.authenticationRepository.reportAuthenticationAttempt(true) + + assertThat(isUnlocked).isTrue() + } + private fun switchToScene(sceneKey: SceneKey) { sceneInteractor.changeScene(SceneModel(sceneKey), "reason") } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt index a9f82392c7e5..2f93c736d3f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt @@ -90,13 +90,8 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { underTest = QuickSettingsSceneViewModel( - bouncerInteractor = - utils.bouncerInteractor( - deviceEntryInteractor = - utils.deviceEntryInteractor( - authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, - ), + deviceEntryInteractor = + utils.deviceEntryInteractor( authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, ), diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index e84d274b4763..790f5fba3bd4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -135,7 +135,6 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { private val bouncerInteractor = utils.bouncerInteractor( - deviceEntryInteractor = deviceEntryInteractor, authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, ) @@ -234,7 +233,6 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { ShadeSceneViewModel( applicationScope = testScope.backgroundScope, deviceEntryInteractor = deviceEntryInteractor, - bouncerInteractor = bouncerInteractor, shadeHeaderViewModel = shadeHeaderViewModel, ) @@ -648,6 +646,9 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { emulateUserDrivenTransition(SceneKey.Bouncer) enterPin() + // This repository state is not changed by the AuthInteractor, it relies on + // KeyguardStateController. + utils.deviceEntryRepository.setUnlocked(true) emulateUiSceneTransition( expectedVisible = false, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt index 589f9aeba19f..2de927a0e8c0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt @@ -96,12 +96,6 @@ class ShadeSceneViewModelTest : SysuiTestCase() { ShadeSceneViewModel( applicationScope = testScope.backgroundScope, deviceEntryInteractor = deviceEntryInteractor, - bouncerInteractor = - utils.bouncerInteractor( - deviceEntryInteractor = deviceEntryInteractor, - authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, - ), shadeHeaderViewModel = shadeHeaderViewModel, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt index 81c5d9c248f3..1ec193979695 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt @@ -25,11 +25,11 @@ import com.android.systemui.authentication.shared.model.AuthenticationPatternCoo import com.android.systemui.authentication.shared.model.AuthenticationResultModel import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository import dagger.Binds import dagger.Module import dagger.Provides import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -37,13 +37,13 @@ import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.currentTime class FakeAuthenticationRepository( - private val deviceEntryRepository: FakeDeviceEntryRepository, private val currentTime: () -> Long, ) : AuthenticationRepository { private val _isAutoConfirmFeatureEnabled = MutableStateFlow(false) override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> = _isAutoConfirmFeatureEnabled.asStateFlow() + override val authenticationChallengeResult = MutableSharedFlow<Boolean>() override val hintedPinLength: Int = HINTING_PIN_LENGTH @@ -80,7 +80,7 @@ class FakeAuthenticationRepository( override suspend fun reportAuthenticationAttempt(isSuccessful: Boolean) { failedAttemptCount = if (isSuccessful) 0 else failedAttemptCount + 1 - deviceEntryRepository.setUnlocked(isSuccessful) + authenticationChallengeResult.emit(isSuccessful) } override suspend fun getPinLength(): Int { @@ -216,9 +216,8 @@ object FakeAuthenticationRepositoryModule { @Provides @SysUISingleton fun provideFake( - deviceEntryRepository: FakeDeviceEntryRepository, scope: TestScope, - ) = FakeAuthenticationRepository(deviceEntryRepository, currentTime = { scope.currentTime }) + ) = FakeAuthenticationRepository(currentTime = { scope.currentTime }) @Module interface Bindings { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt index f0293489cb87..5ae210b18a3e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt @@ -39,6 +39,10 @@ class FakeDeviceEntryRepository @Inject constructor() : DeviceEntryRepository { return isInsecureLockscreenEnabled } + override fun reportSuccessfulAuthentication() { + _isUnlocked.value = true + } + fun setUnlocked(isUnlocked: Boolean) { _isUnlocked.value = isUnlocked } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt index 36ec18fc4de4..6afca15c5fec 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt @@ -110,7 +110,6 @@ class SceneTestUtils( val deviceEntryRepository: FakeDeviceEntryRepository by lazy { FakeDeviceEntryRepository() } val authenticationRepository: FakeAuthenticationRepository by lazy { FakeAuthenticationRepository( - deviceEntryRepository = deviceEntryRepository, currentTime = { testScope.currentTime }, ) } @@ -181,6 +180,7 @@ class SceneTestUtils( sceneInteractor = sceneInteractor, deviceEntryFaceAuthRepository = faceAuthRepository, trustRepository = trustRepository, + flags = FakeSceneContainerFlags(enabled = true) ) } @@ -221,7 +221,6 @@ class SceneTestUtils( } fun bouncerInteractor( - deviceEntryInteractor: DeviceEntryInteractor, authenticationInteractor: AuthenticationInteractor, sceneInteractor: SceneInteractor, ): BouncerInteractor { @@ -229,7 +228,6 @@ class SceneTestUtils( applicationScope = applicationScope(), applicationContext = context, repository = BouncerRepository(featureFlags), - deviceEntryInteractor = deviceEntryInteractor, authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, flags = sceneContainerFlags, |