diff options
author | 2023-09-27 12:19:33 +0000 | |
---|---|---|
committer | 2023-09-29 11:05:37 +0000 | |
commit | af001916d68de16cd7ea5f2013240f40197020bc (patch) | |
tree | b7c1408127a8a577480b3fb0ebc8e76c9d85457d | |
parent | 48d97faecfa9064d1d24a528d27b9f998bedd885 (diff) |
[flexiglass] Add a DeviceEntry module.
This module hosts business logic and state related to device entry, i.e.
when the user successfully dismisses (or bypasses) the lockscreen. This
is distinct from the authentication module, which is specifically
concerned with determining a user's identity.
The names of certain state variables have changed, too:
* `isLockscreenDismissed` is now `isDeviceEntered`
* `canSwipeToDismiss` is now `canSwipeToEnter`
* `isLockscreenEnabled` is now `isInsecureLockscreenEnabled`
Bug: 301253588
Test: Existing unit tests still pass.
Test: Added new unit tests.
Test: Manually tested the 3 different bouncer auth methods, lockscreen
bypass and swipe, and verified they still work as expected.
Change-Id: I9596513b4319eb0a9b9a25bee3a1c1726396e4ca
38 files changed, 941 insertions, 662 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index b2287d876a48..51dafac7b421 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -18,6 +18,7 @@ package com.android.keyguard; import static android.app.StatusBarManager.SESSION_KEYGUARD; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; + import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_BIOMETRIC; import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_EXTENDED_ACCESS; import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_NONE_SECURITY; @@ -68,8 +69,6 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.keyguard.dagger.KeyguardBouncerScope; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Gefingerpoken; -import com.android.systemui.res.R; -import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor; import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate; import com.android.systemui.biometrics.SideFpsController; import com.android.systemui.biometrics.SideFpsUiRequestSource; @@ -77,6 +76,7 @@ import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.classifier.FalsingA11yDelegate; import com.android.systemui.classifier.FalsingCollector; +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; @@ -84,6 +84,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.res.R; import com.android.systemui.scene.shared.flag.SceneContainerFlags; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -420,7 +421,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } }; private final UserInteractor mUserInteractor; - private final Provider<AuthenticationInteractor> mAuthenticationInteractor; + private final Provider<DeviceEntryInteractor> mDeviceEntryInteractor; private final Provider<JavaAdapter> mJavaAdapter; private final DeviceProvisionedController mDeviceProvisionedController; private final Lazy<PrimaryBouncerInteractor> mPrimaryBouncerInteractor; @@ -457,7 +458,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard FaceAuthAccessibilityDelegate faceAuthAccessibilityDelegate, KeyguardTransitionInteractor keyguardTransitionInteractor, Lazy<PrimaryBouncerInteractor> primaryBouncerInteractor, - Provider<AuthenticationInteractor> authenticationInteractor + Provider<DeviceEntryInteractor> deviceEntryInteractor ) { super(view); view.setAccessibilityDelegate(faceAuthAccessibilityDelegate); @@ -487,7 +488,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor; mBouncerMessageInteractor = bouncerMessageInteractor; mUserInteractor = userInteractor; - mAuthenticationInteractor = authenticationInteractor; + mDeviceEntryInteractor = deviceEntryInteractor; mJavaAdapter = javaAdapter; mKeyguardTransitionInteractor = keyguardTransitionInteractor; mDeviceProvisionedController = deviceProvisionedController; @@ -519,9 +520,9 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard // When the scene framework says that the lockscreen has been dismissed, dismiss the // keyguard here, revealing the underlying app or launcher: mSceneTransitionCollectionJob = mJavaAdapter.get().alwaysCollectFlow( - mAuthenticationInteractor.get().isLockscreenDismissed(), - isLockscreenDismissed -> { - if (isLockscreenDismissed) { + mDeviceEntryInteractor.get().isDeviceEntered(), + isDeviceEntered -> { + if (isDeviceEntered) { final int selectedUserId = mUserInteractor.getSelectedUserId(); showNextSecurityScreenOrFinish( /* authenticated= */ true, @@ -1081,15 +1082,11 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard * one side). */ private boolean canUseOneHandedBouncer() { - switch(mCurrentSecurityMode) { - case PIN: - case Pattern: - case SimPin: - case SimPuk: - return getResources().getBoolean(R.bool.can_use_one_handed_bouncer); - default: - return false; - } + return switch (mCurrentSecurityMode) { + case PIN, Pattern, SimPin, SimPuk -> getResources().getBoolean( + R.bool.can_use_one_handed_bouncer); + default -> false; + }; } private boolean canDisplayUserSwitcher() { 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 b2433d4832f4..80be008dbd3a 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 @@ -29,9 +29,9 @@ import com.android.systemui.authentication.data.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationResultModel import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.dagger.SysUISingleton 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 com.android.systemui.util.kotlin.pairwise import com.android.systemui.util.time.SystemClock @@ -59,18 +59,6 @@ import kotlinx.coroutines.withContext /** Defines interface for classes that can access authentication-related application state. */ interface AuthenticationRepository { - - /** - * Whether the device is unlocked. - * - * A device that is not yet unlocked requires unlocking by completing an authentication - * challenge according to the current authentication method, unless in cases when the current - * authentication method is not "secure" (for example, None); in such cases, the value of this - * flow will always be `true`, even if the lockscreen is showing and still needs to be dismissed - * by the user to proceed. - */ - val isUnlocked: StateFlow<Boolean> - /** * Whether the auto confirm feature is enabled for the currently-selected user. * @@ -129,14 +117,6 @@ interface AuthenticationRepository { /** Returns the length of the PIN or `0` if the current auth method is not PIN. */ suspend fun getPinLength(): Int - /** - * Returns whether the lockscreen is enabled. - * - * When the lockscreen is not enabled, it shouldn't show in cases when the authentication method - * is considered not secure (for example, "swipe" is considered to be "none"). - */ - suspend fun isLockscreenEnabled(): Boolean - /** Reports an authentication attempt. */ suspend fun reportAuthenticationAttempt(isSuccessful: Boolean) @@ -167,6 +147,7 @@ interface AuthenticationRepository { suspend fun checkCredential(credential: LockscreenCredential): AuthenticationResultModel } +@SysUISingleton class AuthenticationRepositoryImpl @Inject constructor( @@ -174,20 +155,10 @@ constructor( private val getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>, @Background private val backgroundDispatcher: CoroutineDispatcher, private val userRepository: UserRepository, - keyguardRepository: KeyguardRepository, private val lockPatternUtils: LockPatternUtils, broadcastDispatcher: BroadcastDispatcher, ) : AuthenticationRepository { - override val isUnlocked = keyguardRepository.isKeyguardUnlocked - - override suspend fun isLockscreenEnabled(): Boolean { - return withContext(backgroundDispatcher) { - val selectedUserId = userRepository.selectedUserId - !lockPatternUtils.isLockScreenDisabled(selectedUserId) - } - } - override val isAutoConfirmEnabled: StateFlow<Boolean> = refreshingFlow( initialValue = false, 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 4cfc6aaa2b50..453a7a6d3536 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 @@ -26,9 +26,7 @@ import com.android.systemui.authentication.shared.model.AuthenticationThrottling import com.android.systemui.dagger.SysUISingleton 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.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository import com.android.systemui.user.data.repository.UserRepository import com.android.systemui.util.time.SystemClock import javax.inject.Inject @@ -42,15 +40,19 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -/** Hosts application business logic related to authentication. */ +/** + * Hosts application business logic related to user authentication. + * + * Note: there is a distinction between authentication (determining a user's identity) and device + * entry (dismissing the lockscreen). For logic that is specific to device entry, please use + * `DeviceEntryInteractor` instead. + */ @SysUISingleton class AuthenticationInteractor @Inject @@ -59,8 +61,7 @@ constructor( private val repository: AuthenticationRepository, @Background private val backgroundDispatcher: CoroutineDispatcher, private val userRepository: UserRepository, - private val keyguardRepository: KeyguardRepository, - sceneInteractor: SceneInteractor, + private val deviceEntryRepository: DeviceEntryRepository, private val clock: SystemClock, ) { /** @@ -77,76 +78,13 @@ constructor( * Note: this layer adds the synthetic authentication method of "swipe" which is special. When * the current authentication method is "swipe", the user does not need to complete any * authentication challenge to unlock the device; they just need to dismiss the lockscreen to - * get past it. This also means that the value of [isUnlocked] remains `false` even when the - * lockscreen is showing and still needs to be dismissed by the user to proceed. + * get past it. This also means that the value of `DeviceEntryInteractor#isUnlocked` remains + * `true` even when the lockscreen is showing and still needs to be dismissed by the user to + * proceed. */ val authenticationMethod: Flow<DomainLayerAuthenticationMethodModel> = repository.authenticationMethod.map { rawModel -> rawModel.toDomainLayer() } - /** - * Whether the device is unlocked. - * - * A device that is not yet unlocked requires unlocking by completing an authentication - * challenge according to the current authentication method, unless in cases when the current - * authentication method is not "secure" (for example, None and Swipe); in such cases, the value - * of this flow will always be `true`, even if the lockscreen is showing and still needs to be - * dismissed by the user to proceed. - */ - val isUnlocked: StateFlow<Boolean> = - combine( - repository.isUnlocked, - authenticationMethod, - ) { isUnlocked, authenticationMethod -> - !authenticationMethod.isSecure || isUnlocked - } - .stateIn( - scope = applicationScope, - started = SharingStarted.Eagerly, - initialValue = false, - ) - - /** - * Whether the lockscreen has been dismissed (by any method). This can be false even when the - * device is unlocked, e.g. when swipe to unlock is enabled. - * - * Note: - * - `false` doesn't mean the lockscreen is visible (it may be occluded or covered by other UI). - * - `true` doesn't mean the lockscreen is invisible (since this state changes before the - * transition occurs). - */ - val isLockscreenDismissed: StateFlow<Boolean> = - sceneInteractor.desiredScene - .map { it.key } - .filter { currentScene -> - currentScene == SceneKey.Gone || currentScene == SceneKey.Lockscreen - } - .map { it == SceneKey.Gone } - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = false, - ) - - /** - * Whether it's currently possible to swipe up to dismiss the lockscreen without requiring - * authentication. This returns false whenever the lockscreen has been dismissed. - * - * Note: `true` doesn't mean the lockscreen is visible. It may be occluded or covered by other - * UI. - */ - val canSwipeToDismiss = - combine(authenticationMethod, isLockscreenDismissed) { - authenticationMethod, - isLockscreenDismissed -> - authenticationMethod is DomainLayerAuthenticationMethodModel.Swipe && - !isLockscreenDismissed - } - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = false, - ) - /** The current authentication throttling state, only meaningful if [isThrottled] is `true`. */ val throttling: StateFlow<AuthenticationThrottlingModel> = repository.throttling @@ -211,32 +149,15 @@ constructor( * Note: this layer adds the synthetic authentication method of "swipe" which is special. When * the current authentication method is "swipe", the user does not need to complete any * authentication challenge to unlock the device; they just need to dismiss the lockscreen to - * get past it. This also means that the value of [isUnlocked] remains `false` even when the - * lockscreen is showing and still needs to be dismissed by the user to proceed. + * get past it. This also means that the value of `DeviceEntryInteractor#isUnlocked` remains + * `true` even when the lockscreen is showing and still needs to be dismissed by the user to + * proceed. */ suspend fun getAuthenticationMethod(): DomainLayerAuthenticationMethodModel { return repository.getAuthenticationMethod().toDomainLayer() } /** - * Returns `true` if the device currently requires authentication before content can be viewed; - * `false` if content can be displayed without unlocking first. - */ - suspend fun isAuthenticationRequired(): Boolean { - return !isUnlocked.value && getAuthenticationMethod().isSecure - } - - /** - * Whether lock screen bypass is enabled. When enabled, the lock screen will be automatically - * dismisses once the authentication challenge is completed. For example, completing a biometric - * authentication challenge via face unlock or fingerprint sensor can automatically bypass the - * lock screen. - */ - fun isBypassEnabled(): Boolean { - return keyguardRepository.isBypassEnabled() - } - - /** * Attempts to authenticate the user and unlock the device. * * If [tryAutoConfirm] is `true`, authentication is attempted if and only if the auth method @@ -312,7 +233,7 @@ constructor( /** Starts refreshing the throttling state every second. */ private suspend fun startThrottlingCountdown() { - cancelCountdown() + cancelThrottlingCountdown() throttlingCountdownJob = applicationScope.launch { while (refreshThrottling() > 0) { @@ -322,14 +243,14 @@ constructor( } /** Cancels any throttling state countdown started in [startThrottlingCountdown]. */ - private fun cancelCountdown() { + private fun cancelThrottlingCountdown() { throttlingCountdownJob?.cancel() throttlingCountdownJob = null } /** Notifies that the currently-selected user has changed. */ private suspend fun onSelectedUserChanged() { - cancelCountdown() + cancelThrottlingCountdown() if (refreshThrottling() > 0) { startThrottlingCountdown() } @@ -378,7 +299,7 @@ constructor( DomainLayerAuthenticationMethodModel { return when (this) { is DataLayerAuthenticationMethodModel.None -> - if (repository.isLockscreenEnabled()) { + if (deviceEntryRepository.isInsecureLockscreenEnabled()) { DomainLayerAuthenticationMethodModel.Swipe } else { DomainLayerAuthenticationMethodModel.None @@ -394,13 +315,10 @@ constructor( /** Result of a user authentication attempt. */ enum class AuthenticationResult { - /** Authentication succeeded and the device is now unlocked. */ + /** Authentication succeeded. */ SUCCEEDED, - /** Authentication failed and the device remains unlocked. */ + /** Authentication failed. */ FAILED, - /** - * Authentication was not performed, e.g. due to insufficient input, and the device remains - * unlocked. - */ + /** Authentication was not performed, e.g. due to insufficient input. */ SKIPPED, } 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 f3a463ba44a4..0c0236999e39 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,6 +26,7 @@ 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 @@ -50,6 +51,7 @@ 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, @@ -144,7 +146,7 @@ constructor( message: String? = null, ) { applicationScope.launch { - if (authenticationInteractor.isAuthenticationRequired()) { + if (deviceEntryInteractor.isAuthenticationRequired()) { repository.setMessage( message ?: promptMessage(authenticationInteractor.getAuthenticationMethod()) ) diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 283a07b206b9..4b6ad6d9be03 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -48,6 +48,7 @@ import com.android.systemui.controls.dagger.ControlsModule; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.SystemUser; import com.android.systemui.demomode.dagger.DemoModeModule; +import com.android.systemui.deviceentry.DeviceEntryModule; import com.android.systemui.display.DisplayModule; import com.android.systemui.doze.dagger.DozeComponent; import com.android.systemui.dreams.dagger.DreamModule; @@ -173,6 +174,7 @@ import javax.inject.Named; ControlsModule.class, CoroutinesModule.class, DemoModeModule.class, + DeviceEntryModule.class, DisableFlagsModule.class, DisplayModule.class, DreamModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt new file mode 100644 index 000000000000..e7f835f7b858 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt @@ -0,0 +1,12 @@ +package com.android.systemui.deviceentry + +import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule +import dagger.Module + +@Module( + includes = + [ + DeviceEntryRepositoryModule::class, + ], +) +object DeviceEntryModule 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 new file mode 100644 index 000000000000..5b85ad01301b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt @@ -0,0 +1,125 @@ +package com.android.systemui.deviceentry.data.repository + +import com.android.internal.widget.LockPatternUtils +import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging +import com.android.systemui.common.coroutine.ConflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.user.data.repository.UserRepository +import dagger.Binds +import dagger.Module +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.withContext + +/** Interface for classes that can access device-entry-related application state. */ +interface DeviceEntryRepository { + /** + * Whether the device is unlocked. + * + * A device that is not yet unlocked requires unlocking by completing an authentication + * challenge according to the current authentication method, unless in cases when the current + * authentication method is not "secure" (for example, None); in such cases, the value of this + * flow will always be `true`, even if the lockscreen is showing and still needs to be dismissed + * by the user to proceed. + */ + val isUnlocked: StateFlow<Boolean> + + /** + * Whether the lockscreen should be shown when the authentication method is not secure (e.g. + * `None` or `Swipe`). + */ + suspend fun isInsecureLockscreenEnabled(): Boolean + + /** + * Whether lockscreen bypass is enabled. When enabled, the lockscreen will be automatically + * dismissed once the authentication challenge is completed. + * + * This is a setting that is specific to the face unlock authentication method, because the user + * intent to unlock is not known. On devices that don't support face unlock, this always returns + * `true`. + * + * When this is `false`, an automatically-triggered face unlock shouldn't automatically dismiss + * the lockscreen. + */ + fun isBypassEnabled(): Boolean +} + +/** Encapsulates application state for device entry. */ +@SysUISingleton +class DeviceEntryRepositoryImpl +@Inject +constructor( + @Application private val applicationScope: CoroutineScope, + @Background private val backgroundDispatcher: CoroutineDispatcher, + private val userRepository: UserRepository, + private val lockPatternUtils: LockPatternUtils, + private val keyguardBypassController: KeyguardBypassController, + keyguardStateController: KeyguardStateController, +) : DeviceEntryRepository { + + override val isUnlocked = + ConflatedCallbackFlow.conflatedCallbackFlow { + val callback = + object : KeyguardStateController.Callback { + override fun onUnlockedChanged() { + trySendWithFailureLogging( + keyguardStateController.isUnlocked, + TAG, + "updated isUnlocked due to onUnlockedChanged" + ) + } + + override fun onKeyguardShowingChanged() { + trySendWithFailureLogging( + keyguardStateController.isUnlocked, + TAG, + "updated isUnlocked due to onKeyguardShowingChanged" + ) + } + } + + keyguardStateController.addCallback(callback) + // Adding the callback does not send an initial update. + trySendWithFailureLogging( + keyguardStateController.isUnlocked, + TAG, + "initial isKeyguardUnlocked" + ) + + awaitClose { keyguardStateController.removeCallback(callback) } + } + .distinctUntilChanged() + .stateIn( + applicationScope, + SharingStarted.Eagerly, + initialValue = false, + ) + + override suspend fun isInsecureLockscreenEnabled(): Boolean { + return withContext(backgroundDispatcher) { + val selectedUserId = userRepository.getSelectedUserInfo().id + !lockPatternUtils.isLockScreenDisabled(selectedUserId) + } + } + + override fun isBypassEnabled() = keyguardBypassController.bypassEnabled + + companion object { + private const val TAG = "DeviceEntryRepositoryImpl" + } +} + +@Module +interface DeviceEntryRepositoryModule { + @Binds fun repository(impl: DeviceEntryRepositoryImpl): DeviceEntryRepository +} 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 new file mode 100644 index 000000000000..5612c9a488ff --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt @@ -0,0 +1,110 @@ +package com.android.systemui.deviceentry.domain.interactor + +import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor +import com.android.systemui.authentication.domain.model.AuthenticationMethodModel +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository +import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.model.SceneKey +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +/** + * Hosts application business logic related to device entry. + * + * Device entry occurs when the user successfully dismisses (or bypasses) the lockscreen, regardless + * of the authentication method used. + */ +@SysUISingleton +class DeviceEntryInteractor +@Inject +constructor( + @Application private val applicationScope: CoroutineScope, + private val repository: DeviceEntryRepository, + private val authenticationInteractor: AuthenticationInteractor, + sceneInteractor: SceneInteractor, +) { + /** + * Whether the device is unlocked. + * + * A device that is not yet unlocked requires unlocking by completing an authentication + * challenge according to the current authentication method, unless in cases when the current + * authentication method is not "secure" (for example, None and Swipe); in such cases, the value + * of this flow will always be `true`, even if the lockscreen is showing and still needs to be + * dismissed by the user to proceed. + */ + val isUnlocked: StateFlow<Boolean> = + combine( + repository.isUnlocked, + authenticationInteractor.authenticationMethod, + ) { isUnlocked, authenticationMethod -> + !authenticationMethod.isSecure || isUnlocked + } + .stateIn( + scope = applicationScope, + started = SharingStarted.Eagerly, + initialValue = false, + ) + + /** + * Whether the device has been entered (i.e. the lockscreen has been dismissed, by any method). + * This can be `false` when the device is unlocked, e.g. when the user still needs to swipe away + * the non-secure lockscreen, even though they've already authenticated. + * + * Note: This does not imply that the lockscreen is visible or not. + */ + val isDeviceEntered: StateFlow<Boolean> = + sceneInteractor.desiredScene + .map { it.key } + .filter { currentScene -> + currentScene == SceneKey.Gone || currentScene == SceneKey.Lockscreen + } + .map { it == SceneKey.Gone } + .stateIn( + scope = applicationScope, + started = SharingStarted.WhileSubscribed(), + initialValue = false, + ) + + /** + * Whether it's currently possible to swipe up to enter the device without requiring + * authentication. This returns `false` whenever the lockscreen has been dismissed. + * + * Note: `true` doesn't mean the lockscreen is visible. It may be occluded or covered by other + * UI. + */ + val canSwipeToEnter = + combine(authenticationInteractor.authenticationMethod, isDeviceEntered) { + authenticationMethod, + isDeviceEntered -> + authenticationMethod is AuthenticationMethodModel.Swipe && !isDeviceEntered + } + .stateIn( + scope = applicationScope, + started = SharingStarted.WhileSubscribed(), + initialValue = false, + ) + + /** + * Returns `true` if the device currently requires authentication before entry is granted; + * `false` if the device can be entered without authenticating first. + */ + suspend fun isAuthenticationRequired(): Boolean { + return !isUnlocked.value && authenticationInteractor.getAuthenticationMethod().isSecure + } + + /** + * Whether lock screen bypass is enabled. When enabled, the lock screen will be automatically + * dismissed once the authentication challenge is completed. For example, completing a biometric + * authentication challenge via face unlock or fingerprint sensor can automatically bypass the + * lock screen. + */ + fun isBypassEnabled() = repository.isBypassEnabled() +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt index 2557e8112e1e..36b93cdf6217 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt @@ -47,7 +47,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.phone.BiometricUnlockController.WakeAndUnlockMode import com.android.systemui.statusbar.phone.DozeParameters -import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.time.SystemClock import javax.inject.Inject @@ -97,9 +96,6 @@ interface KeyguardRepository { */ val isKeyguardShowing: Flow<Boolean> - /** Is the keyguard in a unlocked state? */ - val isKeyguardUnlocked: StateFlow<Boolean> - /** Is an activity showing over the keyguard? */ val isKeyguardOccluded: Flow<Boolean> @@ -206,14 +202,6 @@ interface KeyguardRepository { */ fun isKeyguardShowing(): Boolean - /** - * Whether lock screen bypass is enabled. When enabled, the lock screen will be automatically - * dismissed once the authentication challenge is completed. For example, completing a biometric - * authentication challenge via face unlock or fingerprint sensor can automatically bypass the - * lock screen. - */ - fun isBypassEnabled(): Boolean - /** Sets whether the bottom area UI should animate the transition out of doze state. */ fun setAnimateDozingTransitions(animate: Boolean) @@ -265,7 +253,6 @@ constructor( screenLifecycle: ScreenLifecycle, biometricUnlockController: BiometricUnlockController, private val keyguardStateController: KeyguardStateController, - private val keyguardBypassController: KeyguardBypassController, private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val dozeTransitionListener: DozeTransitionListener, private val dozeParameters: DozeParameters, @@ -370,44 +357,6 @@ constructor( } .distinctUntilChanged() - override val isKeyguardUnlocked: StateFlow<Boolean> = - conflatedCallbackFlow { - val callback = - object : KeyguardStateController.Callback { - override fun onUnlockedChanged() { - trySendWithFailureLogging( - keyguardStateController.isUnlocked, - TAG, - "updated isKeyguardUnlocked due to onUnlockedChanged" - ) - } - - override fun onKeyguardShowingChanged() { - trySendWithFailureLogging( - keyguardStateController.isUnlocked, - TAG, - "updated isKeyguardUnlocked due to onKeyguardShowingChanged" - ) - } - } - - keyguardStateController.addCallback(callback) - // Adding the callback does not send an initial update. - trySendWithFailureLogging( - keyguardStateController.isUnlocked, - TAG, - "initial isKeyguardUnlocked" - ) - - awaitClose { keyguardStateController.removeCallback(callback) } - } - .distinctUntilChanged() - .stateIn( - scope, - SharingStarted.Eagerly, - initialValue = false, - ) - override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow { val callback = object : KeyguardStateController.Callback { @@ -543,10 +492,6 @@ constructor( return keyguardStateController.isShowing } - override fun isBypassEnabled(): Boolean { - return keyguardBypassController.bypassEnabled - } - // TODO(b/297345631): Expose this at the interactor level instead so that it can be powered by // [SceneInteractor] when scenes are ready. override val statusBarState: StateFlow<StatusBarState> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index 338f9945f5db..80634682c8cf 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -23,7 +23,6 @@ import android.app.StatusBarManager import android.graphics.Point import android.util.MathUtils import com.android.app.animation.Interpolators -import com.android.systemui.res.R import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow @@ -31,6 +30,7 @@ import com.android.systemui.common.shared.model.Position import com.android.systemui.common.shared.model.SharedNotificationContainerPosition import com.android.systemui.common.ui.data.repository.ConfigurationRepository import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.KeyguardRepository @@ -40,10 +40,10 @@ import com.android.systemui.keyguard.shared.model.DozeStateModel import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff import com.android.systemui.keyguard.shared.model.DozeTransitionModel import com.android.systemui.keyguard.shared.model.KeyguardRootViewVisibilityState -import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.ScreenModel import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.WakefulnessModel +import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.scene.shared.model.SceneKey @@ -79,6 +79,7 @@ constructor( private val commandQueue: CommandQueue, featureFlags: FeatureFlags, sceneContainerFlags: SceneContainerFlags, + deviceEntryRepository: DeviceEntryRepository, bouncerRepository: KeyguardBouncerRepository, configurationRepository: ConfigurationRepository, shadeRepository: ShadeRepository, @@ -168,7 +169,7 @@ constructor( val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing /** Whether the keyguard is unlocked or not. */ - val isKeyguardUnlocked: Flow<Boolean> = repository.isKeyguardUnlocked + val isKeyguardUnlocked: Flow<Boolean> = deviceEntryRepository.isUnlocked /** Whether the keyguard is occluded (covered by an activity). */ val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded @@ -323,26 +324,7 @@ constructor( repository.setAnimateDozingTransitions(animate) } - fun isKeyguardDismissable(): Boolean { - return repository.isKeyguardUnlocked.value - } - companion object { private const val TAG = "KeyguardInteractor" - - fun isKeyguardVisibleInState(state: KeyguardState): Boolean { - return when (state) { - KeyguardState.OFF -> true - KeyguardState.DOZING -> true - KeyguardState.DREAMING -> true - KeyguardState.AOD -> true - KeyguardState.ALTERNATE_BOUNCER -> true - KeyguardState.PRIMARY_BOUNCER -> true - KeyguardState.LOCKSCREEN -> true - KeyguardState.GONE -> false - KeyguardState.OCCLUDED -> true - KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> false - } - } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt index 91b33572345d..c03e4d950cae 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt @@ -16,10 +16,10 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.communal.domain.interactor.CommunalInteractor 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.scene.shared.model.SceneKey import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -34,26 +34,22 @@ class LockscreenSceneViewModel @Inject constructor( @Application applicationScope: CoroutineScope, - authenticationInteractor: AuthenticationInteractor, + deviceEntryInteractor: DeviceEntryInteractor, communalInteractor: CommunalInteractor, val longPress: KeyguardLongPressViewModel, ) { /** The key of the scene we should switch to when swiping up. */ val upDestinationSceneKey: StateFlow<SceneKey> = - authenticationInteractor.isUnlocked + deviceEntryInteractor.isUnlocked .map { isUnlocked -> upDestinationSceneKey(isUnlocked) } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), - initialValue = upDestinationSceneKey(authenticationInteractor.isUnlocked.value), + initialValue = upDestinationSceneKey(deviceEntryInteractor.isUnlocked.value), ) private fun upDestinationSceneKey(isUnlocked: Boolean): SceneKey { - return if (isUnlocked) { - SceneKey.Gone - } else { - SceneKey.Bouncer - } + return if (isUnlocked) SceneKey.Gone else SceneKey.Bouncer } /** The key of the scene we should switch to when swiping left. */ diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt index 722d3661d0ae..a3499bd5c264 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt @@ -26,6 +26,7 @@ import com.android.systemui.classifier.FalsingCollectorActual import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.DisplayId +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.model.SysUiState @@ -63,6 +64,7 @@ class SceneContainerStartable constructor( @Application private val applicationScope: CoroutineScope, private val sceneInteractor: SceneInteractor, + private val deviceEntryInteractor: DeviceEntryInteractor, private val authenticationInteractor: AuthenticationInteractor, private val keyguardInteractor: KeyguardInteractor, private val flags: SceneContainerFlags, @@ -119,7 +121,7 @@ constructor( /** Switches between scenes based on ever-changing application state. */ private fun automaticallySwitchScenes() { applicationScope.launch { - authenticationInteractor.isUnlocked + deviceEntryInteractor.isUnlocked .mapNotNull { isUnlocked -> val renderedScenes = when (val transitionState = sceneInteractor.transitionState.value) { @@ -130,7 +132,6 @@ constructor( transitionState.toScene, ) } - val isBypassEnabled = authenticationInteractor.isBypassEnabled() when { isUnlocked -> when { @@ -141,7 +142,7 @@ constructor( // When the device becomes unlocked in Lockscreen, go to Gone if // bypass is enabled. renderedScenes.contains(SceneKey.Lockscreen) -> - if (isBypassEnabled) { + if (deviceEntryInteractor.isBypassEnabled()) { SceneKey.Gone to "device unlocked in Lockscreen scene with bypass" } else { @@ -191,7 +192,7 @@ constructor( } WakefulnessState.STARTING_TO_WAKE -> { val authMethod = authenticationInteractor.getAuthenticationMethod() - val isUnlocked = authenticationInteractor.isUnlocked.value + val isUnlocked = deviceEntryInteractor.isUnlocked.value when { authMethod == AuthenticationMethodModel.None -> { switchToScene( @@ -241,7 +242,7 @@ constructor( /** Collects and reports signals into the falsing system. */ private fun collectFalsingSignals() { applicationScope.launch { - authenticationInteractor.isLockscreenDismissed.collect { isLockscreenDismissed -> + deviceEntryInteractor.isDeviceEntered.collect { isLockscreenDismissed -> if (isLockscreenDismissed) { falsingCollector.onSuccessfulUnlock() } 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 068d5a59ca2e..9c5a20189dd2 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,10 +16,10 @@ package com.android.systemui.shade.ui.viewmodel -import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor 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 import com.android.systemui.scene.shared.model.SceneKey import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -34,15 +34,15 @@ class ShadeSceneViewModel @Inject constructor( @Application private val applicationScope: CoroutineScope, - authenticationInteractor: AuthenticationInteractor, + deviceEntryInteractor: DeviceEntryInteractor, private val bouncerInteractor: BouncerInteractor, val shadeHeaderViewModel: ShadeHeaderViewModel, ) { /** The key of the scene we should switch to when swiping up. */ val upDestinationSceneKey: StateFlow<SceneKey> = combine( - authenticationInteractor.isUnlocked, - authenticationInteractor.canSwipeToDismiss, + deviceEntryInteractor.isUnlocked, + deviceEntryInteractor.canSwipeToEnter, ) { isUnlocked, canSwipeToDismiss -> upDestinationSceneKey( isUnlocked = isUnlocked, @@ -54,8 +54,8 @@ constructor( started = SharingStarted.WhileSubscribed(), initialValue = upDestinationSceneKey( - isUnlocked = authenticationInteractor.isUnlocked.value, - canSwipeToDismiss = authenticationInteractor.canSwipeToDismiss.value, + isUnlocked = deviceEntryInteractor.isUnlocked.value, + canSwipeToDismiss = deviceEntryInteractor.canSwipeToEnter.value, ), ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 8d86d729d958..eedf35f1e9d4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone; import static android.view.WindowInsets.Type.navigationBars; + import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN; import static com.android.systemui.plugins.ActivityStarter.OnDismissAction; import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK; @@ -66,7 +67,6 @@ import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor; -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor; import com.android.systemui.keyguard.shared.model.DismissAction; @@ -134,7 +134,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // dranw its first frame. private static final long KEYGUARD_DISMISS_DURATION_LOCKED = 2000; - private static String TAG = "StatusBarKeyguardViewManager"; + private static final String TAG = "StatusBarKeyguardViewManager"; private static final boolean DEBUG = false; protected final Context mContext; diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt index f6649bdc9d3f..d54843d39d21 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt @@ -36,10 +36,8 @@ import com.android.internal.logging.UiEventLogger import com.android.internal.widget.LockPatternUtils import com.android.keyguard.KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback import com.android.keyguard.KeyguardSecurityModel.SecurityMode -import com.android.systemui.res.R import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase -import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate import com.android.systemui.biometrics.SideFpsController import com.android.systemui.biometrics.SideFpsUiRequestSource @@ -47,6 +45,7 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants import com.android.systemui.classifier.FalsingA11yDelegate import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor @@ -54,6 +53,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac import com.android.systemui.log.SessionTracker import com.android.systemui.plugins.ActivityStarter.OnDismissAction import com.android.systemui.plugins.FalsingManager +import com.android.systemui.res.R import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.ObservableTransitionState @@ -157,7 +157,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { private lateinit var sceneTestUtils: SceneTestUtils private lateinit var sceneInteractor: SceneInteractor private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor - private lateinit var authenticationInteractor: AuthenticationInteractor + private lateinit var deviceEntryInteractor: DeviceEntryInteractor @Mock private lateinit var primaryBouncerInteractor: Lazy<PrimaryBouncerInteractor> private lateinit var sceneTransitionStateFlow: MutableStateFlow<ObservableTransitionState> @@ -229,10 +229,10 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { sceneTransitionStateFlow = MutableStateFlow(ObservableTransitionState.Idle(SceneKey.Lockscreen)) sceneInteractor.setTransitionState(sceneTransitionStateFlow) - authenticationInteractor = - sceneTestUtils.authenticationInteractor( - repository = sceneTestUtils.authenticationRepository(), - sceneInteractor = sceneInteractor + deviceEntryInteractor = + sceneTestUtils.deviceEntryInteractor( + authenticationInteractor = sceneTestUtils.authenticationInteractor(), + sceneInteractor = sceneInteractor, ) underTest = @@ -268,7 +268,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { keyguardTransitionInteractor, primaryBouncerInteractor, ) { - authenticationInteractor + deviceEntryInteractor } } 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 d3a2a73959dd..0283382d02be 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 @@ -74,7 +74,6 @@ class AuthenticationRepositoryTest : SysuiTestCase() { getSecurityMode = getSecurityMode, backgroundDispatcher = testUtils.testDispatcher, userRepository = userRepository, - keyguardRepository = testUtils.keyguardRepository, lockPatternUtils = lockPatternUtils, broadcastDispatcher = fakeBroadcastDispatcher, ) 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 874053a83654..a102890db7b0 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 @@ -20,15 +20,12 @@ import android.app.admin.DevicePolicyManager import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel -import com.android.systemui.authentication.data.repository.AuthenticationRepository import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.shared.model.SceneModel import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds @@ -47,13 +44,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope - private val repository: AuthenticationRepository = utils.authenticationRepository() - private val sceneInteractor = utils.sceneInteractor() - private val underTest = - utils.authenticationInteractor( - repository = repository, - sceneInteractor = sceneInteractor, - ) + private val underTest = utils.authenticationInteractor() @Test fun authenticationMethod() = @@ -79,10 +70,10 @@ class AuthenticationInteractorTest : SysuiTestCase() { val authMethod by collectLastValue(underTest.authenticationMethod) runCurrent() - utils.authenticationRepository.apply { - setAuthenticationMethod(DataLayerAuthenticationMethodModel.None) - setLockscreenEnabled(true) - } + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.None + ) + utils.deviceEntryRepository.setInsecureLockscreenEnabled(true) assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.Swipe) assertThat(underTest.getAuthenticationMethod()) @@ -95,10 +86,10 @@ class AuthenticationInteractorTest : SysuiTestCase() { val authMethod by collectLastValue(underTest.authenticationMethod) runCurrent() - utils.authenticationRepository.apply { - setAuthenticationMethod(DataLayerAuthenticationMethodModel.None) - setLockscreenEnabled(false) - } + utils.authenticationRepository.setAuthenticationMethod( + DataLayerAuthenticationMethodModel.None + ) + utils.deviceEntryRepository.setInsecureLockscreenEnabled(false) assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.None) assertThat(underTest.getAuthenticationMethod()) @@ -106,130 +97,6 @@ class AuthenticationInteractorTest : SysuiTestCase() { } @Test - fun isUnlocked_whenAuthMethodIsNoneAndLockscreenDisabled_isTrue() = - testScope.runTest { - val isUnlocked by collectLastValue(underTest.isUnlocked) - utils.authenticationRepository.apply { - setAuthenticationMethod(DataLayerAuthenticationMethodModel.None) - setLockscreenEnabled(false) - // Toggle isUnlocked, twice. - // - // This is done because the underTest.isUnlocked flow doesn't receive values from - // just changing the state above; the actual isUnlocked state needs to change to - // cause the logic under test to "pick up" the current state again. - // - // It is done twice to make sure that we don't actually change the isUnlocked state - // from what it originally was. - setUnlocked(!utils.authenticationRepository.isUnlocked.value) - runCurrent() - setUnlocked(!utils.authenticationRepository.isUnlocked.value) - runCurrent() - } - - assertThat(isUnlocked).isTrue() - } - - @Test - fun isUnlocked_whenAuthMethodIsNoneAndLockscreenEnabled_isTrue() = - testScope.runTest { - utils.authenticationRepository.apply { - setAuthenticationMethod(DataLayerAuthenticationMethodModel.None) - setLockscreenEnabled(true) - } - - val isUnlocked by collectLastValue(underTest.isUnlocked) - assertThat(isUnlocked).isTrue() - } - - @Test - fun canSwipeToDismiss_onLockscreenWithSwipe_isTrue() = - testScope.runTest { - utils.authenticationRepository.apply { - setAuthenticationMethod(DataLayerAuthenticationMethodModel.None) - setLockscreenEnabled(true) - } - switchToScene(SceneKey.Lockscreen) - - val canSwipeToDismiss by collectLastValue(underTest.canSwipeToDismiss) - assertThat(canSwipeToDismiss).isTrue() - } - - @Test - fun canSwipeToDismiss_onLockscreenWithPin_isFalse() = - testScope.runTest { - utils.authenticationRepository.apply { - setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin) - setLockscreenEnabled(true) - } - switchToScene(SceneKey.Lockscreen) - - val canSwipeToDismiss by collectLastValue(underTest.canSwipeToDismiss) - assertThat(canSwipeToDismiss).isFalse() - } - - @Test - fun canSwipeToDismiss_afterLockscreenDismissedInSwipeMode_isFalse() = - testScope.runTest { - utils.authenticationRepository.apply { - setAuthenticationMethod(DataLayerAuthenticationMethodModel.None) - setLockscreenEnabled(true) - } - switchToScene(SceneKey.Lockscreen) - switchToScene(SceneKey.Gone) - - val canSwipeToDismiss by collectLastValue(underTest.canSwipeToDismiss) - assertThat(canSwipeToDismiss).isFalse() - } - - @Test - fun isAuthenticationRequired_lockedAndSecured_true() = - testScope.runTest { - utils.authenticationRepository.apply { - setUnlocked(false) - runCurrent() - setAuthenticationMethod(DataLayerAuthenticationMethodModel.Password) - } - - assertThat(underTest.isAuthenticationRequired()).isTrue() - } - - @Test - fun isAuthenticationRequired_lockedAndNotSecured_false() = - testScope.runTest { - utils.authenticationRepository.apply { - setUnlocked(false) - runCurrent() - setAuthenticationMethod(DataLayerAuthenticationMethodModel.None) - } - - assertThat(underTest.isAuthenticationRequired()).isFalse() - } - - @Test - fun isAuthenticationRequired_unlockedAndSecured_false() = - testScope.runTest { - utils.authenticationRepository.apply { - setUnlocked(true) - runCurrent() - setAuthenticationMethod(DataLayerAuthenticationMethodModel.Password) - } - - assertThat(underTest.isAuthenticationRequired()).isFalse() - } - - @Test - fun isAuthenticationRequired_unlockedAndNotSecured_false() = - testScope.runTest { - utils.authenticationRepository.apply { - setUnlocked(true) - runCurrent() - setAuthenticationMethod(DataLayerAuthenticationMethodModel.None) - } - - assertThat(underTest.isAuthenticationRequired()).isFalse() - } - - @Test fun authenticate_withCorrectPin_returnsTrue() = testScope.runTest { val isThrottled by collectLastValue(underTest.isThrottled) @@ -366,7 +233,6 @@ class AuthenticationInteractorTest : SysuiTestCase() { @Test fun tryAutoConfirm_withAutoConfirmWrongPinCorrectLength_returnsFalseAndDoesNotUnlockDevice() = testScope.runTest { - val isUnlocked by collectLastValue(underTest.isUnlocked) utils.authenticationRepository.apply { setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin) setAutoConfirmEnabled(true) @@ -378,13 +244,13 @@ class AuthenticationInteractorTest : SysuiTestCase() { ) ) .isEqualTo(AuthenticationResult.FAILED) + val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked) assertThat(isUnlocked).isFalse() } @Test fun tryAutoConfirm_withAutoConfirmLongerPin_returnsFalseAndDoesNotUnlockDevice() = testScope.runTest { - val isUnlocked by collectLastValue(underTest.isUnlocked) utils.authenticationRepository.apply { setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin) setAutoConfirmEnabled(true) @@ -396,13 +262,13 @@ class AuthenticationInteractorTest : SysuiTestCase() { ) ) .isEqualTo(AuthenticationResult.FAILED) + val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked) assertThat(isUnlocked).isFalse() } @Test fun tryAutoConfirm_withAutoConfirmCorrectPin_returnsTrueAndUnlocksDevice() = testScope.runTest { - val isUnlocked by collectLastValue(underTest.isUnlocked) utils.authenticationRepository.apply { setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin) setAutoConfirmEnabled(true) @@ -414,13 +280,13 @@ class AuthenticationInteractorTest : SysuiTestCase() { ) ) .isEqualTo(AuthenticationResult.SUCCEEDED) + val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked) assertThat(isUnlocked).isTrue() } @Test fun tryAutoConfirm_withoutAutoConfirmButCorrectPin_returnsNullAndHasNoEffects() = testScope.runTest { - val isUnlocked by collectLastValue(underTest.isUnlocked) utils.authenticationRepository.apply { setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin) setAutoConfirmEnabled(false) @@ -432,26 +298,27 @@ class AuthenticationInteractorTest : SysuiTestCase() { ) ) .isEqualTo(AuthenticationResult.SKIPPED) + val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked) assertThat(isUnlocked).isFalse() } @Test fun tryAutoConfirm_withoutCorrectPassword_returnsNullAndHasNoEffects() = testScope.runTest { - val isUnlocked by collectLastValue(underTest.isUnlocked) utils.authenticationRepository.setAuthenticationMethod( DataLayerAuthenticationMethodModel.Password ) 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(underTest.isUnlocked) + val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked) val throttling by collectLastValue(underTest.throttling) val isThrottled by collectLastValue(underTest.isThrottled) utils.authenticationRepository.setAuthenticationMethod( @@ -462,7 +329,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { assertThat(isThrottled).isFalse() assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) - utils.authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) assertThat(isUnlocked).isFalse() assertThat(isThrottled).isFalse() assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) @@ -605,8 +472,4 @@ class AuthenticationInteractorTest : SysuiTestCase() { assertThat(hintedPinLength).isNull() } - - private fun switchToScene(sceneKey: SceneKey) { - sceneInteractor.changeScene(SceneModel(sceneKey), "reason") - } } 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 92c8a395a696..a9ba36a9fde4 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 @@ -47,13 +47,16 @@ class BouncerInteractorTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope - private val authenticationInteractor = - utils.authenticationInteractor( - repository = utils.authenticationRepository(), - ) + 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, ) @@ -76,7 +79,7 @@ class BouncerInteractorTest : SysuiTestCase() { utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) runCurrent() - utils.authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) underTest.showOrUnlockDevice() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN) @@ -111,7 +114,7 @@ class BouncerInteractorTest : SysuiTestCase() { utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) runCurrent() utils.authenticationRepository.setAutoConfirmEnabled(true) - utils.authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) underTest.showOrUnlockDevice() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN) @@ -148,7 +151,7 @@ class BouncerInteractorTest : SysuiTestCase() { utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) runCurrent() - utils.authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) underTest.showOrUnlockDevice() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.clearMessage() @@ -180,7 +183,7 @@ class BouncerInteractorTest : SysuiTestCase() { AuthenticationMethodModel.Password ) runCurrent() - utils.authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) underTest.showOrUnlockDevice() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD) @@ -215,7 +218,7 @@ class BouncerInteractorTest : SysuiTestCase() { AuthenticationMethodModel.Pattern ) runCurrent() - utils.authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) underTest.showOrUnlockDevice() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN) @@ -268,7 +271,7 @@ class BouncerInteractorTest : SysuiTestCase() { testScope.runTest { val currentScene by collectLastValue(sceneInteractor.desiredScene) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setUnlocked(true) + utils.deviceEntryRepository.setUnlocked(true) runCurrent() underTest.showOrUnlockDevice() @@ -281,8 +284,8 @@ class BouncerInteractorTest : SysuiTestCase() { testScope.runTest { val currentScene by collectLastValue(sceneInteractor.desiredScene) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) - utils.authenticationRepository.setLockscreenEnabled(true) - utils.authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setInsecureLockscreenEnabled(true) + utils.deviceEntryRepository.setUnlocked(false) underTest.showOrUnlockDevice() @@ -298,7 +301,7 @@ class BouncerInteractorTest : SysuiTestCase() { AuthenticationMethodModel.Password ) runCurrent() - utils.authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) val customMessage = "Hello there!" underTest.showOrUnlockDevice(customMessage) 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 2f7dde02fdce..b5177e1587ee 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 @@ -37,17 +37,20 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope - private val authenticationInteractor = - utils.authenticationInteractor( - utils.authenticationRepository(), - ) + 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 da2534d6fb14..b75355a82406 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 @@ -44,12 +44,15 @@ class BouncerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope - private val authenticationInteractor = - utils.authenticationInteractor( - repository = utils.authenticationRepository, + 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 = utils.sceneInteractor(), ) @@ -223,6 +226,8 @@ class BouncerViewModelTest : SysuiTestCase() { DataLayerAuthenticationMethodModel.Pattern } ) - setLockscreenEnabled(model !is DomainLayerAuthenticationMethodModel.None) + utils.deviceEntryRepository.setInsecureLockscreenEnabled( + model !is DomainLayerAuthenticationMethodModel.None + ) } } 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 c1b33542267b..0926399a8617 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 @@ -44,13 +44,16 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope - private val authenticationInteractor = - utils.authenticationInteractor( - repository = utils.authenticationRepository(), - ) + 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, ) @@ -139,7 +142,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { utils.authenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.Password ) - utils.authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) @@ -207,7 +210,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { private fun TestScope.lockDeviceAndOpenPasswordBouncer() { utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Password) - utils.authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") 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 bf109d9b2b61..2e7c9aaa67e8 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 @@ -47,13 +47,16 @@ class PatternBouncerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope - private val authenticationInteractor = - utils.authenticationInteractor( - repository = utils.authenticationRepository(), - ) + 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, ) @@ -378,7 +381,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { private fun TestScope.lockDeviceAndOpenPatternBouncer() { utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pattern) - utils.authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") assertThat(collectLastValue(sceneInteractor.desiredScene).invoke()) 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 2576204c247f..255bbe3ae231 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 @@ -47,12 +47,15 @@ class PinBouncerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope private val sceneInteractor = utils.sceneInteractor() - private val authenticationInteractor = - utils.authenticationInteractor( - repository = utils.authenticationRepository(), + 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, ) @@ -81,7 +84,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { val currentScene by collectLastValue(sceneInteractor.desiredScene) val message by collectLastValue(bouncerViewModel.message) val pin by collectLastValue(underTest.pinInput.map { it.getPin() }) - utils.authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") @@ -103,7 +106,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { val message by collectLastValue(bouncerViewModel.message) val pin by collectLastValue(underTest.pinInput.map { it.getPin() }) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") @@ -358,7 +361,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { private fun TestScope.lockDeviceAndOpenPinBouncer() { utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") 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 new file mode 100644 index 000000000000..8e8cbe4a7cf2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt @@ -0,0 +1,127 @@ +package com.android.systemui.deviceentry.data.repository + +import android.content.pm.UserInfo +import androidx.test.filters.SmallTest +import com.android.internal.widget.LockPatternUtils +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.scene.SceneTestUtils +import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.user.data.repository.FakeUserRepository +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class DeviceEntryRepositoryTest : SysuiTestCase() { + + @Mock private lateinit var lockPatternUtils: LockPatternUtils + @Mock private lateinit var keyguardBypassController: KeyguardBypassController + @Mock private lateinit var keyguardStateController: KeyguardStateController + + private val testUtils = SceneTestUtils(this) + private val testScope = testUtils.testScope + private val userRepository = FakeUserRepository() + + private lateinit var underTest: DeviceEntryRepository + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + userRepository.setUserInfos(USER_INFOS) + runBlocking { userRepository.setSelectedUserInfo(USER_INFOS[0]) } + + underTest = + DeviceEntryRepositoryImpl( + applicationScope = testScope.backgroundScope, + backgroundDispatcher = testUtils.testDispatcher, + userRepository = userRepository, + lockPatternUtils = lockPatternUtils, + keyguardBypassController = keyguardBypassController, + keyguardStateController = keyguardStateController, + ) + } + + @Test + fun isUnlocked() = + testScope.runTest { + whenever(keyguardStateController.isUnlocked).thenReturn(false) + val isUnlocked by collectLastValue(underTest.isUnlocked) + + runCurrent() + assertThat(isUnlocked).isFalse() + + val captor = argumentCaptor<KeyguardStateController.Callback>() + Mockito.verify(keyguardStateController, Mockito.atLeastOnce()) + .addCallback(captor.capture()) + + whenever(keyguardStateController.isUnlocked).thenReturn(true) + captor.value.onUnlockedChanged() + runCurrent() + assertThat(isUnlocked).isTrue() + + whenever(keyguardStateController.isUnlocked).thenReturn(false) + captor.value.onKeyguardShowingChanged() + runCurrent() + assertThat(isUnlocked).isFalse() + } + + @Test + fun isInsecureLockscreenEnabled() = + testScope.runTest { + whenever(lockPatternUtils.isLockScreenDisabled(USER_INFOS[0].id)).thenReturn(false) + whenever(lockPatternUtils.isLockScreenDisabled(USER_INFOS[1].id)).thenReturn(true) + + userRepository.setSelectedUserInfo(USER_INFOS[0]) + assertThat(underTest.isInsecureLockscreenEnabled()).isTrue() + + userRepository.setSelectedUserInfo(USER_INFOS[1]) + assertThat(underTest.isInsecureLockscreenEnabled()).isFalse() + } + + @Test + fun isBypassEnabled_disabledInController() = + testScope.runTest { + whenever(keyguardBypassController.isBypassEnabled).thenAnswer { false } + whenever(keyguardBypassController.bypassEnabled).thenAnswer { false } + assertThat(underTest.isBypassEnabled()).isFalse() + } + + @Test + fun isBypassEnabled_enabledInController() = + testScope.runTest { + whenever(keyguardBypassController.isBypassEnabled).thenAnswer { true } + whenever(keyguardBypassController.bypassEnabled).thenAnswer { true } + assertThat(underTest.isBypassEnabled()).isTrue() + } + + companion object { + private val USER_INFOS = + listOf( + UserInfo( + /* id= */ 100, + /* name= */ "First user", + /* flags= */ 0, + ), + UserInfo( + /* id= */ 101, + /* name= */ "Second user", + /* flags= */ 0, + ), + ) + } +} 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 new file mode 100644 index 000000000000..55582e123969 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt @@ -0,0 +1,234 @@ +package com.android.systemui.deviceentry.domain.interactor + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.model.AuthenticationMethodModel +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository +import com.android.systemui.scene.SceneTestUtils +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.SceneModel +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class DeviceEntryInteractorTest : SysuiTestCase() { + + private val utils = SceneTestUtils(this) + private val testScope = utils.testScope + private val repository: FakeDeviceEntryRepository = utils.deviceEntryRepository + private val sceneInteractor = utils.sceneInteractor() + private val authenticationInteractor = utils.authenticationInteractor() + private val underTest = + utils.deviceEntryInteractor( + repository = repository, + authenticationInteractor = authenticationInteractor, + sceneInteractor = sceneInteractor, + ) + + @Test + fun isUnlocked_whenAuthMethodIsNoneAndLockscreenDisabled_isTrue() = + testScope.runTest { + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + utils.deviceEntryRepository.apply { + setInsecureLockscreenEnabled(false) + + // Toggle isUnlocked, twice. + // + // This is done because the underTest.isUnlocked flow doesn't receive values from + // just changing the state above; the actual isUnlocked state needs to change to + // cause the logic under test to "pick up" the current state again. + // + // It is done twice to make sure that we don't actually change the isUnlocked state + // from what it originally was. + setUnlocked(!isUnlocked.value) + runCurrent() + setUnlocked(!isUnlocked.value) + runCurrent() + } + + val isUnlocked by collectLastValue(underTest.isUnlocked) + assertThat(isUnlocked).isTrue() + } + + @Test + fun isUnlocked_whenAuthMethodIsNoneAndLockscreenEnabled_isTrue() = + testScope.runTest { + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + utils.deviceEntryRepository.setInsecureLockscreenEnabled(true) + + val isUnlocked by collectLastValue(underTest.isUnlocked) + assertThat(isUnlocked).isTrue() + } + + @Test + fun isDeviceEntered_onLockscreenWithSwipe_isFalse() = + testScope.runTest { + val isDeviceEntered by collectLastValue(underTest.isDeviceEntered) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + utils.deviceEntryRepository.setInsecureLockscreenEnabled(true) + switchToScene(SceneKey.Lockscreen) + + assertThat(isDeviceEntered).isFalse() + } + + @Test + fun isDeviceEntered_onShadeBeforeDismissingLockscreenWithSwipe_isFalse() = + testScope.runTest { + val isDeviceEntered by collectLastValue(underTest.isDeviceEntered) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + utils.deviceEntryRepository.setInsecureLockscreenEnabled(true) + switchToScene(SceneKey.Lockscreen) + runCurrent() + switchToScene(SceneKey.Shade) + + assertThat(isDeviceEntered).isFalse() + } + + @Test + fun isDeviceEntered_afterDismissingLockscreenWithSwipe_isTrue() = + testScope.runTest { + val isDeviceEntered by collectLastValue(underTest.isDeviceEntered) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + utils.deviceEntryRepository.setInsecureLockscreenEnabled(true) + switchToScene(SceneKey.Lockscreen) + runCurrent() + switchToScene(SceneKey.Gone) + + assertThat(isDeviceEntered).isTrue() + } + + @Test + fun isDeviceEntered_onShadeAfterDismissingLockscreenWithSwipe_isTrue() = + testScope.runTest { + val isDeviceEntered by collectLastValue(underTest.isDeviceEntered) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + utils.deviceEntryRepository.setInsecureLockscreenEnabled(true) + switchToScene(SceneKey.Lockscreen) + runCurrent() + switchToScene(SceneKey.Gone) + runCurrent() + switchToScene(SceneKey.Shade) + + assertThat(isDeviceEntered).isTrue() + } + + @Test + fun isDeviceEntered_onBouncer_isFalse() = + testScope.runTest { + utils.authenticationRepository.setAuthenticationMethod( + AuthenticationMethodModel.Pattern + ) + utils.deviceEntryRepository.setInsecureLockscreenEnabled(true) + switchToScene(SceneKey.Lockscreen) + runCurrent() + switchToScene(SceneKey.Bouncer) + + val isDeviceEntered by collectLastValue(underTest.isDeviceEntered) + assertThat(isDeviceEntered).isFalse() + } + + @Test + fun canSwipeToEnter_onLockscreenWithSwipe_isTrue() = + testScope.runTest { + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + utils.deviceEntryRepository.setInsecureLockscreenEnabled(true) + switchToScene(SceneKey.Lockscreen) + + val canSwipeToEnter by collectLastValue(underTest.canSwipeToEnter) + assertThat(canSwipeToEnter).isTrue() + } + + @Test + fun canSwipeToEnter_onLockscreenWithPin_isFalse() = + testScope.runTest { + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.deviceEntryRepository.setInsecureLockscreenEnabled(true) + switchToScene(SceneKey.Lockscreen) + + val canSwipeToEnter by collectLastValue(underTest.canSwipeToEnter) + assertThat(canSwipeToEnter).isFalse() + } + + @Test + fun canSwipeToEnter_afterLockscreenDismissedInSwipeMode_isFalse() = + testScope.runTest { + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + utils.deviceEntryRepository.setInsecureLockscreenEnabled(true) + switchToScene(SceneKey.Lockscreen) + runCurrent() + switchToScene(SceneKey.Gone) + + val canSwipeToEnter by collectLastValue(underTest.canSwipeToEnter) + assertThat(canSwipeToEnter).isFalse() + } + + @Test + fun isAuthenticationRequired_lockedAndSecured_true() = + testScope.runTest { + utils.deviceEntryRepository.setUnlocked(false) + runCurrent() + utils.authenticationRepository.setAuthenticationMethod( + AuthenticationMethodModel.Password + ) + + assertThat(underTest.isAuthenticationRequired()).isTrue() + } + + @Test + fun isAuthenticationRequired_lockedAndNotSecured_false() = + testScope.runTest { + utils.deviceEntryRepository.setUnlocked(false) + runCurrent() + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + + assertThat(underTest.isAuthenticationRequired()).isFalse() + } + + @Test + fun isAuthenticationRequired_unlockedAndSecured_false() = + testScope.runTest { + utils.deviceEntryRepository.setUnlocked(true) + runCurrent() + utils.authenticationRepository.setAuthenticationMethod( + AuthenticationMethodModel.Password + ) + + assertThat(underTest.isAuthenticationRequired()).isFalse() + } + + @Test + fun isAuthenticationRequired_unlockedAndNotSecured_false() = + testScope.runTest { + utils.deviceEntryRepository.setUnlocked(true) + runCurrent() + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + + assertThat(underTest.isAuthenticationRequired()).isFalse() + } + + @Test + fun isBypassEnabled_enabledInRepository_true() = + testScope.runTest { + utils.deviceEntryRepository.setBypassEnabled(true) + assertThat(underTest.isBypassEnabled()).isTrue() + } + + @Test + fun isBypassEnabled_disabledInRepository_false() = + testScope.runTest { + utils.deviceEntryRepository.setBypassEnabled(false) + assertThat(underTest.isBypassEnabled()).isFalse() + } + + private fun switchToScene(sceneKey: SceneKey) { + sceneInteractor.changeScene(SceneModel(sceneKey), "reason") + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt index 2691860bc25b..f93051c41b61 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt @@ -44,7 +44,6 @@ import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.phone.DozeParameters -import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor @@ -81,7 +80,6 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { @Mock private lateinit var biometricUnlockController: BiometricUnlockController @Mock private lateinit var dozeTransitionListener: DozeTransitionListener @Mock private lateinit var authController: AuthController - @Mock private lateinit var keyguardBypassController: KeyguardBypassController @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor @Mock private lateinit var dreamOverlayCallbackController: DreamOverlayCallbackController @Mock private lateinit var dozeParameters: DozeParameters @@ -103,7 +101,6 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { screenLifecycle, biometricUnlockController, keyguardStateController, - keyguardBypassController, keyguardUpdateMonitor, dozeTransitionListener, dozeParameters, @@ -213,23 +210,9 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { } @Test - fun isBypassEnabled_disabledInController() { - whenever(keyguardBypassController.isBypassEnabled).thenReturn(false) - whenever(keyguardBypassController.bypassEnabled).thenReturn(false) - assertThat(underTest.isBypassEnabled()).isFalse() - } - - @Test - fun isBypassEnabled_enabledInController() { - whenever(keyguardBypassController.isBypassEnabled).thenReturn(true) - whenever(keyguardBypassController.bypassEnabled).thenReturn(true) - assertThat(underTest.isBypassEnabled()).isTrue() - } - - @Test fun isAodAvailable() = runTest { val flow = underTest.isAodAvailable - var isAodAvailable = collectLastValue(flow) + val isAodAvailable = collectLastValue(flow) runCurrent() val callback = @@ -273,29 +256,6 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { } @Test - fun isKeyguardUnlocked() = - testScope.runTest { - whenever(keyguardStateController.isUnlocked).thenReturn(false) - val isKeyguardUnlocked by collectLastValue(underTest.isKeyguardUnlocked) - - runCurrent() - assertThat(isKeyguardUnlocked).isFalse() - - val captor = argumentCaptor<KeyguardStateController.Callback>() - verify(keyguardStateController, atLeastOnce()).addCallback(captor.capture()) - - whenever(keyguardStateController.isUnlocked).thenReturn(true) - captor.value.onUnlockedChanged() - runCurrent() - assertThat(isKeyguardUnlocked).isTrue() - - whenever(keyguardStateController.isUnlocked).thenReturn(false) - captor.value.onKeyguardShowingChanged() - runCurrent() - assertThat(isKeyguardUnlocked).isFalse() - } - - @Test fun isDozing() = testScope.runTest { underTest.setIsDozing(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt index 90e217f3418c..82c7fa4c4d12 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt @@ -15,8 +15,6 @@ * */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package com.android.systemui.keyguard.domain.interactor import android.app.StatusBarManager @@ -27,13 +25,11 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR import com.android.systemui.keyguard.data.repository.FakeCommandQueue -import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel import com.android.systemui.scene.SceneTestUtils -import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.shade.data.repository.FakeShadeRepository @@ -42,62 +38,53 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.onCompletion -import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.MockitoAnnotations +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RoboPilotTest @RunWith(AndroidJUnit4::class) class KeyguardInteractorTest : SysuiTestCase() { - private lateinit var commandQueue: FakeCommandQueue - private lateinit var featureFlags: FakeFeatureFlags - private lateinit var testScope: TestScope - - private lateinit var underTest: KeyguardInteractor - private lateinit var repository: FakeKeyguardRepository - private lateinit var bouncerRepository: FakeKeyguardBouncerRepository - private lateinit var configurationRepository: FakeConfigurationRepository - private lateinit var shadeRepository: FakeShadeRepository - private lateinit var sceneInteractor: SceneInteractor - private lateinit var transitionState: MutableStateFlow<ObservableTransitionState> + + private val testUtils = SceneTestUtils(this) + private val testScope = testUtils.testScope + private val repository = testUtils.keyguardRepository + private val sceneInteractor = testUtils.sceneInteractor() + private val commandQueue = FakeCommandQueue() + private val featureFlags = FakeFeatureFlagsClassic().apply { set(FACE_AUTH_REFACTOR, true) } + private val bouncerRepository = FakeKeyguardBouncerRepository() + private val configurationRepository = FakeConfigurationRepository() + private val shadeRepository = FakeShadeRepository() + private val transitionState: MutableStateFlow<ObservableTransitionState> = + MutableStateFlow(ObservableTransitionState.Idle(SceneKey.Gone)) + + private val underTest = + KeyguardInteractor( + repository = repository, + commandQueue = commandQueue, + featureFlags = featureFlags, + sceneContainerFlags = testUtils.sceneContainerFlags, + deviceEntryRepository = testUtils.deviceEntryRepository, + bouncerRepository = bouncerRepository, + configurationRepository = configurationRepository, + shadeRepository = shadeRepository, + sceneInteractorProvider = { sceneInteractor }, + ) @Before fun setUp() { - MockitoAnnotations.initMocks(this) - featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) } - commandQueue = FakeCommandQueue() - val sceneTestUtils = SceneTestUtils(this) - testScope = sceneTestUtils.testScope - repository = sceneTestUtils.keyguardRepository - bouncerRepository = FakeKeyguardBouncerRepository() - configurationRepository = FakeConfigurationRepository() - shadeRepository = FakeShadeRepository() - sceneInteractor = sceneTestUtils.sceneInteractor() - transitionState = MutableStateFlow(ObservableTransitionState.Idle(SceneKey.Gone)) sceneInteractor.setTransitionState(transitionState) - underTest = - KeyguardInteractor( - repository = repository, - commandQueue = commandQueue, - featureFlags = featureFlags, - sceneContainerFlags = sceneTestUtils.sceneContainerFlags, - bouncerRepository = bouncerRepository, - configurationRepository = configurationRepository, - shadeRepository = shadeRepository, - sceneInteractorProvider = { sceneInteractor }, - ) } @Test fun onCameraLaunchDetected() = testScope.runTest { val flow = underTest.onCameraLaunchDetected - var cameraLaunchSource = collectLastValue(flow) + val cameraLaunchSource = collectLastValue(flow) runCurrent() commandQueue.doForEachCallback { @@ -175,7 +162,7 @@ class KeyguardInteractorTest : SysuiTestCase() { @Test fun keyguardVisibilityIsDefinedAsKeyguardShowingButNotOccluded() = runTest { - var isVisible = collectLastValue(underTest.isKeyguardVisible) + val isVisible = collectLastValue(underTest.isKeyguardVisible) repository.setKeyguardShowing(true) repository.setKeyguardOccluded(false) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index f2636c543844..591653ee214f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -1226,7 +1226,6 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { // GIVEN the keyguard is showing locked keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) - keyguardRepository.setKeyguardUnlocked(false) runCurrent() shadeRepository.setShadeModel( ShadeModel( diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt index 1681cfdce822..6e94691d42a9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt @@ -37,10 +37,6 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope private val sceneInteractor = utils.sceneInteractor() - private val authenticationInteractor = - utils.authenticationInteractor( - repository = utils.authenticationRepository(), - ) private val underTest = createLockscreenSceneViewModel() @@ -49,8 +45,8 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { testScope.runTest { val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) - utils.authenticationRepository.setLockscreenEnabled(true) - utils.authenticationRepository.setUnlocked(true) + utils.deviceEntryRepository.setInsecureLockscreenEnabled(true) + utils.deviceEntryRepository.setUnlocked(true) sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen), "reason") assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone) @@ -61,7 +57,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { testScope.runTest { val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen), "reason") assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer) @@ -88,7 +84,11 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel { return LockscreenSceneViewModel( applicationScope = testScope.backgroundScope, - authenticationInteractor = authenticationInteractor, + deviceEntryInteractor = + utils.deviceEntryInteractor( + authenticationInteractor = utils.authenticationInteractor(), + sceneInteractor = utils.sceneInteractor(), + ), communalInteractor = utils.communalInteractor(), longPress = KeyguardLongPressViewModel( 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 8ae89304974d..f1c99d71b4e5 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 @@ -48,11 +48,6 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope private val sceneInteractor = utils.sceneInteractor() - private val authenticationInteractor = - utils.authenticationInteractor( - repository = utils.authenticationRepository(), - ) - private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock()) private var mobileIconsViewModel: MobileIconsViewModel = @@ -85,10 +80,17 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { broadcastDispatcher = fakeBroadcastDispatcher, ) + val authenticationInteractor = utils.authenticationInteractor() + underTest = QuickSettingsSceneViewModel( bouncerInteractor = utils.bouncerInteractor( + deviceEntryInteractor = + utils.deviceEntryInteractor( + authenticationInteractor = authenticationInteractor, + sceneInteractor = sceneInteractor, + ), authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, ), @@ -101,7 +103,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { testScope.runTest { val currentScene by collectLastValue(sceneInteractor.desiredScene) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setUnlocked(true) + utils.deviceEntryRepository.setUnlocked(true) runCurrent() underTest.onContentClicked() @@ -114,7 +116,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { testScope.runTest { val currentScene by collectLastValue(sceneInteractor.desiredScene) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) runCurrent() underTest.onContentClicked() 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 85bd92bf3b12..5259013afc95 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -83,7 +83,6 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope - private val sceneContainerConfig = utils.fakeSceneContainerConfig() private val sceneRepository = utils.fakeSceneContainerRepository( @@ -93,14 +92,12 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { utils.sceneInteractor( repository = sceneRepository, ) - - private val authenticationRepository = utils.authenticationRepository() - private val authenticationInteractor = - utils.authenticationInteractor( - repository = authenticationRepository, + private val authenticationInteractor = utils.authenticationInteractor() + private val deviceEntryInteractor = + utils.deviceEntryInteractor( + authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, ) - private val communalInteractor = utils.communalInteractor() private val transitionState = @@ -116,6 +113,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { private val bouncerInteractor = utils.bouncerInteractor( + deviceEntryInteractor = deviceEntryInteractor, authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, ) @@ -128,7 +126,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { private val lockscreenSceneViewModel = LockscreenSceneViewModel( applicationScope = testScope.backgroundScope, - authenticationInteractor = authenticationInteractor, + deviceEntryInteractor = deviceEntryInteractor, communalInteractor = communalInteractor, longPress = KeyguardLongPressViewModel( @@ -178,12 +176,12 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { shadeSceneViewModel = ShadeSceneViewModel( applicationScope = testScope.backgroundScope, - authenticationInteractor = authenticationInteractor, + deviceEntryInteractor = deviceEntryInteractor, bouncerInteractor = bouncerInteractor, shadeHeaderViewModel = shadeHeaderViewModel, ) - authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) val displayTracker = FakeDisplayTracker(context) val sysUiState = SysUiState(displayTracker) @@ -191,6 +189,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { SceneContainerStartable( applicationScope = testScope.backgroundScope, sceneInteractor = sceneInteractor, + deviceEntryInteractor = deviceEntryInteractor, authenticationInteractor = authenticationInteractor, keyguardInteractor = keyguardInteractor, flags = utils.sceneContainerFlags, @@ -417,13 +416,13 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { // Set the lockscreen enabled bit _before_ set the auth method as the code picks up on the // lockscreen enabled bit _after_ the auth method is changed and the lockscreen enabled bit // is not an observable that can trigger a new evaluation. - authenticationRepository.setLockscreenEnabled( + utils.deviceEntryRepository.setInsecureLockscreenEnabled( authMethod !is DomainLayerAuthenticationMethodModel.None ) - authenticationRepository.setAuthenticationMethod(authMethod.toDataLayer()) + utils.authenticationRepository.setAuthenticationMethod(authMethod.toDataLayer()) if (!authMethod.isSecure) { // When the auth method is not secure, the device is never considered locked. - authenticationRepository.setUnlocked(true) + utils.deviceEntryRepository.setUnlocked(true) } runCurrent() } @@ -528,14 +527,14 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { .that(authMethod.isSecure) .isTrue() - authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) runCurrent() } /** Unlocks the device by entering the correct PIN. Ends up in the Gone scene. */ private fun TestScope.unlockDevice() { assertWithMessage("Cannot unlock a device that's already unlocked!") - .that(authenticationInteractor.isUnlocked.value) + .that(deviceEntryInteractor.isUnlocked.value) .isFalse() emulateUserDrivenTransition(SceneKey.Bouncer) diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index 16fdf8e40ed1..00a20ccc1c0d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -59,17 +59,13 @@ class SceneContainerStartableTest : SysuiTestCase() { private val testScope = utils.testScope private val sceneInteractor = utils.sceneInteractor() private val sceneContainerFlags = utils.sceneContainerFlags - private val authenticationRepository = utils.authenticationRepository() - private val authenticationInteractor = - utils.authenticationInteractor( - repository = authenticationRepository, + private val authenticationInteractor = utils.authenticationInteractor() + private val deviceEntryInteractor = + utils.deviceEntryInteractor( + authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, ) - private val keyguardRepository = utils.keyguardRepository - private val keyguardInteractor = - utils.keyguardInteractor( - repository = keyguardRepository, - ) + private val keyguardInteractor = utils.keyguardInteractor() private val sysUiState: SysUiState = mock() private val falsingCollector: FalsingCollector = mock() @@ -77,6 +73,7 @@ class SceneContainerStartableTest : SysuiTestCase() { SceneContainerStartable( applicationScope = testScope.backgroundScope, sceneInteractor = sceneInteractor, + deviceEntryInteractor = deviceEntryInteractor, authenticationInteractor = authenticationInteractor, keyguardInteractor = keyguardInteractor, flags = sceneContainerFlags, @@ -141,7 +138,7 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) underTest.start() - authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) } @@ -157,7 +154,7 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer) underTest.start() - authenticationRepository.setUnlocked(true) + utils.deviceEntryRepository.setUnlocked(true) assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) } @@ -173,7 +170,7 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) underTest.start() - authenticationRepository.setUnlocked(true) + utils.deviceEntryRepository.setUnlocked(true) assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) } @@ -189,7 +186,7 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) underTest.start() - authenticationRepository.setUnlocked(true) + utils.deviceEntryRepository.setUnlocked(true) assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) } @@ -205,7 +202,7 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) underTest.start() - keyguardRepository.setWakefulnessModel(STARTING_TO_SLEEP) + utils.keyguardRepository.setWakefulnessModel(STARTING_TO_SLEEP) assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) } @@ -251,7 +248,7 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) underTest.start() - keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON) + utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON) assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) } @@ -267,7 +264,7 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) underTest.start() - keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON) + utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON) assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) } @@ -283,7 +280,7 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) underTest.start() - keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON) + utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON) assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) } @@ -300,9 +297,9 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) underTest.start() - authenticationRepository.setUnlocked(true) + utils.deviceEntryRepository.setUnlocked(true) runCurrent() - keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON) + utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON) assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) } @@ -389,11 +386,11 @@ class SceneContainerStartableTest : SysuiTestCase() { runCurrent() verify(falsingCollector).setShowingAod(false) - keyguardRepository.setIsDozing(true) + utils.keyguardRepository.setIsDozing(true) runCurrent() verify(falsingCollector).setShowingAod(true) - keyguardRepository.setIsDozing(false) + utils.keyguardRepository.setIsDozing(false) runCurrent() verify(falsingCollector, times(2)).setShowingAod(false) } @@ -401,7 +398,7 @@ class SceneContainerStartableTest : SysuiTestCase() { @Test fun collectFalsingSignals_screenOnAndOff_aodUnavailable() = testScope.runTest { - keyguardRepository.setAodAvailable(false) + utils.keyguardRepository.setAodAvailable(false) runCurrent() prepareState( initialSceneKey = SceneKey.Lockscreen, @@ -414,31 +411,31 @@ class SceneContainerStartableTest : SysuiTestCase() { verify(falsingCollector, never()).onScreenOnFromTouch() verify(falsingCollector, never()).onScreenOff() - keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON) + utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON) runCurrent() verify(falsingCollector, times(1)).onScreenTurningOn() verify(falsingCollector, never()).onScreenOnFromTouch() verify(falsingCollector, never()).onScreenOff() - keyguardRepository.setWakefulnessModel(ASLEEP) + utils.keyguardRepository.setWakefulnessModel(ASLEEP) runCurrent() verify(falsingCollector, times(1)).onScreenTurningOn() verify(falsingCollector, never()).onScreenOnFromTouch() verify(falsingCollector, times(1)).onScreenOff() - keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_TAP) + utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_TAP) runCurrent() verify(falsingCollector, times(1)).onScreenTurningOn() verify(falsingCollector, times(1)).onScreenOnFromTouch() verify(falsingCollector, times(1)).onScreenOff() - keyguardRepository.setWakefulnessModel(ASLEEP) + utils.keyguardRepository.setWakefulnessModel(ASLEEP) runCurrent() verify(falsingCollector, times(1)).onScreenTurningOn() verify(falsingCollector, times(1)).onScreenOnFromTouch() verify(falsingCollector, times(2)).onScreenOff() - keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON) + utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON) runCurrent() verify(falsingCollector, times(2)).onScreenTurningOn() verify(falsingCollector, times(1)).onScreenOnFromTouch() @@ -448,7 +445,7 @@ class SceneContainerStartableTest : SysuiTestCase() { @Test fun collectFalsingSignals_screenOnAndOff_aodAvailable() = testScope.runTest { - keyguardRepository.setAodAvailable(true) + utils.keyguardRepository.setAodAvailable(true) runCurrent() prepareState( initialSceneKey = SceneKey.Lockscreen, @@ -461,31 +458,31 @@ class SceneContainerStartableTest : SysuiTestCase() { verify(falsingCollector, never()).onScreenOnFromTouch() verify(falsingCollector, never()).onScreenOff() - keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON) + utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON) runCurrent() verify(falsingCollector, never()).onScreenTurningOn() verify(falsingCollector, never()).onScreenOnFromTouch() verify(falsingCollector, never()).onScreenOff() - keyguardRepository.setWakefulnessModel(ASLEEP) + utils.keyguardRepository.setWakefulnessModel(ASLEEP) runCurrent() verify(falsingCollector, never()).onScreenTurningOn() verify(falsingCollector, never()).onScreenOnFromTouch() verify(falsingCollector, never()).onScreenOff() - keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_TAP) + utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_TAP) runCurrent() verify(falsingCollector, never()).onScreenTurningOn() verify(falsingCollector, never()).onScreenOnFromTouch() verify(falsingCollector, never()).onScreenOff() - keyguardRepository.setWakefulnessModel(ASLEEP) + utils.keyguardRepository.setWakefulnessModel(ASLEEP) runCurrent() verify(falsingCollector, never()).onScreenTurningOn() verify(falsingCollector, never()).onScreenOnFromTouch() verify(falsingCollector, never()).onScreenOff() - keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON) + utils.keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON) runCurrent() verify(falsingCollector, never()).onScreenTurningOn() verify(falsingCollector, never()).onScreenOnFromTouch() @@ -521,8 +518,8 @@ class SceneContainerStartableTest : SysuiTestCase() { ): MutableStateFlow<ObservableTransitionState> { assumeTrue(Flags.SCENE_CONTAINER_ENABLED) sceneContainerFlags.enabled = true - authenticationRepository.setUnlocked(isDeviceUnlocked) - keyguardRepository.setBypassEnabled(isBypassEnabled) + utils.deviceEntryRepository.setUnlocked(isDeviceUnlocked) + utils.deviceEntryRepository.setBypassEnabled(isBypassEnabled) val transitionStateFlow = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Idle(SceneKey.Lockscreen) @@ -534,8 +531,10 @@ class SceneContainerStartableTest : SysuiTestCase() { sceneInteractor.onSceneChanged(SceneModel(it), "reason") } authenticationMethod?.let { - authenticationRepository.setAuthenticationMethod(authenticationMethod.toDataLayer()) - authenticationRepository.setLockscreenEnabled( + utils.authenticationRepository.setAuthenticationMethod( + authenticationMethod.toDataLayer() + ) + utils.deviceEntryRepository.setInsecureLockscreenEnabled( authenticationMethod != AuthenticationMethodModel.None ) } 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 5c75d9cff10a..602bd5fded3d 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 @@ -47,9 +47,10 @@ class ShadeSceneViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope private val sceneInteractor = utils.sceneInteractor() - private val authenticationInteractor = - utils.authenticationInteractor( - repository = utils.authenticationRepository(), + private val authenticationInteractor = utils.authenticationInteractor() + private val deviceEntryInteractor = + utils.deviceEntryInteractor( + authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, ) @@ -88,9 +89,10 @@ class ShadeSceneViewModelTest : SysuiTestCase() { underTest = ShadeSceneViewModel( applicationScope = testScope.backgroundScope, - authenticationInteractor = authenticationInteractor, + deviceEntryInteractor = deviceEntryInteractor, bouncerInteractor = utils.bouncerInteractor( + deviceEntryInteractor = deviceEntryInteractor, authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, ), @@ -103,7 +105,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { testScope.runTest { val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Lockscreen) } @@ -113,7 +115,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { testScope.runTest { val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setUnlocked(true) + utils.deviceEntryRepository.setUnlocked(true) assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone) } @@ -122,7 +124,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() = testScope.runTest { val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey) - utils.authenticationRepository.setLockscreenEnabled(true) + utils.deviceEntryRepository.setInsecureLockscreenEnabled(true) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen), "reason") sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason") @@ -134,7 +136,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() = testScope.runTest { val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey) - utils.authenticationRepository.setLockscreenEnabled(true) + utils.deviceEntryRepository.setInsecureLockscreenEnabled(true) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason") sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason") @@ -147,7 +149,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { testScope.runTest { val currentScene by collectLastValue(sceneInteractor.desiredScene) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setUnlocked(true) + utils.deviceEntryRepository.setUnlocked(true) runCurrent() underTest.onContentClicked() @@ -160,7 +162,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { testScope.runTest { val currentScene by collectLastValue(sceneInteractor.desiredScene) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setUnlocked(false) + utils.deviceEntryRepository.setUnlocked(false) runCurrent() underTest.onContentClicked() 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 f2e45281da93..4fc3e3f66e6d 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 @@ -24,20 +24,19 @@ import com.android.systemui.authentication.data.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate import com.android.systemui.authentication.shared.model.AuthenticationResultModel import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel +import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow class FakeAuthenticationRepository( + private val deviceEntryRepository: FakeDeviceEntryRepository, private val currentTime: () -> Long, ) : AuthenticationRepository { private val _isAutoConfirmEnabled = MutableStateFlow(false) override val isAutoConfirmEnabled: StateFlow<Boolean> = _isAutoConfirmEnabled.asStateFlow() - private val _isUnlocked = MutableStateFlow(false) - override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow() - override val hintedPinLength: Int = HINTING_PIN_LENGTH private val _isPatternVisible = MutableStateFlow(true) @@ -53,7 +52,6 @@ class FakeAuthenticationRepository( override val minPatternLength: Int = 4 - private var isLockscreenEnabled = true private var failedAttemptCount = 0 private var throttlingEndTimestamp = 0L private var credentialOverride: List<Any>? = null @@ -72,13 +70,9 @@ class FakeAuthenticationRepository( credentialOverride = pin } - override suspend fun isLockscreenEnabled(): Boolean { - return isLockscreenEnabled - } - override suspend fun reportAuthenticationAttempt(isSuccessful: Boolean) { failedAttemptCount = if (isSuccessful) 0 else failedAttemptCount + 1 - _isUnlocked.value = isSuccessful + deviceEntryRepository.setUnlocked(isSuccessful) } override suspend fun getPinLength(): Int { @@ -97,18 +91,10 @@ class FakeAuthenticationRepository( _throttling.value = throttlingModel } - fun setUnlocked(isUnlocked: Boolean) { - _isUnlocked.value = isUnlocked - } - fun setAutoConfirmEnabled(isEnabled: Boolean) { _isAutoConfirmEnabled.value = isEnabled } - fun setLockscreenEnabled(isLockscreenEnabled: Boolean) { - this.isLockscreenEnabled = isLockscreenEnabled - } - override suspend fun setThrottleDuration(durationMs: Int) { throttlingEndTimestamp = if (durationMs > 0) currentTime() + durationMs else 0 } 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 new file mode 100644 index 000000000000..5e60a09e006b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt @@ -0,0 +1,35 @@ +package com.android.systemui.deviceentry.data.repository + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** Fake implementation of [DeviceEntryRepository] */ +class FakeDeviceEntryRepository : DeviceEntryRepository { + + private var isInsecureLockscreenEnabled = true + private var isBypassEnabled = false + + private val _isUnlocked = MutableStateFlow(false) + override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow() + + override fun isBypassEnabled(): Boolean { + return isBypassEnabled + } + + override suspend fun isInsecureLockscreenEnabled(): Boolean { + return isInsecureLockscreenEnabled + } + + fun setUnlocked(isUnlocked: Boolean) { + _isUnlocked.value = isUnlocked + } + + fun setInsecureLockscreenEnabled(isLockscreenEnabled: Boolean) { + this.isInsecureLockscreenEnabled = isLockscreenEnabled + } + + fun setBypassEnabled(isBypassEnabled: Boolean) { + this.isBypassEnabled = isBypassEnabled + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt index a5f5d52ef56c..aa52609d6d47 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt @@ -63,9 +63,6 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { private val _isKeyguardShowing = MutableStateFlow(false) override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing - private val _isKeyguardUnlocked = MutableStateFlow(false) - override val isKeyguardUnlocked: StateFlow<Boolean> = _isKeyguardUnlocked.asStateFlow() - private val _isKeyguardOccluded = MutableStateFlow(false) override val isKeyguardOccluded: Flow<Boolean> = _isKeyguardOccluded @@ -150,11 +147,6 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { return _isKeyguardShowing.value } - private var _isBypassEnabled = false - override fun isBypassEnabled(): Boolean { - return _isBypassEnabled - } - override fun setAnimateDozingTransitions(animate: Boolean) { _animateBottomAreaDozingTransitions.tryEmit(animate) } @@ -252,14 +244,6 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { _statusBarState.value = state } - fun setKeyguardUnlocked(isUnlocked: Boolean) { - _isKeyguardUnlocked.value = isUnlocked - } - - fun setBypassEnabled(isEnabled: Boolean) { - _isBypassEnabled = isEnabled - } - fun setScreenModel(screenModel: ScreenModel) { _screenModel.value = screenModel } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt index 2e3bb2b545d8..1cae09b794b8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.domain.interactor import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository +import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeCommandQueue @@ -42,6 +43,7 @@ object KeyguardInteractorFactory { sceneContainerFlags: SceneContainerFlags = FakeSceneContainerFlags(), repository: FakeKeyguardRepository = FakeKeyguardRepository(), commandQueue: FakeCommandQueue = FakeCommandQueue(), + deviceEntryRepository: FakeDeviceEntryRepository = FakeDeviceEntryRepository(), bouncerRepository: FakeKeyguardBouncerRepository = FakeKeyguardBouncerRepository(), configurationRepository: FakeConfigurationRepository = FakeConfigurationRepository(), shadeRepository: FakeShadeRepository = FakeShadeRepository(), @@ -52,6 +54,7 @@ object KeyguardInteractorFactory { commandQueue = commandQueue, featureFlags = featureFlags, sceneContainerFlags = sceneContainerFlags, + deviceEntryRepository = deviceEntryRepository, bouncerRepository = bouncerRepository, configurationRepository = configurationRepository, shadeRepository = shadeRepository, @@ -60,6 +63,7 @@ object KeyguardInteractorFactory { commandQueue = commandQueue, featureFlags = featureFlags, sceneContainerFlags = sceneContainerFlags, + deviceEntryRepository = deviceEntryRepository, bouncerRepository = bouncerRepository, configurationRepository = configurationRepository, shadeRepository = shadeRepository, @@ -69,7 +73,7 @@ object KeyguardInteractorFactory { } /** Provide defaults, otherwise tests will throw an error */ - fun createFakeFeatureFlags(): FakeFeatureFlags { + private fun createFakeFeatureFlags(): FakeFeatureFlags { return FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) } } @@ -78,6 +82,7 @@ object KeyguardInteractorFactory { val commandQueue: FakeCommandQueue, val featureFlags: FakeFeatureFlags, val sceneContainerFlags: SceneContainerFlags, + val deviceEntryRepository: FakeDeviceEntryRepository, val bouncerRepository: FakeKeyguardBouncerRepository, val configurationRepository: FakeConfigurationRepository, val shadeRepository: FakeShadeRepository, 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 69c89e8f4af6..179206ff87b1 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 @@ -34,6 +34,9 @@ import com.android.systemui.common.ui.data.repository.FakeConfigurationRepositor import com.android.systemui.communal.data.repository.FakeCommunalRepository import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository import com.android.systemui.communal.domain.interactor.CommunalInteractor +import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository +import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeCommandQueue @@ -73,16 +76,10 @@ class SceneTestUtils( val testScope = TestScope(testDispatcher) val featureFlags = FakeFeatureFlagsClassic().apply { set(Flags.FACE_AUTH_REFACTOR, false) } val sceneContainerFlags = FakeSceneContainerFlags().apply { enabled = true } - private val userRepository: UserRepository by lazy { - FakeUserRepository().apply { - val users = listOf(UserInfo(/* id= */ 0, "name", /* flags= */ 0)) - setUserInfos(users) - runBlocking { setSelectedUserInfo(users.first()) } - } - } - + val deviceEntryRepository: FakeDeviceEntryRepository by lazy { FakeDeviceEntryRepository() } val authenticationRepository: FakeAuthenticationRepository by lazy { FakeAuthenticationRepository( + deviceEntryRepository = deviceEntryRepository, currentTime = { testScope.currentTime }, ) } @@ -103,6 +100,14 @@ class SceneTestUtils( } val powerRepository: FakePowerRepository by lazy { FakePowerRepository() } + private val userRepository: UserRepository by lazy { + FakeUserRepository().apply { + val users = listOf(UserInfo(/* id= */ 0, "name", /* flags= */ 0)) + setUserInfos(users) + runBlocking { setSelectedUserInfo(users.first()) } + } + } + private val context = test.context private val falsingCollectorFake: FalsingCollector by lazy { FalsingCollectorFake() } @@ -145,31 +150,41 @@ class SceneTestUtils( ) } - fun authenticationRepository(): FakeAuthenticationRepository { - return authenticationRepository + fun deviceEntryInteractor( + repository: DeviceEntryRepository = deviceEntryRepository, + authenticationInteractor: AuthenticationInteractor, + sceneInteractor: SceneInteractor, + ): DeviceEntryInteractor { + return DeviceEntryInteractor( + applicationScope = applicationScope(), + repository = repository, + authenticationInteractor = authenticationInteractor, + sceneInteractor = sceneInteractor, + ) } fun authenticationInteractor( - repository: AuthenticationRepository, - sceneInteractor: SceneInteractor = sceneInteractor(), + repository: AuthenticationRepository = authenticationRepository, ): AuthenticationInteractor { return AuthenticationInteractor( applicationScope = applicationScope(), repository = repository, backgroundDispatcher = testDispatcher, userRepository = userRepository, - keyguardRepository = keyguardRepository, - sceneInteractor = sceneInteractor, + deviceEntryRepository = deviceEntryRepository, clock = mock { whenever(elapsedRealtime()).thenAnswer { testScope.currentTime } } ) } - fun keyguardInteractor(repository: KeyguardRepository): KeyguardInteractor { + fun keyguardInteractor( + repository: KeyguardRepository = keyguardRepository + ): KeyguardInteractor { return KeyguardInteractor( repository = repository, commandQueue = FakeCommandQueue(), featureFlags = featureFlags, sceneContainerFlags = sceneContainerFlags, + deviceEntryRepository = FakeDeviceEntryRepository(), bouncerRepository = FakeKeyguardBouncerRepository(), configurationRepository = FakeConfigurationRepository(), shadeRepository = FakeShadeRepository(), @@ -185,6 +200,7 @@ class SceneTestUtils( } fun bouncerInteractor( + deviceEntryInteractor: DeviceEntryInteractor, authenticationInteractor: AuthenticationInteractor, sceneInteractor: SceneInteractor, ): BouncerInteractor { @@ -192,6 +208,7 @@ class SceneTestUtils( applicationScope = applicationScope(), applicationContext = context, repository = BouncerRepository(), + deviceEntryInteractor = deviceEntryInteractor, authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, flags = sceneContainerFlags, |