diff options
author | 2023-09-29 12:33:20 +0000 | |
---|---|---|
committer | 2023-09-29 12:33:20 +0000 | |
commit | 102064b4ab6c4e04ad099b39f9ce71e2fe1b1ab7 (patch) | |
tree | 959d697f5aa1a71c8e171db3794f16bc66bf36cf | |
parent | 683becc63dc76780dc956a5a2dfb3f36b8b77f47 (diff) | |
parent | af001916d68de16cd7ea5f2013240f40197020bc (diff) |
Merge "[flexiglass] Add a DeviceEntry module." into main
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, |