Loading packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +42 −1 Original line number Diff line number Diff line Loading @@ -79,17 +79,25 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteracto import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.model.SceneContainerNames; import com.android.systemui.scene.shared.model.SceneKey; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.user.domain.interactor.UserInteractor; import com.android.systemui.util.ViewController; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.settings.GlobalSettings; import java.io.File; import java.util.Optional; import javax.inject.Inject; import javax.inject.Provider; import kotlinx.coroutines.Job; /** Controller for {@link KeyguardSecurityContainer} */ @KeyguardBouncerScope Loading Loading @@ -378,6 +386,10 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard showPrimarySecurityScreen(false); } }; private final UserInteractor mUserInteractor; private final Provider<SceneInteractor> mSceneInteractor; private final Provider<JavaAdapter> mJavaAdapter; @Nullable private Job mSceneTransitionCollectionJob; @Inject public KeyguardSecurityContainerController(KeyguardSecurityContainer view, Loading @@ -402,7 +414,10 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard ViewMediatorCallback viewMediatorCallback, AudioManager audioManager, KeyguardFaceAuthInteractor keyguardFaceAuthInteractor, BouncerMessageInteractor bouncerMessageInteractor BouncerMessageInteractor bouncerMessageInteractor, Provider<JavaAdapter> javaAdapter, UserInteractor userInteractor, Provider<SceneInteractor> sceneInteractor ) { super(view); mLockPatternUtils = lockPatternUtils; Loading @@ -429,6 +444,9 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mAudioManager = audioManager; mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor; mBouncerMessageInteractor = bouncerMessageInteractor; mUserInteractor = userInteractor; mSceneInteractor = sceneInteractor; mJavaAdapter = javaAdapter; } @Override Loading @@ -451,6 +469,24 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mView.setOnKeyListener(mOnKeyListener); showPrimarySecurityScreen(false); if (mFeatureFlags.isEnabled(Flags.SCENE_CONTAINER)) { // When the scene framework transitions from bouncer to gone, we dismiss the keyguard. mSceneTransitionCollectionJob = mJavaAdapter.get().alwaysCollectFlow( mSceneInteractor.get().sceneTransitions(SceneContainerNames.SYSTEM_UI_DEFAULT), sceneTransitionModel -> { if (sceneTransitionModel != null && sceneTransitionModel.getFrom() == SceneKey.Bouncer.INSTANCE && sceneTransitionModel.getTo() == SceneKey.Gone.INSTANCE) { final int selectedUserId = mUserInteractor.getSelectedUserId(); showNextSecurityScreenOrFinish( /* authenticated= */ true, selectedUserId, /* bypassSecondaryLockScreen= */ true, mSecurityModel.getSecurityMode(selectedUserId)); } }); } } @Override Loading @@ -459,6 +495,11 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mConfigurationController.removeCallback(mConfigurationListener); mView.removeMotionEventListener(mGlobalTouchListener); mUserSwitcherController.removeUserSwitchCallback(mUserSwitchCallback); if (mSceneTransitionCollectionJob != null) { mSceneTransitionCollectionJob.cancel(null); mSceneTransitionCollectionJob = null; } } /** */ Loading packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt +181 −38 Original line number Diff line number Diff line Loading @@ -14,25 +14,39 @@ * limitations under the License. */ @file:OptIn(ExperimentalCoroutinesApi::class) package com.android.systemui.authentication.data.repository import com.android.internal.widget.LockPatternChecker import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockscreenCredential import com.android.keyguard.KeyguardSecurityModel import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationResultModel import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel 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.time.SystemClock import dagger.Binds import dagger.Module import java.util.function.Function import javax.inject.Inject import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext Loading @@ -57,11 +71,17 @@ interface AuthenticationRepository { */ val isBypassEnabled: StateFlow<Boolean> /** * Number of consecutively failed authentication attempts. This resets to `0` when * authentication succeeds. */ val failedAuthenticationAttempts: StateFlow<Int> /** Whether the auto confirm feature is enabled for the currently-selected user. */ val isAutoConfirmEnabled: StateFlow<Boolean> /** The length of the PIN for which we should show a hint. */ val hintedPinLength: Int /** Whether the pattern should be visible for the currently-selected user. */ val isPatternVisible: StateFlow<Boolean> /** The current throttling state, as cached via [setThrottling]. */ val throttling: StateFlow<AuthenticationThrottlingModel> /** * Returns the currently-configured authentication method. This determines how the Loading @@ -69,11 +89,48 @@ interface AuthenticationRepository { */ suspend fun getAuthenticationMethod(): AuthenticationMethodModel /** 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 /** See [isBypassEnabled]. */ fun setBypassEnabled(isBypassEnabled: Boolean) /** See [failedAuthenticationAttempts]. */ fun setFailedAuthenticationAttempts(failedAuthenticationAttempts: Int) /** Reports an authentication attempt. */ suspend fun reportAuthenticationAttempt(isSuccessful: Boolean) /** Returns the current number of failed authentication attempts. */ suspend fun getFailedAuthenticationAttemptCount(): Int /** * Returns the timestamp for when the current throttling will end, allowing the user to attempt * authentication again. * * Note that this is in milliseconds and it matches [SystemClock.elapsedRealtime]. */ suspend fun getThrottlingEndTimestamp(): Long /** Sets the cached throttling state, updating the [throttling] flow. */ fun setThrottling(throttlingModel: AuthenticationThrottlingModel) /** * Sets the throttling timeout duration (time during which the user should not be allowed to * attempt authentication). */ suspend fun setThrottleDuration(durationMs: Int) /** * Checks the given [LockscreenCredential] to see if it's correct, returning an * [AuthenticationResultModel] representing what happened. */ suspend fun checkCredential(credential: LockscreenCredential): AuthenticationResultModel } class AuthenticationRepositoryImpl Loading @@ -83,8 +140,8 @@ constructor( private val getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>, @Background private val backgroundDispatcher: CoroutineDispatcher, private val userRepository: UserRepository, private val lockPatternUtils: LockPatternUtils, keyguardRepository: KeyguardRepository, private val lockPatternUtils: LockPatternUtils, ) : AuthenticationRepository { override val isUnlocked: StateFlow<Boolean> = Loading @@ -94,54 +151,140 @@ constructor( initialValue = false, ) override suspend fun isLockscreenEnabled(): Boolean { return withContext(backgroundDispatcher) { val selectedUserId = userRepository.selectedUserId !lockPatternUtils.isLockPatternEnabled(selectedUserId) } } private val _isBypassEnabled = MutableStateFlow(false) override val isBypassEnabled: StateFlow<Boolean> = _isBypassEnabled.asStateFlow() private val _failedAuthenticationAttempts = MutableStateFlow(0) override val failedAuthenticationAttempts: StateFlow<Int> = _failedAuthenticationAttempts.asStateFlow() override val isAutoConfirmEnabled: StateFlow<Boolean> = userRepository.selectedUserInfo .map { it.id } .flatMapLatest { userId -> flow { emit(lockPatternUtils.isAutoPinConfirmEnabled(userId)) } .flowOn(backgroundDispatcher) } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = true, ) override val hintedPinLength: Int = LockPatternUtils.MIN_AUTO_PIN_REQUIREMENT_LENGTH override val isPatternVisible: StateFlow<Boolean> = userRepository.selectedUserInfo .map { it.id } .flatMapLatest { userId -> flow { emit(lockPatternUtils.isVisiblePatternEnabled(userId)) } .flowOn(backgroundDispatcher) } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = true, ) private val _throttling = MutableStateFlow(AuthenticationThrottlingModel()) override val throttling: StateFlow<AuthenticationThrottlingModel> = _throttling.asStateFlow() private val UserRepository.selectedUserId: Int get() = getSelectedUserInfo().id override suspend fun getAuthenticationMethod(): AuthenticationMethodModel { return withContext(backgroundDispatcher) { val selectedUserId = userRepository.getSelectedUserInfo().id val selectedUserId = userRepository.selectedUserId when (getSecurityMode.apply(selectedUserId)) { KeyguardSecurityModel.SecurityMode.PIN, KeyguardSecurityModel.SecurityMode.SimPin -> AuthenticationMethodModel.Pin( code = listOf(1, 2, 3, 4), // TODO(b/280883900): remove this autoConfirm = lockPatternUtils.isAutoPinConfirmEnabled(selectedUserId), ) KeyguardSecurityModel.SecurityMode.Password, KeyguardSecurityModel.SecurityMode.SimPuk -> AuthenticationMethodModel.Password( password = "password", // TODO(b/280883900): remove this ) KeyguardSecurityModel.SecurityMode.Pattern -> AuthenticationMethodModel.Pattern( coordinates = listOf( AuthenticationMethodModel.Pattern.PatternCoordinate(2, 0), AuthenticationMethodModel.Pattern.PatternCoordinate(2, 1), AuthenticationMethodModel.Pattern.PatternCoordinate(2, 2), AuthenticationMethodModel.Pattern.PatternCoordinate(1, 1), AuthenticationMethodModel.Pattern.PatternCoordinate(0, 0), AuthenticationMethodModel.Pattern.PatternCoordinate(0, 1), AuthenticationMethodModel.Pattern.PatternCoordinate(0, 2), ), // TODO(b/280883900): remove this ) KeyguardSecurityModel.SecurityMode.SimPin, KeyguardSecurityModel.SecurityMode.SimPuk -> AuthenticationMethodModel.Pin KeyguardSecurityModel.SecurityMode.Password -> AuthenticationMethodModel.Password KeyguardSecurityModel.SecurityMode.Pattern -> AuthenticationMethodModel.Pattern KeyguardSecurityModel.SecurityMode.None -> AuthenticationMethodModel.None KeyguardSecurityModel.SecurityMode.Invalid -> error("Invalid security mode!") null -> error("Invalid security is null!") } } } override suspend fun getPinLength(): Int { return withContext(backgroundDispatcher) { val selectedUserId = userRepository.selectedUserId lockPatternUtils.getPinLength(selectedUserId) } } override fun setBypassEnabled(isBypassEnabled: Boolean) { _isBypassEnabled.value = isBypassEnabled } override fun setFailedAuthenticationAttempts(failedAuthenticationAttempts: Int) { _failedAuthenticationAttempts.value = failedAuthenticationAttempts override suspend fun reportAuthenticationAttempt(isSuccessful: Boolean) { val selectedUserId = userRepository.selectedUserId withContext(backgroundDispatcher) { if (isSuccessful) { lockPatternUtils.reportSuccessfulPasswordAttempt(selectedUserId) } else { lockPatternUtils.reportFailedPasswordAttempt(selectedUserId) } } } override suspend fun getFailedAuthenticationAttemptCount(): Int { return withContext(backgroundDispatcher) { val selectedUserId = userRepository.selectedUserId lockPatternUtils.getCurrentFailedPasswordAttempts(selectedUserId) } } override suspend fun getThrottlingEndTimestamp(): Long { return withContext(backgroundDispatcher) { val selectedUserId = userRepository.selectedUserId lockPatternUtils.getLockoutAttemptDeadline(selectedUserId) } } override fun setThrottling(throttlingModel: AuthenticationThrottlingModel) { _throttling.value = throttlingModel } override suspend fun setThrottleDuration(durationMs: Int) { withContext(backgroundDispatcher) { lockPatternUtils.setLockoutAttemptDeadline( userRepository.selectedUserId, durationMs, ) } } override suspend fun checkCredential( credential: LockscreenCredential ): AuthenticationResultModel { return suspendCoroutine { continuation -> LockPatternChecker.checkCredential( lockPatternUtils, credential, userRepository.selectedUserId, object : LockPatternChecker.OnCheckCallback { override fun onChecked(matched: Boolean, throttleTimeoutMs: Int) { continuation.resume( AuthenticationResultModel( isSuccessful = matched, throttleDurationMs = throttleTimeoutMs, ) ) } override fun onCancelled() { continuation.resume(AuthenticationResultModel(isSuccessful = false)) } override fun onEarlyMatched() = Unit } ) } } } Loading packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt +165 −63 File changed.Preview size limit exceeded, changes collapsed. Show changes packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt +3 −24 Original line number Diff line number Diff line Loading @@ -16,8 +16,6 @@ package com.android.systemui.authentication.shared.model import androidx.annotation.VisibleForTesting /** Enumerates all known authentication methods. */ sealed class AuthenticationMethodModel( /** Loading @@ -34,30 +32,11 @@ sealed class AuthenticationMethodModel( /** The most basic authentication method. The lock screen can be swiped away when displayed. */ object Swipe : AuthenticationMethodModel(isSecure = false) /** * Authentication method using a PIN. * * In practice, a pin is restricted to 16 decimal digits , see * [android.app.admin.DevicePolicyManager.MAX_PASSWORD_LENGTH] */ data class Pin(val code: List<Int>, val autoConfirm: Boolean) : AuthenticationMethodModel(isSecure = true) { /** Convenience constructor for tests only. */ @VisibleForTesting constructor( code: Long, autoConfirm: Boolean = false ) : this(code.toString(10).map { it - '0' }, autoConfirm) {} } data class Password(val password: String) : AuthenticationMethodModel(isSecure = true) object Pin : AuthenticationMethodModel(isSecure = true) data class Pattern( val coordinates: List<PatternCoordinate>, val isPatternVisible: Boolean = true, ) : AuthenticationMethodModel(isSecure = true) { object Password : AuthenticationMethodModel(isSecure = true) object Pattern : AuthenticationMethodModel(isSecure = true) { data class PatternCoordinate( val x: Int, val y: Int, Loading packages/SystemUI/src/com/android/systemui/bouncer/shared/model/AuthenticationThrottledModel.kt→packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationResultModel.kt +25 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * Copyright 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. Loading @@ -14,17 +14,12 @@ * limitations under the License. */ package com.android.systemui.bouncer.shared.model package com.android.systemui.authentication.shared.model /** * Models application state for when further authentication attempts are being throttled due to too * many consecutive failed authentication attempts. */ data class AuthenticationThrottledModel( /** Total number of failed attempts so far. */ val failedAttemptCount: Int, /** Total amount of time the user has to wait before attempting again. */ val totalDurationSec: Int, /** Remaining amount of time the user has to wait before attempting again. */ val remainingDurationSec: Int, /** Models the result of an authentication attempt. */ data class AuthenticationResultModel( /** Whether authentication was successful. */ val isSuccessful: Boolean = false, /** If [isSuccessful] is `false`, how long the user must wait before trying again. */ val throttleDurationMs: Int = 0, ) Loading
packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +42 −1 Original line number Diff line number Diff line Loading @@ -79,17 +79,25 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteracto import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.model.SceneContainerNames; import com.android.systemui.scene.shared.model.SceneKey; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.user.domain.interactor.UserInteractor; import com.android.systemui.util.ViewController; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.settings.GlobalSettings; import java.io.File; import java.util.Optional; import javax.inject.Inject; import javax.inject.Provider; import kotlinx.coroutines.Job; /** Controller for {@link KeyguardSecurityContainer} */ @KeyguardBouncerScope Loading Loading @@ -378,6 +386,10 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard showPrimarySecurityScreen(false); } }; private final UserInteractor mUserInteractor; private final Provider<SceneInteractor> mSceneInteractor; private final Provider<JavaAdapter> mJavaAdapter; @Nullable private Job mSceneTransitionCollectionJob; @Inject public KeyguardSecurityContainerController(KeyguardSecurityContainer view, Loading @@ -402,7 +414,10 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard ViewMediatorCallback viewMediatorCallback, AudioManager audioManager, KeyguardFaceAuthInteractor keyguardFaceAuthInteractor, BouncerMessageInteractor bouncerMessageInteractor BouncerMessageInteractor bouncerMessageInteractor, Provider<JavaAdapter> javaAdapter, UserInteractor userInteractor, Provider<SceneInteractor> sceneInteractor ) { super(view); mLockPatternUtils = lockPatternUtils; Loading @@ -429,6 +444,9 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mAudioManager = audioManager; mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor; mBouncerMessageInteractor = bouncerMessageInteractor; mUserInteractor = userInteractor; mSceneInteractor = sceneInteractor; mJavaAdapter = javaAdapter; } @Override Loading @@ -451,6 +469,24 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mView.setOnKeyListener(mOnKeyListener); showPrimarySecurityScreen(false); if (mFeatureFlags.isEnabled(Flags.SCENE_CONTAINER)) { // When the scene framework transitions from bouncer to gone, we dismiss the keyguard. mSceneTransitionCollectionJob = mJavaAdapter.get().alwaysCollectFlow( mSceneInteractor.get().sceneTransitions(SceneContainerNames.SYSTEM_UI_DEFAULT), sceneTransitionModel -> { if (sceneTransitionModel != null && sceneTransitionModel.getFrom() == SceneKey.Bouncer.INSTANCE && sceneTransitionModel.getTo() == SceneKey.Gone.INSTANCE) { final int selectedUserId = mUserInteractor.getSelectedUserId(); showNextSecurityScreenOrFinish( /* authenticated= */ true, selectedUserId, /* bypassSecondaryLockScreen= */ true, mSecurityModel.getSecurityMode(selectedUserId)); } }); } } @Override Loading @@ -459,6 +495,11 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mConfigurationController.removeCallback(mConfigurationListener); mView.removeMotionEventListener(mGlobalTouchListener); mUserSwitcherController.removeUserSwitchCallback(mUserSwitchCallback); if (mSceneTransitionCollectionJob != null) { mSceneTransitionCollectionJob.cancel(null); mSceneTransitionCollectionJob = null; } } /** */ Loading
packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt +181 −38 Original line number Diff line number Diff line Loading @@ -14,25 +14,39 @@ * limitations under the License. */ @file:OptIn(ExperimentalCoroutinesApi::class) package com.android.systemui.authentication.data.repository import com.android.internal.widget.LockPatternChecker import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockscreenCredential import com.android.keyguard.KeyguardSecurityModel import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationResultModel import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel 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.time.SystemClock import dagger.Binds import dagger.Module import java.util.function.Function import javax.inject.Inject import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext Loading @@ -57,11 +71,17 @@ interface AuthenticationRepository { */ val isBypassEnabled: StateFlow<Boolean> /** * Number of consecutively failed authentication attempts. This resets to `0` when * authentication succeeds. */ val failedAuthenticationAttempts: StateFlow<Int> /** Whether the auto confirm feature is enabled for the currently-selected user. */ val isAutoConfirmEnabled: StateFlow<Boolean> /** The length of the PIN for which we should show a hint. */ val hintedPinLength: Int /** Whether the pattern should be visible for the currently-selected user. */ val isPatternVisible: StateFlow<Boolean> /** The current throttling state, as cached via [setThrottling]. */ val throttling: StateFlow<AuthenticationThrottlingModel> /** * Returns the currently-configured authentication method. This determines how the Loading @@ -69,11 +89,48 @@ interface AuthenticationRepository { */ suspend fun getAuthenticationMethod(): AuthenticationMethodModel /** 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 /** See [isBypassEnabled]. */ fun setBypassEnabled(isBypassEnabled: Boolean) /** See [failedAuthenticationAttempts]. */ fun setFailedAuthenticationAttempts(failedAuthenticationAttempts: Int) /** Reports an authentication attempt. */ suspend fun reportAuthenticationAttempt(isSuccessful: Boolean) /** Returns the current number of failed authentication attempts. */ suspend fun getFailedAuthenticationAttemptCount(): Int /** * Returns the timestamp for when the current throttling will end, allowing the user to attempt * authentication again. * * Note that this is in milliseconds and it matches [SystemClock.elapsedRealtime]. */ suspend fun getThrottlingEndTimestamp(): Long /** Sets the cached throttling state, updating the [throttling] flow. */ fun setThrottling(throttlingModel: AuthenticationThrottlingModel) /** * Sets the throttling timeout duration (time during which the user should not be allowed to * attempt authentication). */ suspend fun setThrottleDuration(durationMs: Int) /** * Checks the given [LockscreenCredential] to see if it's correct, returning an * [AuthenticationResultModel] representing what happened. */ suspend fun checkCredential(credential: LockscreenCredential): AuthenticationResultModel } class AuthenticationRepositoryImpl Loading @@ -83,8 +140,8 @@ constructor( private val getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>, @Background private val backgroundDispatcher: CoroutineDispatcher, private val userRepository: UserRepository, private val lockPatternUtils: LockPatternUtils, keyguardRepository: KeyguardRepository, private val lockPatternUtils: LockPatternUtils, ) : AuthenticationRepository { override val isUnlocked: StateFlow<Boolean> = Loading @@ -94,54 +151,140 @@ constructor( initialValue = false, ) override suspend fun isLockscreenEnabled(): Boolean { return withContext(backgroundDispatcher) { val selectedUserId = userRepository.selectedUserId !lockPatternUtils.isLockPatternEnabled(selectedUserId) } } private val _isBypassEnabled = MutableStateFlow(false) override val isBypassEnabled: StateFlow<Boolean> = _isBypassEnabled.asStateFlow() private val _failedAuthenticationAttempts = MutableStateFlow(0) override val failedAuthenticationAttempts: StateFlow<Int> = _failedAuthenticationAttempts.asStateFlow() override val isAutoConfirmEnabled: StateFlow<Boolean> = userRepository.selectedUserInfo .map { it.id } .flatMapLatest { userId -> flow { emit(lockPatternUtils.isAutoPinConfirmEnabled(userId)) } .flowOn(backgroundDispatcher) } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = true, ) override val hintedPinLength: Int = LockPatternUtils.MIN_AUTO_PIN_REQUIREMENT_LENGTH override val isPatternVisible: StateFlow<Boolean> = userRepository.selectedUserInfo .map { it.id } .flatMapLatest { userId -> flow { emit(lockPatternUtils.isVisiblePatternEnabled(userId)) } .flowOn(backgroundDispatcher) } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = true, ) private val _throttling = MutableStateFlow(AuthenticationThrottlingModel()) override val throttling: StateFlow<AuthenticationThrottlingModel> = _throttling.asStateFlow() private val UserRepository.selectedUserId: Int get() = getSelectedUserInfo().id override suspend fun getAuthenticationMethod(): AuthenticationMethodModel { return withContext(backgroundDispatcher) { val selectedUserId = userRepository.getSelectedUserInfo().id val selectedUserId = userRepository.selectedUserId when (getSecurityMode.apply(selectedUserId)) { KeyguardSecurityModel.SecurityMode.PIN, KeyguardSecurityModel.SecurityMode.SimPin -> AuthenticationMethodModel.Pin( code = listOf(1, 2, 3, 4), // TODO(b/280883900): remove this autoConfirm = lockPatternUtils.isAutoPinConfirmEnabled(selectedUserId), ) KeyguardSecurityModel.SecurityMode.Password, KeyguardSecurityModel.SecurityMode.SimPuk -> AuthenticationMethodModel.Password( password = "password", // TODO(b/280883900): remove this ) KeyguardSecurityModel.SecurityMode.Pattern -> AuthenticationMethodModel.Pattern( coordinates = listOf( AuthenticationMethodModel.Pattern.PatternCoordinate(2, 0), AuthenticationMethodModel.Pattern.PatternCoordinate(2, 1), AuthenticationMethodModel.Pattern.PatternCoordinate(2, 2), AuthenticationMethodModel.Pattern.PatternCoordinate(1, 1), AuthenticationMethodModel.Pattern.PatternCoordinate(0, 0), AuthenticationMethodModel.Pattern.PatternCoordinate(0, 1), AuthenticationMethodModel.Pattern.PatternCoordinate(0, 2), ), // TODO(b/280883900): remove this ) KeyguardSecurityModel.SecurityMode.SimPin, KeyguardSecurityModel.SecurityMode.SimPuk -> AuthenticationMethodModel.Pin KeyguardSecurityModel.SecurityMode.Password -> AuthenticationMethodModel.Password KeyguardSecurityModel.SecurityMode.Pattern -> AuthenticationMethodModel.Pattern KeyguardSecurityModel.SecurityMode.None -> AuthenticationMethodModel.None KeyguardSecurityModel.SecurityMode.Invalid -> error("Invalid security mode!") null -> error("Invalid security is null!") } } } override suspend fun getPinLength(): Int { return withContext(backgroundDispatcher) { val selectedUserId = userRepository.selectedUserId lockPatternUtils.getPinLength(selectedUserId) } } override fun setBypassEnabled(isBypassEnabled: Boolean) { _isBypassEnabled.value = isBypassEnabled } override fun setFailedAuthenticationAttempts(failedAuthenticationAttempts: Int) { _failedAuthenticationAttempts.value = failedAuthenticationAttempts override suspend fun reportAuthenticationAttempt(isSuccessful: Boolean) { val selectedUserId = userRepository.selectedUserId withContext(backgroundDispatcher) { if (isSuccessful) { lockPatternUtils.reportSuccessfulPasswordAttempt(selectedUserId) } else { lockPatternUtils.reportFailedPasswordAttempt(selectedUserId) } } } override suspend fun getFailedAuthenticationAttemptCount(): Int { return withContext(backgroundDispatcher) { val selectedUserId = userRepository.selectedUserId lockPatternUtils.getCurrentFailedPasswordAttempts(selectedUserId) } } override suspend fun getThrottlingEndTimestamp(): Long { return withContext(backgroundDispatcher) { val selectedUserId = userRepository.selectedUserId lockPatternUtils.getLockoutAttemptDeadline(selectedUserId) } } override fun setThrottling(throttlingModel: AuthenticationThrottlingModel) { _throttling.value = throttlingModel } override suspend fun setThrottleDuration(durationMs: Int) { withContext(backgroundDispatcher) { lockPatternUtils.setLockoutAttemptDeadline( userRepository.selectedUserId, durationMs, ) } } override suspend fun checkCredential( credential: LockscreenCredential ): AuthenticationResultModel { return suspendCoroutine { continuation -> LockPatternChecker.checkCredential( lockPatternUtils, credential, userRepository.selectedUserId, object : LockPatternChecker.OnCheckCallback { override fun onChecked(matched: Boolean, throttleTimeoutMs: Int) { continuation.resume( AuthenticationResultModel( isSuccessful = matched, throttleDurationMs = throttleTimeoutMs, ) ) } override fun onCancelled() { continuation.resume(AuthenticationResultModel(isSuccessful = false)) } override fun onEarlyMatched() = Unit } ) } } } Loading
packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt +165 −63 File changed.Preview size limit exceeded, changes collapsed. Show changes
packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt +3 −24 Original line number Diff line number Diff line Loading @@ -16,8 +16,6 @@ package com.android.systemui.authentication.shared.model import androidx.annotation.VisibleForTesting /** Enumerates all known authentication methods. */ sealed class AuthenticationMethodModel( /** Loading @@ -34,30 +32,11 @@ sealed class AuthenticationMethodModel( /** The most basic authentication method. The lock screen can be swiped away when displayed. */ object Swipe : AuthenticationMethodModel(isSecure = false) /** * Authentication method using a PIN. * * In practice, a pin is restricted to 16 decimal digits , see * [android.app.admin.DevicePolicyManager.MAX_PASSWORD_LENGTH] */ data class Pin(val code: List<Int>, val autoConfirm: Boolean) : AuthenticationMethodModel(isSecure = true) { /** Convenience constructor for tests only. */ @VisibleForTesting constructor( code: Long, autoConfirm: Boolean = false ) : this(code.toString(10).map { it - '0' }, autoConfirm) {} } data class Password(val password: String) : AuthenticationMethodModel(isSecure = true) object Pin : AuthenticationMethodModel(isSecure = true) data class Pattern( val coordinates: List<PatternCoordinate>, val isPatternVisible: Boolean = true, ) : AuthenticationMethodModel(isSecure = true) { object Password : AuthenticationMethodModel(isSecure = true) object Pattern : AuthenticationMethodModel(isSecure = true) { data class PatternCoordinate( val x: Int, val y: Int, Loading
packages/SystemUI/src/com/android/systemui/bouncer/shared/model/AuthenticationThrottledModel.kt→packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationResultModel.kt +25 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * Copyright 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. Loading @@ -14,17 +14,12 @@ * limitations under the License. */ package com.android.systemui.bouncer.shared.model package com.android.systemui.authentication.shared.model /** * Models application state for when further authentication attempts are being throttled due to too * many consecutive failed authentication attempts. */ data class AuthenticationThrottledModel( /** Total number of failed attempts so far. */ val failedAttemptCount: Int, /** Total amount of time the user has to wait before attempting again. */ val totalDurationSec: Int, /** Remaining amount of time the user has to wait before attempting again. */ val remainingDurationSec: Int, /** Models the result of an authentication attempt. */ data class AuthenticationResultModel( /** Whether authentication was successful. */ val isSuccessful: Boolean = false, /** If [isSuccessful] is `false`, how long the user must wait before trying again. */ val throttleDurationMs: Int = 0, )