Loading packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +14 −17 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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() { Loading packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt +2 −31 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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. * Loading Loading @@ -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) Loading Loading @@ -167,6 +147,7 @@ interface AuthenticationRepository { suspend fun checkCredential(credential: LockscreenCredential): AuthenticationResultModel } @SysUISingleton class AuthenticationRepositoryImpl @Inject constructor( Loading @@ -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, Loading packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt +22 −104 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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, ) { /** Loading @@ -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 Loading Loading @@ -211,31 +149,14 @@ 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. * Loading Loading @@ -312,7 +233,7 @@ constructor( /** Starts refreshing the throttling state every second. */ private suspend fun startThrottlingCountdown() { cancelCountdown() cancelThrottlingCountdown() throttlingCountdownJob = applicationScope.launch { while (refreshThrottling() > 0) { Loading @@ -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() } Loading Loading @@ -378,7 +299,7 @@ constructor( DomainLayerAuthenticationMethodModel { return when (this) { is DataLayerAuthenticationMethodModel.None -> if (repository.isLockscreenEnabled()) { if (deviceEntryRepository.isInsecureLockscreenEnabled()) { DomainLayerAuthenticationMethodModel.Swipe } else { DomainLayerAuthenticationMethodModel.None Loading @@ -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, } packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +3 −1 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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, Loading Loading @@ -144,7 +146,7 @@ constructor( message: String? = null, ) { applicationScope.launch { if (authenticationInteractor.isAuthenticationRequired()) { if (deviceEntryInteractor.isAuthenticationRequired()) { repository.setMessage( message ?: promptMessage(authenticationInteractor.getAuthenticationMethod()) ) Loading packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +2 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -173,6 +174,7 @@ import javax.inject.Named; ControlsModule.class, CoroutinesModule.class, DemoModeModule.class, DeviceEntryModule.class, DisableFlagsModule.class, DisplayModule.class, DreamModule.class, Loading Loading
packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +14 −17 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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() { Loading
packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt +2 −31 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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. * Loading Loading @@ -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) Loading Loading @@ -167,6 +147,7 @@ interface AuthenticationRepository { suspend fun checkCredential(credential: LockscreenCredential): AuthenticationResultModel } @SysUISingleton class AuthenticationRepositoryImpl @Inject constructor( Loading @@ -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, Loading
packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt +22 −104 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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, ) { /** Loading @@ -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 Loading Loading @@ -211,31 +149,14 @@ 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. * Loading Loading @@ -312,7 +233,7 @@ constructor( /** Starts refreshing the throttling state every second. */ private suspend fun startThrottlingCountdown() { cancelCountdown() cancelThrottlingCountdown() throttlingCountdownJob = applicationScope.launch { while (refreshThrottling() > 0) { Loading @@ -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() } Loading Loading @@ -378,7 +299,7 @@ constructor( DomainLayerAuthenticationMethodModel { return when (this) { is DataLayerAuthenticationMethodModel.None -> if (repository.isLockscreenEnabled()) { if (deviceEntryRepository.isInsecureLockscreenEnabled()) { DomainLayerAuthenticationMethodModel.Swipe } else { DomainLayerAuthenticationMethodModel.None Loading @@ -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, }
packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +3 −1 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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, Loading Loading @@ -144,7 +146,7 @@ constructor( message: String? = null, ) { applicationScope.launch { if (authenticationInteractor.isAuthenticationRequired()) { if (deviceEntryInteractor.isAuthenticationRequired()) { repository.setMessage( message ?: promptMessage(authenticationInteractor.getAuthenticationMethod()) ) Loading
packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +2 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -173,6 +174,7 @@ import javax.inject.Named; ControlsModule.class, CoroutinesModule.class, DemoModeModule.class, DeviceEntryModule.class, DisableFlagsModule.class, DisplayModule.class, DreamModule.class, Loading