Move all bouncer messages domain logic to the interactor
Summary of changes:
1. Remove all duplicated state from the repository, we still need one field to save custom messages
2. Migrate all the domain logic to the interactor
3. Fix trust Agent disabled message not being shown, we need to check if trust is usually managed insted of currently managed.
4. Check whether fingerprint auth is enrolled to show the "unlock with fingerprint" message
5. Move the BouncerMessageRepository tests to the interactor
6. If face auth is class3, then whenever face auth is locked out, we should show "Pin is required after too many attempts"
7. Got rid of BouncerMessageFactory as well, having a separate prompt_reason and then mapping it in the factory seemed like an unnecessary step, now the interactor directly creates the model when it was returning the prompt reason.
Fixes: 293477650
Fixes: 293476775
Fixes: 293472698
Fixes: 293478276
Test: atest BouncerMessageInteractorTest
Test: atest TrustRepositoryTest
Change-Id: Ib33fa67c3238d3b219dc10ab26972202b3f7bbee
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
index 21960e2..83b1a2c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
@@ -67,42 +67,6 @@
int PROMPT_REASON_TRUSTAGENT_EXPIRED = 8;
/**
- * Prompt that is shown when there is an incorrect primary authentication input.
- */
- int PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT = 9;
-
- /**
- * Prompt that is shown when there is an incorrect face biometric input.
- */
- int PROMPT_REASON_INCORRECT_FACE_INPUT = 10;
-
- /**
- * Prompt that is shown when there is an incorrect fingerprint biometric input.
- */
- int PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT = 11;
-
- /**
- * Prompt that is shown when face authentication is in locked out state.
- */
- int PROMPT_REASON_FACE_LOCKED_OUT = 12;
-
- /**
- * Prompt that is shown when fingerprint authentication is in locked out state.
- */
- int PROMPT_REASON_FINGERPRINT_LOCKED_OUT = 13;
-
- /**
- * Default prompt that is shown on the bouncer.
- */
- int PROMPT_REASON_DEFAULT = 14;
-
- /**
- * Prompt that is shown when primary authentication is in locked out state after too many
- * attempts
- */
- int PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT = 15;
-
- /**
* Strong auth is required because the device has just booted because of an automatic
* mainline update.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt
deleted file mode 100644
index f93337c..0000000
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- * Copyright (C) 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bouncer.data.factory
-
-import android.annotation.IntDef
-import com.android.keyguard.KeyguardSecurityModel
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FACE_LOCKED_OUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FINGERPRINT_LOCKED_OUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FACE_INPUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST
-import com.android.systemui.res.R.string.bouncer_face_not_recognized
-import com.android.systemui.res.R.string.keyguard_enter_password
-import com.android.systemui.res.R.string.keyguard_enter_pattern
-import com.android.systemui.res.R.string.keyguard_enter_pin
-import com.android.systemui.res.R.string.kg_bio_too_many_attempts_password
-import com.android.systemui.res.R.string.kg_bio_too_many_attempts_pattern
-import com.android.systemui.res.R.string.kg_bio_too_many_attempts_pin
-import com.android.systemui.res.R.string.kg_bio_try_again_or_password
-import com.android.systemui.res.R.string.kg_bio_try_again_or_pattern
-import com.android.systemui.res.R.string.kg_bio_try_again_or_pin
-import com.android.systemui.res.R.string.kg_face_locked_out
-import com.android.systemui.res.R.string.kg_fp_locked_out
-import com.android.systemui.res.R.string.kg_fp_not_recognized
-import com.android.systemui.res.R.string.kg_primary_auth_locked_out_password
-import com.android.systemui.res.R.string.kg_primary_auth_locked_out_pattern
-import com.android.systemui.res.R.string.kg_primary_auth_locked_out_pin
-import com.android.systemui.res.R.string.kg_prompt_after_dpm_lock
-import com.android.systemui.res.R.string.kg_prompt_after_update_password
-import com.android.systemui.res.R.string.kg_prompt_after_update_pattern
-import com.android.systemui.res.R.string.kg_prompt_after_update_pin
-import com.android.systemui.res.R.string.kg_prompt_after_user_lockdown_password
-import com.android.systemui.res.R.string.kg_prompt_after_user_lockdown_pattern
-import com.android.systemui.res.R.string.kg_prompt_after_user_lockdown_pin
-import com.android.systemui.res.R.string.kg_prompt_auth_timeout
-import com.android.systemui.res.R.string.kg_prompt_password_auth_timeout
-import com.android.systemui.res.R.string.kg_prompt_pattern_auth_timeout
-import com.android.systemui.res.R.string.kg_prompt_pin_auth_timeout
-import com.android.systemui.res.R.string.kg_prompt_reason_restart_password
-import com.android.systemui.res.R.string.kg_prompt_reason_restart_pattern
-import com.android.systemui.res.R.string.kg_prompt_reason_restart_pin
-import com.android.systemui.res.R.string.kg_prompt_unattended_update
-import com.android.systemui.res.R.string.kg_too_many_failed_attempts_countdown
-import com.android.systemui.res.R.string.kg_trust_agent_disabled
-import com.android.systemui.res.R.string.kg_unlock_with_password_or_fp
-import com.android.systemui.res.R.string.kg_unlock_with_pattern_or_fp
-import com.android.systemui.res.R.string.kg_unlock_with_pin_or_fp
-import com.android.systemui.res.R.string.kg_wrong_input_try_fp_suggestion
-import com.android.systemui.res.R.string.kg_wrong_password_try_again
-import com.android.systemui.res.R.string.kg_wrong_pattern_try_again
-import com.android.systemui.res.R.string.kg_wrong_pin_try_again
-import com.android.systemui.bouncer.shared.model.BouncerMessageModel
-import com.android.systemui.bouncer.shared.model.Message
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import javax.inject.Inject
-
-@SysUISingleton
-class BouncerMessageFactory
-@Inject
-constructor(
- private val biometricSettingsRepository: BiometricSettingsRepository,
- private val securityModel: KeyguardSecurityModel,
-) {
-
- fun createFromPromptReason(
- @BouncerPromptReason reason: Int,
- userId: Int,
- secondaryMsgOverride: String? = null
- ): BouncerMessageModel? {
- val pair =
- getBouncerMessage(
- reason,
- securityModel.getSecurityMode(userId),
- biometricSettingsRepository.isFingerprintAuthCurrentlyAllowed.value
- )
- return pair?.let {
- BouncerMessageModel(
- message = Message(messageResId = pair.first, animate = false),
- secondaryMessage =
- secondaryMsgOverride?.let {
- Message(message = secondaryMsgOverride, animate = false)
- }
- ?: Message(messageResId = pair.second, animate = false)
- )
- }
- }
-
- /**
- * Helper method that provides the relevant bouncer message that should be shown for different
- * scenarios indicated by [reason]. [securityMode] & [fpAuthIsAllowed] parameters are used to
- * provide a more specific message.
- */
- private fun getBouncerMessage(
- @BouncerPromptReason reason: Int,
- securityMode: SecurityMode,
- fpAuthIsAllowed: Boolean = false
- ): Pair<Int, Int>? {
- return when (reason) {
- // Primary auth locked out
- PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT -> primaryAuthLockedOut(securityMode)
- // Primary auth required reasons
- PROMPT_REASON_RESTART -> authRequiredAfterReboot(securityMode)
- PROMPT_REASON_TIMEOUT -> authRequiredAfterPrimaryAuthTimeout(securityMode)
- PROMPT_REASON_DEVICE_ADMIN -> authRequiredAfterAdminLockdown(securityMode)
- PROMPT_REASON_USER_REQUEST -> authRequiredAfterUserLockdown(securityMode)
- PROMPT_REASON_PREPARE_FOR_UPDATE -> authRequiredForUnattendedUpdate(securityMode)
- PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE -> authRequiredForMainlineUpdate(securityMode)
- PROMPT_REASON_FINGERPRINT_LOCKED_OUT -> fingerprintUnlockUnavailable(securityMode)
- PROMPT_REASON_AFTER_LOCKOUT -> biometricLockout(securityMode)
- // Non strong auth not available reasons
- PROMPT_REASON_FACE_LOCKED_OUT ->
- if (fpAuthIsAllowed) faceLockedOutButFingerprintAvailable(securityMode)
- else faceLockedOut(securityMode)
- PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT ->
- if (fpAuthIsAllowed) nonStrongAuthTimeoutWithFingerprintAllowed(securityMode)
- else nonStrongAuthTimeout(securityMode)
- PROMPT_REASON_TRUSTAGENT_EXPIRED ->
- if (fpAuthIsAllowed) trustAgentDisabledWithFingerprintAllowed(securityMode)
- else trustAgentDisabled(securityMode)
- // Auth incorrect input reasons.
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT ->
- if (fpAuthIsAllowed) incorrectSecurityInputWithFingerprint(securityMode)
- else incorrectSecurityInput(securityMode)
- PROMPT_REASON_INCORRECT_FACE_INPUT ->
- if (fpAuthIsAllowed) incorrectFaceInputWithFingerprintAllowed(securityMode)
- else incorrectFaceInput(securityMode)
- PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT -> incorrectFingerprintInput(securityMode)
- // Default message
- PROMPT_REASON_DEFAULT ->
- if (fpAuthIsAllowed) defaultMessageWithFingerprint(securityMode)
- else defaultMessage(securityMode)
- else -> null
- }
- }
-
- fun emptyMessage(): BouncerMessageModel =
- BouncerMessageModel(Message(message = ""), Message(message = ""))
-}
-
-@Retention(AnnotationRetention.SOURCE)
-@IntDef(
- PROMPT_REASON_TIMEOUT,
- PROMPT_REASON_DEVICE_ADMIN,
- PROMPT_REASON_USER_REQUEST,
- PROMPT_REASON_AFTER_LOCKOUT,
- PROMPT_REASON_PREPARE_FOR_UPDATE,
- PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT,
- PROMPT_REASON_TRUSTAGENT_EXPIRED,
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
- PROMPT_REASON_INCORRECT_FACE_INPUT,
- PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT,
- PROMPT_REASON_FACE_LOCKED_OUT,
- PROMPT_REASON_FINGERPRINT_LOCKED_OUT,
- PROMPT_REASON_DEFAULT,
- PROMPT_REASON_NONE,
- PROMPT_REASON_RESTART,
- PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT,
- PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE,
-)
-annotation class BouncerPromptReason
-
-private fun defaultMessage(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0)
- SecurityMode.Password -> Pair(keyguard_enter_password, 0)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, 0)
- else -> Pair(0, 0)
- }
-}
-
-private fun defaultMessageWithFingerprint(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, 0)
- SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, 0)
- SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, 0)
- else -> Pair(0, 0)
- }
-}
-
-private fun incorrectSecurityInput(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, 0)
- SecurityMode.Password -> Pair(kg_wrong_password_try_again, 0)
- SecurityMode.PIN -> Pair(kg_wrong_pin_try_again, 0)
- else -> Pair(0, 0)
- }
-}
-
-private fun incorrectSecurityInputWithFingerprint(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, kg_wrong_input_try_fp_suggestion)
- SecurityMode.Password -> Pair(kg_wrong_password_try_again, kg_wrong_input_try_fp_suggestion)
- SecurityMode.PIN -> Pair(kg_wrong_pin_try_again, kg_wrong_input_try_fp_suggestion)
- else -> Pair(0, 0)
- }
-}
-
-private fun incorrectFingerprintInput(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_pattern)
- SecurityMode.Password -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_password)
- SecurityMode.PIN -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_pin)
- else -> Pair(0, 0)
- }
-}
-
-private fun incorrectFaceInput(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_pattern)
- SecurityMode.Password -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_password)
- SecurityMode.PIN -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_pin)
- else -> Pair(0, 0)
- }
-}
-
-private fun incorrectFaceInputWithFingerprintAllowed(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, bouncer_face_not_recognized)
- SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, bouncer_face_not_recognized)
- SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, bouncer_face_not_recognized)
- else -> Pair(0, 0)
- }
-}
-
-private fun biometricLockout(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_bio_too_many_attempts_pattern)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_bio_too_many_attempts_password)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_bio_too_many_attempts_pin)
- else -> Pair(0, 0)
- }
-}
-
-private fun authRequiredAfterReboot(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_reason_restart_pattern)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_reason_restart_password)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin)
- else -> Pair(0, 0)
- }
-}
-
-private fun authRequiredAfterAdminLockdown(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_dpm_lock)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_after_dpm_lock)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock)
- else -> Pair(0, 0)
- }
-}
-
-private fun authRequiredAfterUserLockdown(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_user_lockdown_pattern)
- SecurityMode.Password ->
- Pair(keyguard_enter_password, kg_prompt_after_user_lockdown_password)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin)
- else -> Pair(0, 0)
- }
-}
-
-private fun authRequiredForUnattendedUpdate(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_unattended_update)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_unattended_update)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_unattended_update)
- else -> Pair(0, 0)
- }
-}
-
-private fun authRequiredForMainlineUpdate(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_update_pattern)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_after_update_password)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_update_pin)
- else -> Pair(0, 0)
- }
-}
-
-private fun authRequiredAfterPrimaryAuthTimeout(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_pattern_auth_timeout)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_password_auth_timeout)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout)
- else -> Pair(0, 0)
- }
-}
-
-private fun nonStrongAuthTimeout(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_auth_timeout)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_auth_timeout)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_auth_timeout)
- else -> Pair(0, 0)
- }
-}
-
-private fun nonStrongAuthTimeoutWithFingerprintAllowed(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_prompt_auth_timeout)
- SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_prompt_auth_timeout)
- SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_prompt_auth_timeout)
- else -> Pair(0, 0)
- }
-}
-
-private fun faceLockedOut(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_face_locked_out)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_face_locked_out)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_face_locked_out)
- else -> Pair(0, 0)
- }
-}
-
-private fun faceLockedOutButFingerprintAvailable(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_face_locked_out)
- SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_face_locked_out)
- SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_face_locked_out)
- else -> Pair(0, 0)
- }
-}
-
-private fun fingerprintUnlockUnavailable(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_fp_locked_out)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_fp_locked_out)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_fp_locked_out)
- else -> Pair(0, 0)
- }
-}
-
-private fun trustAgentDisabled(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_trust_agent_disabled)
- SecurityMode.Password -> Pair(keyguard_enter_password, kg_trust_agent_disabled)
- SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_trust_agent_disabled)
- else -> Pair(0, 0)
- }
-}
-
-private fun trustAgentDisabledWithFingerprintAllowed(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_trust_agent_disabled)
- SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_trust_agent_disabled)
- SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_trust_agent_disabled)
- else -> Pair(0, 0)
- }
-}
-
-private fun primaryAuthLockedOut(securityMode: SecurityMode): Pair<Int, Int> {
- return when (securityMode) {
- SecurityMode.Pattern ->
- Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_pattern)
- SecurityMode.Password ->
- Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_password)
- SecurityMode.PIN ->
- Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_pin)
- else -> Pair(0, 0)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt
index 97c1bdb..094dc0a 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt
@@ -16,315 +16,26 @@
package com.android.systemui.bouncer.data.repository
-import android.hardware.biometrics.BiometricSourceType
-import android.hardware.biometrics.BiometricSourceType.FACE
-import android.hardware.biometrics.BiometricSourceType.FINGERPRINT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FACE_LOCKED_OUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FINGERPRINT_LOCKED_OUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FACE_INPUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
import com.android.systemui.bouncer.shared.model.BouncerMessageModel
-import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.SystemPropertiesHelper
-import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.data.repository.TrustRepository
-import com.android.systemui.user.data.repository.UserRepository
import javax.inject.Inject
-import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
/** Provide different sources of messages that needs to be shown on the bouncer. */
interface BouncerMessageRepository {
- /**
- * Messages that are shown in response to the incorrect security attempts on the bouncer and
- * primary authentication method being locked out, along with countdown messages before primary
- * auth is active again.
- */
- val primaryAuthMessage: Flow<BouncerMessageModel?>
+ val bouncerMessage: Flow<BouncerMessageModel>
- /**
- * Help messages that are shown to the user on how to successfully perform authentication using
- * face.
- */
- val faceAcquisitionMessage: Flow<BouncerMessageModel?>
-
- /**
- * Help messages that are shown to the user on how to successfully perform authentication using
- * fingerprint.
- */
- val fingerprintAcquisitionMessage: Flow<BouncerMessageModel?>
-
- /** Custom message that is displayed when the bouncer is being shown to launch an app. */
- val customMessage: Flow<BouncerMessageModel?>
-
- /**
- * Messages that are shown in response to biometric authentication attempts through face or
- * fingerprint.
- */
- val biometricAuthMessage: Flow<BouncerMessageModel?>
-
- /** Messages that are shown when certain auth flags are set. */
- val authFlagsMessage: Flow<BouncerMessageModel?>
-
- /** Messages that are show after biometrics are locked out temporarily or permanently */
- val biometricLockedOutMessage: Flow<BouncerMessageModel?>
-
- /** Set the value for [primaryAuthMessage] */
- fun setPrimaryAuthMessage(value: BouncerMessageModel?)
-
- /** Set the value for [faceAcquisitionMessage] */
- fun setFaceAcquisitionMessage(value: BouncerMessageModel?)
- /** Set the value for [fingerprintAcquisitionMessage] */
- fun setFingerprintAcquisitionMessage(value: BouncerMessageModel?)
-
- /** Set the value for [customMessage] */
- fun setCustomMessage(value: BouncerMessageModel?)
-
- /**
- * Clear any previously set messages for [primaryAuthMessage], [faceAcquisitionMessage],
- * [fingerprintAcquisitionMessage] & [customMessage]
- */
- fun clearMessage()
+ fun setMessage(message: BouncerMessageModel)
}
-private const val SYS_BOOT_REASON_PROP = "sys.boot.reason.last"
-private const val REBOOT_MAINLINE_UPDATE = "reboot,mainline_update"
-
@SysUISingleton
-class BouncerMessageRepositoryImpl
-@Inject
-constructor(
- trustRepository: TrustRepository,
- biometricSettingsRepository: BiometricSettingsRepository,
- updateMonitor: KeyguardUpdateMonitor,
- private val bouncerMessageFactory: BouncerMessageFactory,
- private val userRepository: UserRepository,
- private val systemPropertiesHelper: SystemPropertiesHelper,
- fingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
-) : BouncerMessageRepository {
+class BouncerMessageRepositoryImpl @Inject constructor() : BouncerMessageRepository {
- private val isAnyBiometricsEnabledAndEnrolled =
- or(
- biometricSettingsRepository.isFaceAuthEnrolledAndEnabled,
- biometricSettingsRepository.isFingerprintEnrolledAndEnabled,
- )
+ private val _bouncerMessage = MutableStateFlow(BouncerMessageModel())
+ override val bouncerMessage: Flow<BouncerMessageModel> = _bouncerMessage
- private val wasRebootedForMainlineUpdate
- get() = systemPropertiesHelper.get(SYS_BOOT_REASON_PROP) == REBOOT_MAINLINE_UPDATE
-
- private val authFlagsBasedPromptReason: Flow<Int> =
- combine(
- biometricSettingsRepository.authenticationFlags,
- trustRepository.isCurrentUserTrustManaged,
- isAnyBiometricsEnabledAndEnrolled,
- ::Triple
- )
- .map { (flags, isTrustManaged, biometricsEnrolledAndEnabled) ->
- val trustOrBiometricsAvailable = (isTrustManaged || biometricsEnrolledAndEnabled)
- return@map if (
- trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterReboot
- ) {
- if (wasRebootedForMainlineUpdate) PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE
- else PROMPT_REASON_RESTART
- } else if (trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterTimeout) {
- PROMPT_REASON_TIMEOUT
- } else if (flags.isPrimaryAuthRequiredAfterDpmLockdown) {
- PROMPT_REASON_DEVICE_ADMIN
- } else if (isTrustManaged && flags.someAuthRequiredAfterUserRequest) {
- PROMPT_REASON_TRUSTAGENT_EXPIRED
- } else if (isTrustManaged && flags.someAuthRequiredAfterTrustAgentExpired) {
- PROMPT_REASON_TRUSTAGENT_EXPIRED
- } else if (trustOrBiometricsAvailable && flags.isInUserLockdown) {
- PROMPT_REASON_USER_REQUEST
- } else if (
- trustOrBiometricsAvailable && flags.primaryAuthRequiredForUnattendedUpdate
- ) {
- PROMPT_REASON_PREPARE_FOR_UPDATE
- } else if (
- trustOrBiometricsAvailable &&
- flags.strongerAuthRequiredAfterNonStrongBiometricsTimeout
- ) {
- PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT
- } else {
- PROMPT_REASON_NONE
- }
- }
-
- private val biometricAuthReason: Flow<Int> =
- conflatedCallbackFlow {
- val callback =
- object : KeyguardUpdateMonitorCallback() {
- override fun onBiometricAuthFailed(
- biometricSourceType: BiometricSourceType?
- ) {
- val promptReason =
- if (biometricSourceType == FINGERPRINT)
- PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT
- else if (
- biometricSourceType == FACE && !updateMonitor.isFaceLockedOut
- ) {
- PROMPT_REASON_INCORRECT_FACE_INPUT
- } else PROMPT_REASON_NONE
- trySendWithFailureLogging(promptReason, TAG, "onBiometricAuthFailed")
- }
-
- override fun onBiometricsCleared() {
- trySendWithFailureLogging(
- PROMPT_REASON_NONE,
- TAG,
- "onBiometricsCleared"
- )
- }
-
- override fun onBiometricAcquired(
- biometricSourceType: BiometricSourceType?,
- acquireInfo: Int
- ) {
- trySendWithFailureLogging(
- PROMPT_REASON_NONE,
- TAG,
- "clearBiometricPrompt for new auth session."
- )
- }
-
- override fun onBiometricAuthenticated(
- userId: Int,
- biometricSourceType: BiometricSourceType?,
- isStrongBiometric: Boolean
- ) {
- trySendWithFailureLogging(
- PROMPT_REASON_NONE,
- TAG,
- "onBiometricAuthenticated"
- )
- }
- }
- updateMonitor.registerCallback(callback)
- awaitClose { updateMonitor.removeCallback(callback) }
- }
- .distinctUntilChanged()
-
- private val _primaryAuthMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val primaryAuthMessage: Flow<BouncerMessageModel?> = _primaryAuthMessage
-
- private val _faceAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val faceAcquisitionMessage: Flow<BouncerMessageModel?> = _faceAcquisitionMessage
-
- private val _fingerprintAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val fingerprintAcquisitionMessage: Flow<BouncerMessageModel?> =
- _fingerprintAcquisitionMessage
-
- private val _customMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val customMessage: Flow<BouncerMessageModel?> = _customMessage
-
- override val biometricAuthMessage: Flow<BouncerMessageModel?> =
- biometricAuthReason
- .map {
- if (it == PROMPT_REASON_NONE) null
- else
- bouncerMessageFactory.createFromPromptReason(
- it,
- userRepository.getSelectedUserInfo().id
- )
- }
- .onStart { emit(null) }
- .distinctUntilChanged()
-
- override val authFlagsMessage: Flow<BouncerMessageModel?> =
- authFlagsBasedPromptReason
- .map {
- if (it == PROMPT_REASON_NONE) null
- else
- bouncerMessageFactory.createFromPromptReason(
- it,
- userRepository.getSelectedUserInfo().id
- )
- }
- .onStart { emit(null) }
- .distinctUntilChanged()
-
- // TODO (b/262838215): Replace with DeviceEntryFaceAuthRepository when the new face auth system
- // has been launched.
- private val faceLockedOut: Flow<Boolean> = conflatedCallbackFlow {
- val callback =
- object : KeyguardUpdateMonitorCallback() {
- override fun onLockedOutStateChanged(biometricSourceType: BiometricSourceType?) {
- if (biometricSourceType == FACE) {
- trySendWithFailureLogging(
- updateMonitor.isFaceLockedOut,
- TAG,
- "face lock out state changed."
- )
- }
- }
- }
- updateMonitor.registerCallback(callback)
- trySendWithFailureLogging(updateMonitor.isFaceLockedOut, TAG, "face lockout initial value")
- awaitClose { updateMonitor.removeCallback(callback) }
- }
-
- override val biometricLockedOutMessage: Flow<BouncerMessageModel?> =
- combine(fingerprintAuthRepository.isLockedOut, faceLockedOut) { fp, face ->
- return@combine if (fp) {
- bouncerMessageFactory.createFromPromptReason(
- PROMPT_REASON_FINGERPRINT_LOCKED_OUT,
- userRepository.getSelectedUserInfo().id
- )
- } else if (face) {
- bouncerMessageFactory.createFromPromptReason(
- PROMPT_REASON_FACE_LOCKED_OUT,
- userRepository.getSelectedUserInfo().id
- )
- } else null
- }
-
- override fun setPrimaryAuthMessage(value: BouncerMessageModel?) {
- _primaryAuthMessage.value = value
- }
-
- override fun setFaceAcquisitionMessage(value: BouncerMessageModel?) {
- _faceAcquisitionMessage.value = value
- }
-
- override fun setFingerprintAcquisitionMessage(value: BouncerMessageModel?) {
- _fingerprintAcquisitionMessage.value = value
- }
-
- override fun setCustomMessage(value: BouncerMessageModel?) {
- _customMessage.value = value
- }
-
- override fun clearMessage() {
- _fingerprintAcquisitionMessage.value = null
- _faceAcquisitionMessage.value = null
- _primaryAuthMessage.value = null
- _customMessage.value = null
- }
-
- companion object {
- const val TAG = "BouncerDetailedMessageRepository"
+ override fun setMessage(message: BouncerMessageModel) {
+ _bouncerMessage.value = message
}
}
-
-private fun or(flow: Flow<Boolean>, anotherFlow: Flow<Boolean>) =
- flow.combine(anotherFlow) { a, b -> a || b }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt
index 497747f..aecfe1d 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt
@@ -16,16 +16,13 @@
package com.android.systemui.bouncer.domain.interactor
-import android.os.Build
import android.util.Log
import com.android.systemui.CoreStartable
import com.android.systemui.bouncer.data.repository.BouncerMessageRepository
-import com.android.systemui.bouncer.shared.model.BouncerMessageModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
private val TAG = BouncerMessageAuditLogger::class.simpleName!!
@@ -37,24 +34,8 @@
constructor(
@Application private val scope: CoroutineScope,
private val repository: BouncerMessageRepository,
- private val interactor: BouncerMessageInteractor,
) : CoreStartable {
override fun start() {
- if (Build.isDebuggable()) {
- collectAndLog(repository.biometricAuthMessage, "biometricMessage: ")
- collectAndLog(repository.primaryAuthMessage, "primaryAuthMessage: ")
- collectAndLog(repository.customMessage, "customMessage: ")
- collectAndLog(repository.faceAcquisitionMessage, "faceAcquisitionMessage: ")
- collectAndLog(
- repository.fingerprintAcquisitionMessage,
- "fingerprintAcquisitionMessage: "
- )
- collectAndLog(repository.authFlagsMessage, "authFlagsMessage: ")
- collectAndLog(interactor.bouncerMessage, "interactor.bouncerMessage: ")
- }
- }
-
- private fun collectAndLog(flow: Flow<BouncerMessageModel?>, context: String) {
- scope.launch { flow.collect { Log.d(TAG, context + it) } }
+ scope.launch { repository.bouncerMessage.collect { Log.d(TAG, "bouncerMessage: $it") } }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
index fe01d08..f612f9a 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
@@ -16,55 +16,234 @@
package com.android.systemui.bouncer.domain.interactor
+import android.hardware.biometrics.BiometricSourceType
import android.os.CountDownTimer
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT
-import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.biometrics.data.repository.FacePropertyRepository
+import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.bouncer.data.repository.BouncerMessageRepository
import com.android.systemui.bouncer.shared.model.BouncerMessageModel
+import com.android.systemui.bouncer.shared.model.Message
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES
+import com.android.systemui.flags.SystemPropertiesHelper
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.TrustRepository
+import com.android.systemui.res.R.string.bouncer_face_not_recognized
+import com.android.systemui.res.R.string.keyguard_enter_password
+import com.android.systemui.res.R.string.keyguard_enter_pattern
+import com.android.systemui.res.R.string.keyguard_enter_pin
+import com.android.systemui.res.R.string.kg_bio_too_many_attempts_password
+import com.android.systemui.res.R.string.kg_bio_too_many_attempts_pattern
+import com.android.systemui.res.R.string.kg_bio_too_many_attempts_pin
+import com.android.systemui.res.R.string.kg_bio_try_again_or_password
+import com.android.systemui.res.R.string.kg_bio_try_again_or_pattern
+import com.android.systemui.res.R.string.kg_bio_try_again_or_pin
+import com.android.systemui.res.R.string.kg_face_locked_out
+import com.android.systemui.res.R.string.kg_fp_not_recognized
+import com.android.systemui.res.R.string.kg_primary_auth_locked_out_password
+import com.android.systemui.res.R.string.kg_primary_auth_locked_out_pattern
+import com.android.systemui.res.R.string.kg_primary_auth_locked_out_pin
+import com.android.systemui.res.R.string.kg_prompt_after_dpm_lock
+import com.android.systemui.res.R.string.kg_prompt_after_update_password
+import com.android.systemui.res.R.string.kg_prompt_after_update_pattern
+import com.android.systemui.res.R.string.kg_prompt_after_update_pin
+import com.android.systemui.res.R.string.kg_prompt_after_user_lockdown_password
+import com.android.systemui.res.R.string.kg_prompt_after_user_lockdown_pattern
+import com.android.systemui.res.R.string.kg_prompt_after_user_lockdown_pin
+import com.android.systemui.res.R.string.kg_prompt_auth_timeout
+import com.android.systemui.res.R.string.kg_prompt_password_auth_timeout
+import com.android.systemui.res.R.string.kg_prompt_pattern_auth_timeout
+import com.android.systemui.res.R.string.kg_prompt_pin_auth_timeout
+import com.android.systemui.res.R.string.kg_prompt_reason_restart_password
+import com.android.systemui.res.R.string.kg_prompt_reason_restart_pattern
+import com.android.systemui.res.R.string.kg_prompt_reason_restart_pin
+import com.android.systemui.res.R.string.kg_prompt_unattended_update
+import com.android.systemui.res.R.string.kg_too_many_failed_attempts_countdown
+import com.android.systemui.res.R.string.kg_trust_agent_disabled
+import com.android.systemui.res.R.string.kg_unlock_with_password_or_fp
+import com.android.systemui.res.R.string.kg_unlock_with_pattern_or_fp
+import com.android.systemui.res.R.string.kg_unlock_with_pin_or_fp
+import com.android.systemui.res.R.string.kg_wrong_input_try_fp_suggestion
+import com.android.systemui.res.R.string.kg_wrong_password_try_again
+import com.android.systemui.res.R.string.kg_wrong_pattern_try_again
+import com.android.systemui.res.R.string.kg_wrong_pin_try_again
import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.kotlin.Quint
import javax.inject.Inject
import kotlin.math.roundToInt
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
+
+private const val SYS_BOOT_REASON_PROP = "sys.boot.reason.last"
+private const val REBOOT_MAINLINE_UPDATE = "reboot,mainline_update"
+private const val TAG = "BouncerMessageInteractor"
@SysUISingleton
class BouncerMessageInteractor
@Inject
constructor(
private val repository: BouncerMessageRepository,
- private val factory: BouncerMessageFactory,
private val userRepository: UserRepository,
private val countDownTimerUtil: CountDownTimerUtil,
private val featureFlags: FeatureFlags,
+ private val updateMonitor: KeyguardUpdateMonitor,
+ trustRepository: TrustRepository,
+ biometricSettingsRepository: BiometricSettingsRepository,
+ private val systemPropertiesHelper: SystemPropertiesHelper,
+ primaryBouncerInteractor: PrimaryBouncerInteractor,
+ @Application private val applicationScope: CoroutineScope,
+ private val facePropertyRepository: FacePropertyRepository,
+ deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
+ faceAuthRepository: DeviceEntryFaceAuthRepository,
+ private val securityModel: KeyguardSecurityModel,
) {
+
+ private val isFingerprintAuthCurrentlyAllowed =
+ deviceEntryFingerprintAuthRepository.isLockedOut
+ .isFalse()
+ .and(biometricSettingsRepository.isFingerprintAuthCurrentlyAllowed)
+ .stateIn(applicationScope, SharingStarted.Eagerly, false)
+
+ private val currentSecurityMode
+ get() = securityModel.getSecurityMode(currentUserId)
+ private val currentUserId
+ get() = userRepository.getSelectedUserInfo().id
+
+ private val kumCallback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType?) {
+ repository.setMessage(
+ when (biometricSourceType) {
+ BiometricSourceType.FINGERPRINT ->
+ incorrectFingerprintInput(currentSecurityMode)
+ BiometricSourceType.FACE ->
+ incorrectFaceInput(
+ currentSecurityMode,
+ isFingerprintAuthCurrentlyAllowed.value
+ )
+ else ->
+ defaultMessage(
+ currentSecurityMode,
+ isFingerprintAuthCurrentlyAllowed.value
+ )
+ }
+ )
+ }
+
+ override fun onBiometricAcquired(
+ biometricSourceType: BiometricSourceType?,
+ acquireInfo: Int
+ ) {
+ super.onBiometricAcquired(biometricSourceType, acquireInfo)
+ }
+
+ override fun onBiometricAuthenticated(
+ userId: Int,
+ biometricSourceType: BiometricSourceType?,
+ isStrongBiometric: Boolean
+ ) {
+ repository.setMessage(defaultMessage)
+ }
+ }
+
+ private val isAnyBiometricsEnabledAndEnrolled =
+ biometricSettingsRepository.isFaceAuthEnrolledAndEnabled.or(
+ biometricSettingsRepository.isFingerprintEnrolledAndEnabled
+ )
+
+ private val wasRebootedForMainlineUpdate
+ get() = systemPropertiesHelper.get(SYS_BOOT_REASON_PROP) == REBOOT_MAINLINE_UPDATE
+
+ private val isFaceAuthClass3
+ get() = facePropertyRepository.sensorInfo.value?.strength == SensorStrength.STRONG
+
+ private val initialBouncerMessage: Flow<BouncerMessageModel> =
+ combine(
+ biometricSettingsRepository.authenticationFlags,
+ trustRepository.isCurrentUserTrustManaged,
+ isAnyBiometricsEnabledAndEnrolled,
+ deviceEntryFingerprintAuthRepository.isLockedOut,
+ faceAuthRepository.isLockedOut,
+ ::Quint
+ )
+ .map { (flags, _, biometricsEnrolledAndEnabled, fpLockedOut, faceLockedOut) ->
+ val isTrustUsuallyManaged = trustRepository.isCurrentUserTrustUsuallyManaged.value
+ val trustOrBiometricsAvailable =
+ (isTrustUsuallyManaged || biometricsEnrolledAndEnabled)
+ return@map if (
+ trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterReboot
+ ) {
+ if (wasRebootedForMainlineUpdate) {
+ authRequiredForMainlineUpdate(currentSecurityMode)
+ } else {
+ authRequiredAfterReboot(currentSecurityMode)
+ }
+ } else if (trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterTimeout) {
+ authRequiredAfterPrimaryAuthTimeout(currentSecurityMode)
+ } else if (flags.isPrimaryAuthRequiredAfterDpmLockdown) {
+ authRequiredAfterAdminLockdown(currentSecurityMode)
+ } else if (
+ trustOrBiometricsAvailable && flags.primaryAuthRequiredForUnattendedUpdate
+ ) {
+ authRequiredForUnattendedUpdate(currentSecurityMode)
+ } else if (fpLockedOut) {
+ class3AuthLockedOut(currentSecurityMode)
+ } else if (faceLockedOut) {
+ if (isFaceAuthClass3) {
+ class3AuthLockedOut(currentSecurityMode)
+ } else {
+ faceLockedOut(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value)
+ }
+ } else if (
+ trustOrBiometricsAvailable &&
+ flags.strongerAuthRequiredAfterNonStrongBiometricsTimeout
+ ) {
+ nonStrongAuthTimeout(
+ currentSecurityMode,
+ isFingerprintAuthCurrentlyAllowed.value
+ )
+ } else if (isTrustUsuallyManaged && flags.someAuthRequiredAfterUserRequest) {
+ trustAgentDisabled(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value)
+ } else if (isTrustUsuallyManaged && flags.someAuthRequiredAfterTrustAgentExpired) {
+ trustAgentDisabled(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value)
+ } else if (trustOrBiometricsAvailable && flags.isInUserLockdown) {
+ authRequiredAfterUserLockdown(currentSecurityMode)
+ } else {
+ defaultMessage
+ }
+ }
+
fun onPrimaryAuthLockedOut(secondsBeforeLockoutReset: Long) {
if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
val callback =
object : CountDownTimerCallback {
override fun onFinish() {
- repository.clearMessage()
+ repository.setMessage(defaultMessage)
}
override fun onTick(millisUntilFinished: Long) {
val secondsRemaining = (millisUntilFinished / 1000.0).roundToInt()
- val message =
- factory.createFromPromptReason(
- reason = PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT,
- userId = userRepository.getSelectedUserInfo().id
- )
- message?.message?.animate = false
- message?.message?.formatterArgs =
+ val message = primaryAuthLockedOut(currentSecurityMode)
+ message.message?.animate = false
+ message.message?.formatterArgs =
mutableMapOf<String, Any>(Pair("count", secondsRemaining))
- repository.setPrimaryAuthMessage(message)
+ repository.setMessage(message)
}
}
countDownTimerUtil.startNewTimer(secondsBeforeLockoutReset * 1000, 1000, callback)
@@ -73,104 +252,58 @@
fun onPrimaryAuthIncorrectAttempt() {
if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
- repository.setPrimaryAuthMessage(
- factory.createFromPromptReason(
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
- userRepository.getSelectedUserInfo().id
- )
+ repository.setMessage(
+ incorrectSecurityInput(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value)
)
}
fun setFingerprintAcquisitionMessage(value: String?) {
if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
-
- repository.setFingerprintAcquisitionMessage(
- if (value != null) {
- factory.createFromPromptReason(
- PROMPT_REASON_DEFAULT,
- userRepository.getSelectedUserInfo().id,
- secondaryMsgOverride = value
- )
- } else {
- null
- }
+ repository.setMessage(
+ defaultMessage(currentSecurityMode, value, isFingerprintAuthCurrentlyAllowed.value)
)
}
fun setFaceAcquisitionMessage(value: String?) {
if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
-
- repository.setFaceAcquisitionMessage(
- if (value != null) {
- factory.createFromPromptReason(
- PROMPT_REASON_DEFAULT,
- userRepository.getSelectedUserInfo().id,
- secondaryMsgOverride = value
- )
- } else {
- null
- }
+ repository.setMessage(
+ defaultMessage(currentSecurityMode, value, isFingerprintAuthCurrentlyAllowed.value)
)
}
fun setCustomMessage(value: String?) {
if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
- repository.setCustomMessage(
- if (value != null) {
- factory.createFromPromptReason(
- PROMPT_REASON_DEFAULT,
- userRepository.getSelectedUserInfo().id,
- secondaryMsgOverride = value
- )
- } else {
- null
- }
+ repository.setMessage(
+ defaultMessage(currentSecurityMode, value, isFingerprintAuthCurrentlyAllowed.value)
)
}
+ private val defaultMessage: BouncerMessageModel
+ get() = defaultMessage(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value)
+
fun onPrimaryBouncerUserInput() {
if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
-
- repository.clearMessage()
+ repository.setMessage(defaultMessage)
}
- fun onBouncerBeingHidden() {
- if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+ val bouncerMessage = repository.bouncerMessage
- repository.clearMessage()
+ init {
+ updateMonitor.registerCallback(kumCallback)
+
+ combine(primaryBouncerInteractor.isShowing, initialBouncerMessage) { showing, bouncerMessage
+ ->
+ if (showing) {
+ bouncerMessage
+ } else {
+ null
+ }
+ }
+ .filterNotNull()
+ .onEach { repository.setMessage(it) }
+ .launchIn(applicationScope)
}
-
- private fun firstNonNullMessage(
- oneMessageModel: Flow<BouncerMessageModel?>,
- anotherMessageModel: Flow<BouncerMessageModel?>
- ): Flow<BouncerMessageModel?> {
- return oneMessageModel.combine(anotherMessageModel) { a, b -> a ?: b }
- }
-
- // Null if feature flag is enabled which gets ignored always or empty bouncer message model that
- // always maps to an empty string.
- private fun nullOrEmptyMessage() =
- flowOf(
- if (featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) null else factory.emptyMessage()
- )
-
- val bouncerMessage =
- listOf(
- nullOrEmptyMessage(),
- repository.primaryAuthMessage,
- repository.biometricAuthMessage,
- repository.fingerprintAcquisitionMessage,
- repository.faceAcquisitionMessage,
- repository.customMessage,
- repository.authFlagsMessage,
- repository.biometricLockedOutMessage,
- userRepository.selectedUserInfo.map {
- factory.createFromPromptReason(PROMPT_REASON_DEFAULT, it.id)
- },
- )
- .reduce(::firstNonNullMessage)
- .distinctUntilChanged()
}
interface CountDownTimerCallback {
@@ -199,3 +332,272 @@
.start()
}
}
+
+private fun Flow<Boolean>.or(anotherFlow: Flow<Boolean>) =
+ this.combine(anotherFlow) { a, b -> a || b }
+
+private fun Flow<Boolean>.and(anotherFlow: Flow<Boolean>) =
+ this.combine(anotherFlow) { a, b -> a && b }
+
+private fun Flow<Boolean>.isFalse() = this.map { !it }
+
+private fun defaultMessage(
+ securityMode: SecurityMode,
+ secondaryMessage: String?,
+ fpAuthIsAllowed: Boolean
+): BouncerMessageModel {
+ return BouncerMessageModel(
+ message =
+ Message(
+ messageResId = defaultMessage(securityMode, fpAuthIsAllowed).message?.messageResId,
+ animate = false
+ ),
+ secondaryMessage = Message(message = secondaryMessage, animate = false)
+ )
+}
+
+private fun defaultMessage(
+ securityMode: SecurityMode,
+ fpAuthIsAllowed: Boolean
+): BouncerMessageModel {
+ return if (fpAuthIsAllowed) {
+ defaultMessageWithFingerprint(securityMode)
+ } else
+ when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0)
+ SecurityMode.Password -> Pair(keyguard_enter_password, 0)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, 0)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun defaultMessageWithFingerprint(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, 0)
+ SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, 0)
+ SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, 0)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun incorrectSecurityInput(
+ securityMode: SecurityMode,
+ fpAuthIsAllowed: Boolean
+): BouncerMessageModel {
+ return if (fpAuthIsAllowed) {
+ incorrectSecurityInputWithFingerprint(securityMode)
+ } else
+ when (securityMode) {
+ SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, 0)
+ SecurityMode.Password -> Pair(kg_wrong_password_try_again, 0)
+ SecurityMode.PIN -> Pair(kg_wrong_pin_try_again, 0)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun incorrectSecurityInputWithFingerprint(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, kg_wrong_input_try_fp_suggestion)
+ SecurityMode.Password -> Pair(kg_wrong_password_try_again, kg_wrong_input_try_fp_suggestion)
+ SecurityMode.PIN -> Pair(kg_wrong_pin_try_again, kg_wrong_input_try_fp_suggestion)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun incorrectFingerprintInput(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_pattern)
+ SecurityMode.Password -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_password)
+ SecurityMode.PIN -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_pin)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun incorrectFaceInput(
+ securityMode: SecurityMode,
+ fpAuthIsAllowed: Boolean
+): BouncerMessageModel {
+ return if (fpAuthIsAllowed) incorrectFaceInputWithFingerprintAllowed(securityMode)
+ else
+ when (securityMode) {
+ SecurityMode.Pattern -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_pattern)
+ SecurityMode.Password -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_password)
+ SecurityMode.PIN -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_pin)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun incorrectFaceInputWithFingerprintAllowed(
+ securityMode: SecurityMode
+): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, bouncer_face_not_recognized)
+ SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, bouncer_face_not_recognized)
+ SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, bouncer_face_not_recognized)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun biometricLockout(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_bio_too_many_attempts_pattern)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_bio_too_many_attempts_password)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_bio_too_many_attempts_pin)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun authRequiredAfterReboot(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_reason_restart_pattern)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_reason_restart_password)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun authRequiredAfterAdminLockdown(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_dpm_lock)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_after_dpm_lock)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun authRequiredAfterUserLockdown(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_user_lockdown_pattern)
+ SecurityMode.Password ->
+ Pair(keyguard_enter_password, kg_prompt_after_user_lockdown_password)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun authRequiredForUnattendedUpdate(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_unattended_update)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_unattended_update)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_unattended_update)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun authRequiredForMainlineUpdate(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_update_pattern)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_after_update_password)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_update_pin)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun authRequiredAfterPrimaryAuthTimeout(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_pattern_auth_timeout)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_password_auth_timeout)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun nonStrongAuthTimeout(
+ securityMode: SecurityMode,
+ fpAuthIsAllowed: Boolean
+): BouncerMessageModel {
+ return if (fpAuthIsAllowed) {
+ nonStrongAuthTimeoutWithFingerprintAllowed(securityMode)
+ } else
+ when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_auth_timeout)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_auth_timeout)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_auth_timeout)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+fun nonStrongAuthTimeoutWithFingerprintAllowed(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_prompt_auth_timeout)
+ SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_prompt_auth_timeout)
+ SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_prompt_auth_timeout)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun faceLockedOut(
+ securityMode: SecurityMode,
+ fpAuthIsAllowed: Boolean
+): BouncerMessageModel {
+ return if (fpAuthIsAllowed) faceLockedOutButFingerprintAvailable(securityMode)
+ else
+ when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_face_locked_out)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_face_locked_out)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_face_locked_out)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun faceLockedOutButFingerprintAvailable(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_face_locked_out)
+ SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_face_locked_out)
+ SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_face_locked_out)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun class3AuthLockedOut(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_bio_too_many_attempts_pattern)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_bio_too_many_attempts_password)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_bio_too_many_attempts_pin)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun trustAgentDisabled(
+ securityMode: SecurityMode,
+ fpAuthIsAllowed: Boolean
+): BouncerMessageModel {
+ return if (fpAuthIsAllowed) trustAgentDisabledWithFingerprintAllowed(securityMode)
+ else
+ when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_trust_agent_disabled)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_trust_agent_disabled)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_trust_agent_disabled)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun trustAgentDisabledWithFingerprintAllowed(
+ securityMode: SecurityMode
+): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_trust_agent_disabled)
+ SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_trust_agent_disabled)
+ SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_trust_agent_disabled)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun primaryAuthLockedOut(securityMode: SecurityMode): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern ->
+ Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_pattern)
+ SecurityMode.Password ->
+ Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_password)
+ SecurityMode.PIN ->
+ Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_pin)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun Pair<Int, Int>.toMessage(): BouncerMessageModel {
+ return BouncerMessageModel(
+ message = Message(messageResId = this.first, animate = false),
+ secondaryMessage = Message(messageResId = this.second, animate = false)
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerMessageModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerMessageModel.kt
index 0e9e962..7b169f4 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerMessageModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerMessageModel.kt
@@ -22,7 +22,10 @@
* Represents the message displayed on the bouncer. It has two parts, primary and a secondary
* message
*/
-data class BouncerMessageModel(val message: Message? = null, val secondaryMessage: Message? = null)
+data class BouncerMessageModel(
+ val message: Message? = null,
+ val secondaryMessage: Message? = null,
+)
/**
* Representation of a single message on the bouncer. It can be either a string or a string resource
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
index e29d6bd..36e5db4 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
@@ -144,7 +144,6 @@
)
}
} else {
- bouncerMessageInteractor.onBouncerBeingHidden()
securityContainerController.onBouncerVisibilityChanged(
/* isVisible= */ false
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt
index 00036ce..6522439 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt
@@ -29,6 +29,7 @@
import com.android.systemui.user.data.repository.UserRepository
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
@@ -36,6 +37,8 @@
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
@@ -44,19 +47,25 @@
/** Encapsulates any state relevant to trust agents and trust grants. */
interface TrustRepository {
+ /** Flow representing whether the current user has enabled any trust agents. */
+ val isCurrentUserTrustUsuallyManaged: StateFlow<Boolean>
+
/** Flow representing whether the current user is trusted. */
val isCurrentUserTrusted: Flow<Boolean>
/** Flow representing whether active unlock is running for the current user. */
val isCurrentUserActiveUnlockRunning: Flow<Boolean>
- /** Reports that whether trust is managed has changed for the current user. */
+ /**
+ * Reports whether a trust agent is currently enabled and managing the trust of the current user
+ */
val isCurrentUserTrustManaged: StateFlow<Boolean>
/** A trust agent is requesting to dismiss the keyguard from a trust change. */
val trustAgentRequestingToDismissKeyguard: Flow<TrustModel>
}
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class TrustRepositoryImpl
@Inject
@@ -174,6 +183,11 @@
}
.map { it!! }
+ override val isCurrentUserTrustUsuallyManaged: StateFlow<Boolean> =
+ userRepository.selectedUserInfo
+ .flatMapLatest { flowOf(trustManager.isTrustUsuallyManaged(it.id)) }
+ .stateIn(applicationScope, started = SharingStarted.Eagerly, false)
+
private fun isUserTrustManaged(userId: Int) =
trustManagedForUser[userId]?.isTrustManaged ?: false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 98e5124..2f1b589 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -84,7 +84,6 @@
import com.android.keyguard.logging.KeyguardLogger;
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
-import com.android.systemui.res.R;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
@@ -103,6 +102,7 @@
import com.android.systemui.log.core.LogLevel;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
@@ -1256,8 +1256,6 @@
if (biometricSourceType == FACE) {
mFaceAcquiredMessageDeferral.reset();
}
- mBouncerMessageInteractor.setFaceAcquisitionMessage(null);
- mBouncerMessageInteractor.setFingerprintAcquisitionMessage(null);
}
@Override
@@ -1278,8 +1276,6 @@
} else if (biometricSourceType == FINGERPRINT) {
onFingerprintAuthError(msgId, errString);
}
- mBouncerMessageInteractor.setFaceAcquisitionMessage(null);
- mBouncerMessageInteractor.setFingerprintAcquisitionMessage(null);
}
private void onFaceAuthError(int msgId, String errString) {
@@ -1351,8 +1347,6 @@
showActionToUnlock();
}
}
- mBouncerMessageInteractor.setFaceAcquisitionMessage(null);
- mBouncerMessageInteractor.setFingerprintAcquisitionMessage(null);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt
deleted file mode 100644
index 8eb274a..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bouncer.data.factory
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.keyguard.KeyguardSecurityModel
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Password
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Pattern
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.shared.model.BouncerMessageModel
-import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.StringSubject
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class BouncerMessageFactoryTest : SysuiTestCase() {
- private lateinit var underTest: BouncerMessageFactory
-
- @Mock private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
-
- @Mock private lateinit var securityModel: KeyguardSecurityModel
-
- private lateinit var testScope: TestScope
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- testScope = TestScope()
- biometricSettingsRepository = FakeBiometricSettingsRepository()
- underTest = BouncerMessageFactory(biometricSettingsRepository, securityModel)
- }
-
- @Test
- fun bouncerMessages_choosesTheRightMessage_basedOnSecurityModeAndFpAuthIsAllowed() =
- testScope.runTest {
- primaryMessage(PROMPT_REASON_DEFAULT, mode = PIN, fpAuthAllowed = false)
- .isEqualTo("Enter PIN")
- primaryMessage(PROMPT_REASON_DEFAULT, mode = PIN, fpAuthAllowed = true)
- .isEqualTo("Unlock with PIN or fingerprint")
-
- primaryMessage(PROMPT_REASON_DEFAULT, mode = Password, fpAuthAllowed = false)
- .isEqualTo("Enter password")
- primaryMessage(PROMPT_REASON_DEFAULT, mode = Password, fpAuthAllowed = true)
- .isEqualTo("Unlock with password or fingerprint")
-
- primaryMessage(PROMPT_REASON_DEFAULT, mode = Pattern, fpAuthAllowed = false)
- .isEqualTo("Draw pattern")
- primaryMessage(PROMPT_REASON_DEFAULT, mode = Pattern, fpAuthAllowed = true)
- .isEqualTo("Unlock with pattern or fingerprint")
- }
-
- @Test
- fun bouncerMessages_overridesSecondaryMessageValue() =
- testScope.runTest {
- val bouncerMessageModel =
- bouncerMessageModel(
- PIN,
- true,
- PROMPT_REASON_DEFAULT,
- secondaryMessageOverride = "face acquisition message"
- )!!
- assertThat(context.resources.getString(bouncerMessageModel.message!!.messageResId!!))
- .isEqualTo("Unlock with PIN or fingerprint")
- assertThat(bouncerMessageModel.secondaryMessage!!.message!!)
- .isEqualTo("face acquisition message")
- }
-
- @Test
- fun bouncerMessages_setsPrimaryAndSecondaryMessage_basedOnSecurityModeAndFpAuthIsAllowed() =
- testScope.runTest {
- primaryMessage(
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
- mode = PIN,
- fpAuthAllowed = true
- )
- .isEqualTo("Wrong PIN. Try again.")
- secondaryMessage(
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
- mode = PIN,
- fpAuthAllowed = true
- )
- .isEqualTo("Or unlock with fingerprint")
-
- primaryMessage(
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
- mode = Password,
- fpAuthAllowed = true
- )
- .isEqualTo("Wrong password. Try again.")
- secondaryMessage(
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
- mode = Password,
- fpAuthAllowed = true
- )
- .isEqualTo("Or unlock with fingerprint")
-
- primaryMessage(
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
- mode = Pattern,
- fpAuthAllowed = true
- )
- .isEqualTo("Wrong pattern. Try again.")
- secondaryMessage(
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
- mode = Pattern,
- fpAuthAllowed = true
- )
- .isEqualTo("Or unlock with fingerprint")
- }
-
- private fun primaryMessage(
- reason: Int,
- mode: KeyguardSecurityModel.SecurityMode,
- fpAuthAllowed: Boolean
- ): StringSubject {
- return assertThat(
- context.resources.getString(
- bouncerMessageModel(mode, fpAuthAllowed, reason)!!.message!!.messageResId!!
- )
- )!!
- }
-
- private fun secondaryMessage(
- reason: Int,
- mode: KeyguardSecurityModel.SecurityMode,
- fpAuthAllowed: Boolean
- ): StringSubject {
- return assertThat(
- context.resources.getString(
- bouncerMessageModel(mode, fpAuthAllowed, reason)!!.secondaryMessage!!.messageResId!!
- )
- )!!
- }
-
- private fun bouncerMessageModel(
- mode: KeyguardSecurityModel.SecurityMode,
- fpAuthAllowed: Boolean,
- reason: Int,
- secondaryMessageOverride: String? = null,
- ): BouncerMessageModel? {
- whenever(securityModel.getSecurityMode(0)).thenReturn(mode)
- biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(fpAuthAllowed)
-
- return underTest.createFromPromptReason(
- reason,
- 0,
- secondaryMsgOverride = secondaryMessageOverride
- )
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt
deleted file mode 100644
index f158b43..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- * Copyright (C) 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bouncer.data.repo
-
-import android.content.pm.UserInfo
-import android.hardware.biometrics.BiometricSourceType
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE
-import com.android.keyguard.KeyguardSecurityModel
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.res.R
-import com.android.systemui.res.R.string.keyguard_enter_pin
-import com.android.systemui.res.R.string.kg_prompt_after_dpm_lock
-import com.android.systemui.res.R.string.kg_prompt_after_user_lockdown_pin
-import com.android.systemui.res.R.string.kg_prompt_auth_timeout
-import com.android.systemui.res.R.string.kg_prompt_pin_auth_timeout
-import com.android.systemui.res.R.string.kg_prompt_reason_restart_pin
-import com.android.systemui.res.R.string.kg_prompt_unattended_update
-import com.android.systemui.res.R.string.kg_trust_agent_disabled
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
-import com.android.systemui.bouncer.data.repository.BouncerMessageRepository
-import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl
-import com.android.systemui.bouncer.shared.model.BouncerMessageModel
-import com.android.systemui.bouncer.shared.model.Message
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.SystemPropertiesHelper
-import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.shared.model.AuthenticationFlags
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-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.ArgumentCaptor
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidJUnit4::class)
-class BouncerMessageRepositoryTest : SysuiTestCase() {
-
- @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor
- @Mock private lateinit var securityModel: KeyguardSecurityModel
- @Mock private lateinit var systemPropertiesHelper: SystemPropertiesHelper
- @Captor
- private lateinit var updateMonitorCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback>
-
- private lateinit var underTest: BouncerMessageRepository
- private lateinit var trustRepository: FakeTrustRepository
- private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
- private lateinit var userRepository: FakeUserRepository
- private lateinit var fingerprintRepository: FakeDeviceEntryFingerprintAuthRepository
- private lateinit var testScope: TestScope
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- trustRepository = FakeTrustRepository()
- biometricSettingsRepository = FakeBiometricSettingsRepository()
- userRepository = FakeUserRepository()
- userRepository.setUserInfos(listOf(PRIMARY_USER))
- fingerprintRepository = FakeDeviceEntryFingerprintAuthRepository()
- testScope = TestScope()
-
- biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(false)
- whenever(securityModel.getSecurityMode(PRIMARY_USER_ID)).thenReturn(PIN)
- underTest =
- BouncerMessageRepositoryImpl(
- trustRepository = trustRepository,
- biometricSettingsRepository = biometricSettingsRepository,
- updateMonitor = updateMonitor,
- bouncerMessageFactory =
- BouncerMessageFactory(biometricSettingsRepository, securityModel),
- userRepository = userRepository,
- fingerprintAuthRepository = fingerprintRepository,
- systemPropertiesHelper = systemPropertiesHelper
- )
- }
-
- @Test
- fun setCustomMessage_propagatesState() =
- testScope.runTest {
- underTest.setCustomMessage(message("not empty"))
-
- val customMessage = collectLastValue(underTest.customMessage)
-
- assertThat(customMessage()).isEqualTo(message("not empty"))
- }
-
- @Test
- fun setFaceMessage_propagatesState() =
- testScope.runTest {
- underTest.setFaceAcquisitionMessage(message("not empty"))
-
- val faceAcquisitionMessage = collectLastValue(underTest.faceAcquisitionMessage)
-
- assertThat(faceAcquisitionMessage()).isEqualTo(message("not empty"))
- }
-
- @Test
- fun setFpMessage_propagatesState() =
- testScope.runTest {
- underTest.setFingerprintAcquisitionMessage(message("not empty"))
-
- val fpAcquisitionMsg = collectLastValue(underTest.fingerprintAcquisitionMessage)
-
- assertThat(fpAcquisitionMsg()).isEqualTo(message("not empty"))
- }
-
- @Test
- fun setPrimaryAuthMessage_propagatesState() =
- testScope.runTest {
- underTest.setPrimaryAuthMessage(message("not empty"))
-
- val primaryAuthMessage = collectLastValue(underTest.primaryAuthMessage)
-
- assertThat(primaryAuthMessage()).isEqualTo(message("not empty"))
- }
-
- @Test
- fun biometricAuthMessage_propagatesBiometricAuthMessages() =
- testScope.runTest {
- userRepository.setSelectedUserInfo(PRIMARY_USER)
- val biometricAuthMessage = collectLastValue(underTest.biometricAuthMessage)
- runCurrent()
-
- verify(updateMonitor).registerCallback(updateMonitorCallback.capture())
-
- updateMonitorCallback.value.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT)
-
- assertThat(biometricAuthMessage())
- .isEqualTo(message(R.string.kg_fp_not_recognized, R.string.kg_bio_try_again_or_pin))
-
- updateMonitorCallback.value.onBiometricAuthFailed(BiometricSourceType.FACE)
-
- assertThat(biometricAuthMessage())
- .isEqualTo(
- message(R.string.bouncer_face_not_recognized, R.string.kg_bio_try_again_or_pin)
- )
-
- updateMonitorCallback.value.onBiometricAcquired(BiometricSourceType.FACE, 0)
-
- assertThat(biometricAuthMessage()).isNull()
- }
-
- @Test
- fun onFaceLockout_propagatesState() =
- testScope.runTest {
- userRepository.setSelectedUserInfo(PRIMARY_USER)
- val lockoutMessage = collectLastValue(underTest.biometricLockedOutMessage)
- runCurrent()
- verify(updateMonitor).registerCallback(updateMonitorCallback.capture())
-
- whenever(updateMonitor.isFaceLockedOut).thenReturn(true)
- updateMonitorCallback.value.onLockedOutStateChanged(BiometricSourceType.FACE)
-
- assertThat(lockoutMessage())
- .isEqualTo(message(keyguard_enter_pin, R.string.kg_face_locked_out))
-
- whenever(updateMonitor.isFaceLockedOut).thenReturn(false)
- updateMonitorCallback.value.onLockedOutStateChanged(BiometricSourceType.FACE)
- assertThat(lockoutMessage()).isNull()
- }
-
- @Test
- fun onFingerprintLockout_propagatesState() =
- testScope.runTest {
- userRepository.setSelectedUserInfo(PRIMARY_USER)
- val lockedOutMessage = collectLastValue(underTest.biometricLockedOutMessage)
- runCurrent()
-
- fingerprintRepository.setLockedOut(true)
-
- assertThat(lockedOutMessage())
- .isEqualTo(message(keyguard_enter_pin, R.string.kg_fp_locked_out))
-
- fingerprintRepository.setLockedOut(false)
- assertThat(lockedOutMessage()).isNull()
- }
-
- @Test
- fun onRestartForMainlineUpdate_shouldProvideRelevantMessage() =
- testScope.runTest {
- whenever(systemPropertiesHelper.get("sys.boot.reason.last"))
- .thenReturn("reboot,mainline_update")
- userRepository.setSelectedUserInfo(PRIMARY_USER)
- biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
-
- verifyMessagesForAuthFlag(
- STRONG_AUTH_REQUIRED_AFTER_BOOT to
- Pair(keyguard_enter_pin, R.string.kg_prompt_after_update_pin),
- )
- }
-
- @Test
- fun onAuthFlagsChanged_withTrustNotManagedAndNoBiometrics_isANoop() =
- testScope.runTest {
- userRepository.setSelectedUserInfo(PRIMARY_USER)
- trustRepository.setCurrentUserTrustManaged(false)
- biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false)
- biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
-
- verifyMessagesForAuthFlag(
- STRONG_AUTH_NOT_REQUIRED to null,
- STRONG_AUTH_REQUIRED_AFTER_BOOT to null,
- SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to null,
- STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null,
- STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to null,
- STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to null,
- STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to null,
- SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to null,
- STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to null,
- STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
- Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock),
- )
- }
-
- @Test
- fun authFlagsChanges_withTrustManaged_providesDifferentMessages() =
- testScope.runTest {
- userRepository.setSelectedUserInfo(PRIMARY_USER)
- biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false)
- biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
-
- trustRepository.setCurrentUserTrustManaged(true)
-
- verifyMessagesForAuthFlag(
- STRONG_AUTH_NOT_REQUIRED to null,
- STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null,
- STRONG_AUTH_REQUIRED_AFTER_BOOT to
- Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin),
- STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
- Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout),
- STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
- Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock),
- SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to
- Pair(keyguard_enter_pin, kg_trust_agent_disabled),
- SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to
- Pair(keyguard_enter_pin, kg_trust_agent_disabled),
- STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
- Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin),
- STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
- Pair(keyguard_enter_pin, kg_prompt_unattended_update),
- STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to
- Pair(keyguard_enter_pin, kg_prompt_auth_timeout),
- )
- }
-
- @Test
- fun authFlagsChanges_withFaceEnrolled_providesDifferentMessages() =
- testScope.runTest {
- userRepository.setSelectedUserInfo(PRIMARY_USER)
- trustRepository.setCurrentUserTrustManaged(false)
- biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
-
- biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
-
- verifyMessagesForAuthFlag(
- STRONG_AUTH_NOT_REQUIRED to null,
- STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null,
- SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to null,
- SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to null,
- STRONG_AUTH_REQUIRED_AFTER_BOOT to
- Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin),
- STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
- Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout),
- STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
- Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock),
- STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
- Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin),
- STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
- Pair(keyguard_enter_pin, kg_prompt_unattended_update),
- STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to
- Pair(keyguard_enter_pin, kg_prompt_auth_timeout),
- )
- }
-
- @Test
- fun authFlagsChanges_withFingerprintEnrolled_providesDifferentMessages() =
- testScope.runTest {
- userRepository.setSelectedUserInfo(PRIMARY_USER)
- trustRepository.setCurrentUserTrustManaged(false)
- biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false)
-
- biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
-
- verifyMessagesForAuthFlag(
- STRONG_AUTH_NOT_REQUIRED to null,
- STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to null,
- SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to null,
- SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to null,
- STRONG_AUTH_REQUIRED_AFTER_BOOT to
- Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin),
- STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
- Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout),
- STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
- Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock),
- STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
- Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin),
- STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
- Pair(keyguard_enter_pin, kg_prompt_unattended_update),
- STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to
- Pair(keyguard_enter_pin, kg_prompt_auth_timeout),
- )
- }
-
- private fun TestScope.verifyMessagesForAuthFlag(
- vararg authFlagToExpectedMessages: Pair<Int, Pair<Int, Int>?>
- ) {
- val authFlagsMessage = collectLastValue(underTest.authFlagsMessage)
-
- authFlagToExpectedMessages.forEach { (flag, messagePair) ->
- biometricSettingsRepository.setAuthenticationFlags(
- AuthenticationFlags(PRIMARY_USER_ID, flag)
- )
-
- assertThat(authFlagsMessage())
- .isEqualTo(messagePair?.let { message(it.first, it.second) })
- }
- }
-
- private fun message(primaryResId: Int, secondaryResId: Int): BouncerMessageModel {
- return BouncerMessageModel(
- message = Message(messageResId = primaryResId, animate = false),
- secondaryMessage = Message(messageResId = secondaryResId, animate = false)
- )
- }
- private fun message(value: String): BouncerMessageModel {
- return BouncerMessageModel(message = Message(message = value))
- }
-
- companion object {
- private const val PRIMARY_USER_ID = 0
- private val PRIMARY_USER =
- UserInfo(
- /* id= */ PRIMARY_USER_ID,
- /* name= */ "primary user",
- /* flags= */ UserInfo.FLAG_PRIMARY
- )
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
index c1286a1..cc4eca5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
@@ -17,187 +17,216 @@
package com.android.systemui.bouncer.domain.interactor
import android.content.pm.UserInfo
+import android.os.Handler
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
-import com.android.systemui.res.R.string.kg_too_many_failed_attempts_countdown
-import com.android.systemui.res.R.string.kg_unlock_with_pin_or_fp
+import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
-import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository
+import com.android.systemui.biometrics.data.repository.FaceSensorInfo
+import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.shared.model.BouncerMessageModel
-import com.android.systemui.bouncer.shared.model.Message
-import com.android.systemui.coroutines.FlowValue
+import com.android.systemui.bouncer.ui.BouncerView
+import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.SystemPropertiesHelper
+import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeTrustRepository
+import com.android.systemui.keyguard.shared.model.AuthenticationFlags
+import com.android.systemui.res.R.string.kg_too_many_failed_attempts_countdown
+import com.android.systemui.res.R.string.kg_trust_agent_disabled
+import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.mockito.KotlinArgumentCaptor
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
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.ArgumentMatchers.eq
import org.mockito.Mock
+import org.mockito.Mockito
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidJUnit4::class)
class BouncerMessageInteractorTest : SysuiTestCase() {
+ private val countDownTimerCallback = KotlinArgumentCaptor(CountDownTimerCallback::class.java)
+ private val repository = BouncerMessageRepositoryImpl()
+ private val userRepository = FakeUserRepository()
+ private val fakeTrustRepository = FakeTrustRepository()
+ private val fakeFacePropertyRepository = FakeFacePropertyRepository()
+ private val bouncerRepository = FakeKeyguardBouncerRepository()
+ private val fakeDeviceEntryFingerprintAuthRepository =
+ FakeDeviceEntryFingerprintAuthRepository()
+ private val fakeDeviceEntryFaceAuthRepository = FakeDeviceEntryFaceAuthRepository()
+ private val biometricSettingsRepository: FakeBiometricSettingsRepository =
+ FakeBiometricSettingsRepository()
+ @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var securityModel: KeyguardSecurityModel
- @Mock private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
@Mock private lateinit var countDownTimerUtil: CountDownTimerUtil
- private lateinit var countDownTimerCallback: KotlinArgumentCaptor<CountDownTimerCallback>
- private lateinit var underTest: BouncerMessageInteractor
- private lateinit var repository: FakeBouncerMessageRepository
- private lateinit var userRepository: FakeUserRepository
+ @Mock private lateinit var systemPropertiesHelper: SystemPropertiesHelper
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+
+ private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
private lateinit var testScope: TestScope
- private lateinit var bouncerMessage: FlowValue<BouncerMessageModel?>
+ private lateinit var underTest: BouncerMessageInteractor
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- repository = FakeBouncerMessageRepository()
- userRepository = FakeUserRepository()
userRepository.setUserInfos(listOf(PRIMARY_USER))
testScope = TestScope()
- countDownTimerCallback = KotlinArgumentCaptor(CountDownTimerCallback::class.java)
- biometricSettingsRepository = FakeBiometricSettingsRepository()
-
allowTestableLooperAsMainThread()
whenever(securityModel.getSecurityMode(PRIMARY_USER_ID)).thenReturn(PIN)
biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+ overrideResource(kg_trust_agent_disabled, "Trust agent is unavailable")
}
suspend fun TestScope.init() {
userRepository.setSelectedUserInfo(PRIMARY_USER)
- val featureFlags = FakeFeatureFlags()
- featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
+ val featureFlags = FakeFeatureFlags().apply { set(Flags.REVAMPED_BOUNCER_MESSAGES, true) }
+ primaryBouncerInteractor =
+ PrimaryBouncerInteractor(
+ bouncerRepository,
+ Mockito.mock(BouncerView::class.java),
+ Mockito.mock(Handler::class.java),
+ Mockito.mock(KeyguardStateController::class.java),
+ Mockito.mock(KeyguardSecurityModel::class.java),
+ Mockito.mock(PrimaryBouncerCallbackInteractor::class.java),
+ Mockito.mock(FalsingCollector::class.java),
+ Mockito.mock(DismissCallbackRegistry::class.java),
+ context,
+ keyguardUpdateMonitor,
+ fakeTrustRepository,
+ testScope.backgroundScope,
+ )
underTest =
BouncerMessageInteractor(
repository = repository,
- factory = BouncerMessageFactory(biometricSettingsRepository, securityModel),
userRepository = userRepository,
countDownTimerUtil = countDownTimerUtil,
- featureFlags = featureFlags
+ featureFlags = featureFlags,
+ updateMonitor = updateMonitor,
+ biometricSettingsRepository = biometricSettingsRepository,
+ applicationScope = this.backgroundScope,
+ trustRepository = fakeTrustRepository,
+ systemPropertiesHelper = systemPropertiesHelper,
+ primaryBouncerInteractor = primaryBouncerInteractor,
+ facePropertyRepository = fakeFacePropertyRepository,
+ deviceEntryFingerprintAuthRepository = fakeDeviceEntryFingerprintAuthRepository,
+ faceAuthRepository = fakeDeviceEntryFaceAuthRepository,
+ securityModel = securityModel
)
- bouncerMessage = collectLastValue(underTest.bouncerMessage)
+ biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+ fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
+ bouncerRepository.setPrimaryShow(true)
+ runCurrent()
}
@Test
- fun onIncorrectSecurityInput_setsTheBouncerModelInTheRepository() =
+ fun onIncorrectSecurityInput_providesTheAppropriateValueForBouncerMessage() =
testScope.runTest {
init()
+ val bouncerMessage by collectLastValue(underTest.bouncerMessage)
underTest.onPrimaryAuthIncorrectAttempt()
- assertThat(repository.primaryAuthMessage).isNotNull()
- assertThat(
- context.resources.getString(
- repository.primaryAuthMessage.value!!.message!!.messageResId!!
- )
- )
- .isEqualTo("Wrong PIN. Try again.")
+ assertThat(bouncerMessage).isNotNull()
+ assertThat(primaryResMessage(bouncerMessage)).isEqualTo("Wrong PIN. Try again.")
}
@Test
fun onUserStartsPrimaryAuthInput_clearsAllSetBouncerMessages() =
testScope.runTest {
init()
- repository.setCustomMessage(message("not empty"))
- repository.setFaceAcquisitionMessage(message("not empty"))
- repository.setFingerprintAcquisitionMessage(message("not empty"))
- repository.setPrimaryAuthMessage(message("not empty"))
+ val bouncerMessage by collectLastValue(underTest.bouncerMessage)
+ underTest.onPrimaryAuthIncorrectAttempt()
+ assertThat(primaryResMessage(bouncerMessage)).isEqualTo("Wrong PIN. Try again.")
underTest.onPrimaryBouncerUserInput()
- assertThat(repository.customMessage.value).isNull()
- assertThat(repository.faceAcquisitionMessage.value).isNull()
- assertThat(repository.fingerprintAcquisitionMessage.value).isNull()
- assertThat(repository.primaryAuthMessage.value).isNull()
+ assertThat(primaryResMessage(bouncerMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
}
@Test
- fun onBouncerBeingHidden_clearsAllSetBouncerMessages() =
+ fun setCustomMessage_propagateValue() =
testScope.runTest {
init()
- repository.setCustomMessage(message("not empty"))
- repository.setFaceAcquisitionMessage(message("not empty"))
- repository.setFingerprintAcquisitionMessage(message("not empty"))
- repository.setPrimaryAuthMessage(message("not empty"))
-
- underTest.onBouncerBeingHidden()
-
- assertThat(repository.customMessage.value).isNull()
- assertThat(repository.faceAcquisitionMessage.value).isNull()
- assertThat(repository.fingerprintAcquisitionMessage.value).isNull()
- assertThat(repository.primaryAuthMessage.value).isNull()
- }
-
- @Test
- fun setCustomMessage_setsRepositoryValue() =
- testScope.runTest {
- init()
+ val bouncerMessage by collectLastValue(underTest.bouncerMessage)
underTest.setCustomMessage("not empty")
- val customMessage = repository.customMessage
- assertThat(customMessage.value!!.message!!.messageResId)
- .isEqualTo(kg_unlock_with_pin_or_fp)
- assertThat(customMessage.value!!.secondaryMessage!!.message).isEqualTo("not empty")
+ assertThat(primaryResMessage(bouncerMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(bouncerMessage?.secondaryMessage?.message).isEqualTo("not empty")
underTest.setCustomMessage(null)
- assertThat(customMessage.value).isNull()
+ assertThat(primaryResMessage(bouncerMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(bouncerMessage?.secondaryMessage?.message).isNull()
}
@Test
- fun setFaceMessage_setsRepositoryValue() =
+ fun setFaceMessage_propagateValue() =
testScope.runTest {
init()
+ val bouncerMessage by collectLastValue(underTest.bouncerMessage)
underTest.setFaceAcquisitionMessage("not empty")
- val faceAcquisitionMessage = repository.faceAcquisitionMessage
-
- assertThat(faceAcquisitionMessage.value!!.message!!.messageResId)
- .isEqualTo(kg_unlock_with_pin_or_fp)
- assertThat(faceAcquisitionMessage.value!!.secondaryMessage!!.message)
- .isEqualTo("not empty")
+ assertThat(primaryResMessage(bouncerMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(bouncerMessage?.secondaryMessage?.message).isEqualTo("not empty")
underTest.setFaceAcquisitionMessage(null)
- assertThat(faceAcquisitionMessage.value).isNull()
+ assertThat(primaryResMessage(bouncerMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(bouncerMessage?.secondaryMessage?.message).isNull()
}
@Test
- fun setFingerprintMessage_setsRepositoryValue() =
+ fun setFingerprintMessage_propagateValue() =
testScope.runTest {
init()
+ val bouncerMessage by collectLastValue(underTest.bouncerMessage)
underTest.setFingerprintAcquisitionMessage("not empty")
- val fingerprintAcquisitionMessage = repository.fingerprintAcquisitionMessage
-
- assertThat(fingerprintAcquisitionMessage.value!!.message!!.messageResId)
- .isEqualTo(kg_unlock_with_pin_or_fp)
- assertThat(fingerprintAcquisitionMessage.value!!.secondaryMessage!!.message)
- .isEqualTo("not empty")
+ assertThat(primaryResMessage(bouncerMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(bouncerMessage?.secondaryMessage?.message).isEqualTo("not empty")
underTest.setFingerprintAcquisitionMessage(null)
- assertThat(fingerprintAcquisitionMessage.value).isNull()
+ assertThat(primaryResMessage(bouncerMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(bouncerMessage?.secondaryMessage?.message).isNull()
}
@Test
fun onPrimaryAuthLockout_startsTimerForSpecifiedNumberOfSeconds() =
testScope.runTest {
init()
+ val bouncerMessage by collectLastValue(underTest.bouncerMessage)
underTest.onPrimaryAuthLockedOut(3)
@@ -206,7 +235,7 @@
countDownTimerCallback.value.onTick(2000L)
- val primaryMessage = repository.primaryAuthMessage.value!!.message!!
+ val primaryMessage = bouncerMessage!!.message!!
assertThat(primaryMessage.messageResId!!)
.isEqualTo(kg_too_many_failed_attempts_countdown)
assertThat(primaryMessage.formatterArgs).isEqualTo(mapOf(Pair("count", 2)))
@@ -216,10 +245,7 @@
fun onPrimaryAuthLockout_timerComplete_resetsRepositoryMessages() =
testScope.runTest {
init()
- repository.setCustomMessage(message("not empty"))
- repository.setFaceAcquisitionMessage(message("not empty"))
- repository.setFingerprintAcquisitionMessage(message("not empty"))
- repository.setPrimaryAuthMessage(message("not empty"))
+ val bouncerMessage by collectLastValue(underTest.bouncerMessage)
underTest.onPrimaryAuthLockedOut(3)
@@ -228,59 +254,269 @@
countDownTimerCallback.value.onFinish()
- assertThat(repository.customMessage.value).isNull()
- assertThat(repository.faceAcquisitionMessage.value).isNull()
- assertThat(repository.fingerprintAcquisitionMessage.value).isNull()
- assertThat(repository.primaryAuthMessage.value).isNull()
+ assertThat(primaryResMessage(bouncerMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(bouncerMessage?.secondaryMessage?.message).isNull()
}
@Test
- fun bouncerMessage_hasPriorityOrderOfMessages() =
+ fun onFaceLockout_propagatesState() =
testScope.runTest {
init()
- repository.setBiometricAuthMessage(message("biometric message"))
- repository.setFaceAcquisitionMessage(message("face acquisition message"))
- repository.setFingerprintAcquisitionMessage(message("fingerprint acquisition message"))
- repository.setPrimaryAuthMessage(message("primary auth message"))
- repository.setAuthFlagsMessage(message("auth flags message"))
- repository.setBiometricLockedOutMessage(message("biometrics locked out"))
- repository.setCustomMessage(message("custom message"))
+ val lockoutMessage by collectLastValue(underTest.bouncerMessage)
- assertThat(bouncerMessage()).isEqualTo(message("primary auth message"))
+ fakeDeviceEntryFaceAuthRepository.setLockedOut(true)
+ runCurrent()
- repository.setPrimaryAuthMessage(null)
+ assertThat(primaryResMessage(lockoutMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(secondaryResMessage(lockoutMessage))
+ .isEqualTo("Can’t unlock with face. Too many attempts.")
- assertThat(bouncerMessage()).isEqualTo(message("biometric message"))
+ fakeDeviceEntryFaceAuthRepository.setLockedOut(false)
+ runCurrent()
- repository.setBiometricAuthMessage(null)
-
- assertThat(bouncerMessage()).isEqualTo(message("fingerprint acquisition message"))
-
- repository.setFingerprintAcquisitionMessage(null)
-
- assertThat(bouncerMessage()).isEqualTo(message("face acquisition message"))
-
- repository.setFaceAcquisitionMessage(null)
-
- assertThat(bouncerMessage()).isEqualTo(message("custom message"))
-
- repository.setCustomMessage(null)
-
- assertThat(bouncerMessage()).isEqualTo(message("auth flags message"))
-
- repository.setAuthFlagsMessage(null)
-
- assertThat(bouncerMessage()).isEqualTo(message("biometrics locked out"))
-
- repository.setBiometricLockedOutMessage(null)
-
- // sets the default message if everything else is null
- assertThat(bouncerMessage()!!.message!!.messageResId)
- .isEqualTo(kg_unlock_with_pin_or_fp)
+ assertThat(primaryResMessage(lockoutMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(lockoutMessage?.secondaryMessage?.message).isNull()
}
- private fun message(value: String): BouncerMessageModel {
- return BouncerMessageModel(message = Message(message = value))
+ @Test
+ fun onFaceLockout_whenItIsClass3_propagatesState() =
+ testScope.runTest {
+ init()
+ val lockoutMessage by collectLastValue(underTest.bouncerMessage)
+ fakeFacePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.STRONG))
+ fakeDeviceEntryFaceAuthRepository.setLockedOut(true)
+ runCurrent()
+
+ assertThat(primaryResMessage(lockoutMessage)).isEqualTo("Enter PIN")
+ assertThat(secondaryResMessage(lockoutMessage))
+ .isEqualTo("PIN is required after too many attempts")
+
+ fakeDeviceEntryFaceAuthRepository.setLockedOut(false)
+ runCurrent()
+
+ assertThat(primaryResMessage(lockoutMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(lockoutMessage?.secondaryMessage?.message).isNull()
+ }
+
+ @Test
+ fun onFingerprintLockout_propagatesState() =
+ testScope.runTest {
+ init()
+ val lockedOutMessage by collectLastValue(underTest.bouncerMessage)
+
+ fakeDeviceEntryFingerprintAuthRepository.setLockedOut(true)
+ runCurrent()
+
+ assertThat(primaryResMessage(lockedOutMessage)).isEqualTo("Enter PIN")
+ assertThat(secondaryResMessage(lockedOutMessage))
+ .isEqualTo("PIN is required after too many attempts")
+
+ fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
+ runCurrent()
+
+ assertThat(primaryResMessage(lockedOutMessage))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(lockedOutMessage?.secondaryMessage?.message).isNull()
+ }
+
+ @Test
+ fun onRestartForMainlineUpdate_shouldProvideRelevantMessage() =
+ testScope.runTest {
+ init()
+ whenever(systemPropertiesHelper.get("sys.boot.reason.last"))
+ .thenReturn("reboot,mainline_update")
+ biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
+
+ verifyMessagesForAuthFlag(
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT to
+ Pair("Enter PIN", "Device updated. Enter PIN to continue.")
+ )
+ }
+
+ @Test
+ fun onAuthFlagsChanged_withTrustNotManagedAndNoBiometrics_isANoop() =
+ testScope.runTest {
+ init()
+ fakeTrustRepository.setTrustUsuallyManaged(false)
+ biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false)
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+
+ val defaultMessage = Pair("Enter PIN", null)
+
+ verifyMessagesForAuthFlag(
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker
+ .STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to defaultMessage,
+ LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
+ Pair("Enter PIN", "For added security, device was locked by work policy")
+ )
+ }
+
+ @Test
+ fun authFlagsChanges_withTrustManaged_providesDifferentMessages() =
+ testScope.runTest {
+ init()
+
+ userRepository.setSelectedUserInfo(PRIMARY_USER)
+ biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false)
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+
+ fakeTrustRepository.setCurrentUserTrustManaged(true)
+ fakeTrustRepository.setTrustUsuallyManaged(true)
+
+ val defaultMessage = Pair("Enter PIN", null)
+
+ verifyMessagesForAuthFlag(
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED to defaultMessage,
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT to
+ Pair("Enter PIN", "PIN is required after device restarts"),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
+ Pair("Enter PIN", "Added security required. PIN not used for a while."),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
+ Pair("Enter PIN", "For added security, device was locked by work policy"),
+ LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to
+ Pair("Enter PIN", "Trust agent is unavailable"),
+ LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to
+ Pair("Enter PIN", "Trust agent is unavailable"),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
+ Pair("Enter PIN", "PIN is required after lockdown"),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
+ Pair("Enter PIN", "Update will install when device not in use"),
+ LockPatternUtils.StrongAuthTracker
+ .STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to
+ Pair(
+ "Enter PIN",
+ "Added security required. Device wasn’t unlocked for a while."
+ ),
+ )
+ }
+
+ @Test
+ fun authFlagsChanges_withFaceEnrolled_providesDifferentMessages() =
+ testScope.runTest {
+ init()
+ userRepository.setSelectedUserInfo(PRIMARY_USER)
+ fakeTrustRepository.setTrustUsuallyManaged(false)
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+
+ biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
+ val defaultMessage = Pair("Enter PIN", null)
+
+ verifyMessagesForAuthFlag(
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED to defaultMessage,
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to
+ defaultMessage,
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT to
+ Pair("Enter PIN", "PIN is required after device restarts"),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
+ Pair("Enter PIN", "Added security required. PIN not used for a while."),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
+ Pair("Enter PIN", "For added security, device was locked by work policy"),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
+ Pair("Enter PIN", "PIN is required after lockdown"),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
+ Pair("Enter PIN", "Update will install when device not in use"),
+ LockPatternUtils.StrongAuthTracker
+ .STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to
+ Pair(
+ "Enter PIN",
+ "Added security required. Device wasn’t unlocked for a while."
+ ),
+ )
+ }
+
+ @Test
+ fun authFlagsChanges_withFingerprintEnrolled_providesDifferentMessages() =
+ testScope.runTest {
+ init()
+ userRepository.setSelectedUserInfo(PRIMARY_USER)
+ fakeTrustRepository.setCurrentUserTrustManaged(false)
+ biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false)
+
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+
+ verifyMessagesForAuthFlag(
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED to
+ Pair("Unlock with PIN or fingerprint", null)
+ )
+
+ biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(false)
+
+ verifyMessagesForAuthFlag(
+ LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST to
+ Pair("Enter PIN", null),
+ LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED to
+ Pair("Enter PIN", null),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT to
+ Pair("Enter PIN", "PIN is required after device restarts"),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT to
+ Pair("Enter PIN", "Added security required. PIN not used for a while."),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW to
+ Pair("Enter PIN", "For added security, device was locked by work policy"),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN to
+ Pair("Enter PIN", "PIN is required after lockdown"),
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE to
+ Pair("Enter PIN", "Update will install when device not in use"),
+ LockPatternUtils.StrongAuthTracker
+ .STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT to
+ Pair(
+ "Enter PIN",
+ "Added security required. Device wasn’t unlocked for a while."
+ ),
+ )
+ }
+
+ private fun primaryResMessage(bouncerMessage: BouncerMessageModel?) =
+ resString(bouncerMessage?.message?.messageResId)
+
+ private fun secondaryResMessage(bouncerMessage: BouncerMessageModel?) =
+ resString(bouncerMessage?.secondaryMessage?.messageResId)
+
+ private fun resString(msgResId: Int?): String? =
+ msgResId?.let { context.resources.getString(it) }
+
+ private fun TestScope.verifyMessagesForAuthFlag(
+ vararg authFlagToExpectedMessages: Pair<Int, Pair<String, String?>>
+ ) {
+ val authFlagsMessage by collectLastValue(underTest.bouncerMessage)
+
+ authFlagToExpectedMessages.forEach { (flag, messagePair) ->
+ biometricSettingsRepository.setAuthenticationFlags(
+ AuthenticationFlags(PRIMARY_USER_ID, flag)
+ )
+ runCurrent()
+
+ assertThat(primaryResMessage(authFlagsMessage)).isEqualTo(messagePair.first)
+ if (messagePair.second == null) {
+ assertThat(authFlagsMessage?.secondaryMessage?.messageResId).isEqualTo(0)
+ assertThat(authFlagsMessage?.secondaryMessage?.message).isNull()
+ } else {
+ assertThat(authFlagsMessage?.secondaryMessage?.messageResId).isNotEqualTo(0)
+ assertThat(secondaryResMessage(authFlagsMessage)).isEqualTo(messagePair.second)
+ }
+ }
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
index 29d7500..7f784d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogcatEchoTracker
import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
@@ -260,4 +261,19 @@
listener.value.onIsActiveUnlockRunningChanged(true, users[0].id)
assertThat(isCurrentUserActiveUnlockRunning).isTrue()
}
+
+ @Test
+ fun isTrustUsuallyManaged_providesTheValueForCurrentUser() =
+ testScope.runTest {
+ runCurrent()
+ val trustUsuallyManaged by collectLastValue(underTest.isCurrentUserTrustUsuallyManaged)
+ whenever(trustManager.isTrustUsuallyManaged(users[0].id)).thenReturn(true)
+ whenever(trustManager.isTrustUsuallyManaged(users[1].id)).thenReturn(false)
+
+ userRepository.setSelectedUserInfo(users[0])
+
+ assertThat(trustUsuallyManaged).isTrue()
+ userRepository.setSelectedUserInfo(users[1])
+ assertThat(trustUsuallyManaged).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 223b1c4..9651072 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.shade
+import android.os.Handler
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.KeyEvent
@@ -24,29 +25,42 @@
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardMessageAreaController
import com.android.keyguard.KeyguardSecurityContainerController
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardBouncerComponent
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.back.domain.interactor.BackActionInteractor
-import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
-import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository
+import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
+import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
+import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.SystemPropertiesHelper
import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
+import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.log.BouncerLogger
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.DragDownHelper
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -62,6 +76,7 @@
import com.android.systemui.statusbar.phone.DozeServiceHost
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.user.data.repository.FakeUserRepository
@@ -201,11 +216,35 @@
featureFlags,
fakeClock,
BouncerMessageInteractor(
- FakeBouncerMessageRepository(),
- mock(BouncerMessageFactory::class.java),
- FakeUserRepository(),
- CountDownTimerUtil(),
- featureFlags
+ repository = BouncerMessageRepositoryImpl(),
+ userRepository = FakeUserRepository(),
+ countDownTimerUtil = mock(CountDownTimerUtil::class.java),
+ featureFlags = featureFlags,
+ updateMonitor = mock(KeyguardUpdateMonitor::class.java),
+ biometricSettingsRepository = FakeBiometricSettingsRepository(),
+ applicationScope = testScope.backgroundScope,
+ trustRepository = FakeTrustRepository(),
+ systemPropertiesHelper = mock(SystemPropertiesHelper::class.java),
+ primaryBouncerInteractor =
+ PrimaryBouncerInteractor(
+ FakeKeyguardBouncerRepository(),
+ mock(BouncerView::class.java),
+ mock(Handler::class.java),
+ mock(KeyguardStateController::class.java),
+ mock(KeyguardSecurityModel::class.java),
+ mock(PrimaryBouncerCallbackInteractor::class.java),
+ mock(FalsingCollector::class.java),
+ mock(DismissCallbackRegistry::class.java),
+ context,
+ mock(KeyguardUpdateMonitor::class.java),
+ FakeTrustRepository(),
+ testScope.backgroundScope,
+ ),
+ facePropertyRepository = FakeFacePropertyRepository(),
+ deviceEntryFingerprintAuthRepository =
+ FakeDeviceEntryFingerprintAuthRepository(),
+ faceAuthRepository = FakeDeviceEntryFaceAuthRepository(),
+ securityModel = mock(KeyguardSecurityModel::class.java),
),
BouncerLogger(logcatLogBuffer("BouncerLog")),
keyEventInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index e817016..0023020 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.shade
+import android.os.Handler
import android.os.SystemClock
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
@@ -23,28 +24,41 @@
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardMessageAreaController
import com.android.keyguard.KeyguardSecurityContainerController
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardBouncerComponent
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.back.domain.interactor.BackActionInteractor
-import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
-import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository
+import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
+import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
+import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.SystemPropertiesHelper
import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
+import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.log.BouncerLogger
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.DragDownHelper
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -60,6 +74,7 @@
import com.android.systemui.statusbar.phone.DozeScrimController
import com.android.systemui.statusbar.phone.DozeServiceHost
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.user.data.repository.FakeUserRepository
@@ -203,11 +218,35 @@
featureFlags,
FakeSystemClock(),
BouncerMessageInteractor(
- FakeBouncerMessageRepository(),
- Mockito.mock(BouncerMessageFactory::class.java),
- FakeUserRepository(),
- CountDownTimerUtil(),
- featureFlags
+ repository = BouncerMessageRepositoryImpl(),
+ userRepository = FakeUserRepository(),
+ countDownTimerUtil = Mockito.mock(CountDownTimerUtil::class.java),
+ featureFlags = featureFlags,
+ updateMonitor = Mockito.mock(KeyguardUpdateMonitor::class.java),
+ biometricSettingsRepository = FakeBiometricSettingsRepository(),
+ applicationScope = testScope.backgroundScope,
+ trustRepository = FakeTrustRepository(),
+ systemPropertiesHelper = Mockito.mock(SystemPropertiesHelper::class.java),
+ primaryBouncerInteractor =
+ PrimaryBouncerInteractor(
+ FakeKeyguardBouncerRepository(),
+ Mockito.mock(BouncerView::class.java),
+ Mockito.mock(Handler::class.java),
+ Mockito.mock(KeyguardStateController::class.java),
+ Mockito.mock(KeyguardSecurityModel::class.java),
+ Mockito.mock(PrimaryBouncerCallbackInteractor::class.java),
+ Mockito.mock(FalsingCollector::class.java),
+ Mockito.mock(DismissCallbackRegistry::class.java),
+ context,
+ Mockito.mock(KeyguardUpdateMonitor::class.java),
+ FakeTrustRepository(),
+ testScope.backgroundScope,
+ ),
+ facePropertyRepository = FakeFacePropertyRepository(),
+ deviceEntryFingerprintAuthRepository =
+ FakeDeviceEntryFingerprintAuthRepository(),
+ faceAuthRepository = FakeDeviceEntryFaceAuthRepository(),
+ securityModel = Mockito.mock(KeyguardSecurityModel::class.java),
),
BouncerLogger(logcatLogBuffer("BouncerLog")),
Mockito.mock(KeyEventInteractor::class.java),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeBouncerMessageRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeBouncerMessageRepository.kt
deleted file mode 100644
index d9b926d..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeBouncerMessageRepository.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bouncer.data.repository
-
-import com.android.systemui.bouncer.shared.model.BouncerMessageModel
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-
-class FakeBouncerMessageRepository : BouncerMessageRepository {
- private val _primaryAuthMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val primaryAuthMessage: StateFlow<BouncerMessageModel?>
- get() = _primaryAuthMessage
-
- private val _faceAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val faceAcquisitionMessage: StateFlow<BouncerMessageModel?>
- get() = _faceAcquisitionMessage
- private val _fingerprintAcquisitionMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val fingerprintAcquisitionMessage: StateFlow<BouncerMessageModel?>
- get() = _fingerprintAcquisitionMessage
- private val _customMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val customMessage: StateFlow<BouncerMessageModel?>
- get() = _customMessage
- private val _biometricAuthMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val biometricAuthMessage: StateFlow<BouncerMessageModel?>
- get() = _biometricAuthMessage
- private val _authFlagsMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val authFlagsMessage: StateFlow<BouncerMessageModel?>
- get() = _authFlagsMessage
-
- private val _biometricLockedOutMessage = MutableStateFlow<BouncerMessageModel?>(null)
- override val biometricLockedOutMessage: Flow<BouncerMessageModel?>
- get() = _biometricLockedOutMessage
-
- override fun setPrimaryAuthMessage(value: BouncerMessageModel?) {
- _primaryAuthMessage.value = value
- }
-
- override fun setFaceAcquisitionMessage(value: BouncerMessageModel?) {
- _faceAcquisitionMessage.value = value
- }
-
- override fun setFingerprintAcquisitionMessage(value: BouncerMessageModel?) {
- _fingerprintAcquisitionMessage.value = value
- }
-
- override fun setCustomMessage(value: BouncerMessageModel?) {
- _customMessage.value = value
- }
-
- fun setBiometricAuthMessage(value: BouncerMessageModel?) {
- _biometricAuthMessage.value = value
- }
-
- fun setAuthFlagsMessage(value: BouncerMessageModel?) {
- _authFlagsMessage.value = value
- }
-
- fun setBiometricLockedOutMessage(value: BouncerMessageModel?) {
- _biometricLockedOutMessage.value = value
- }
-
- override fun clearMessage() {
- _primaryAuthMessage.value = null
- _faceAcquisitionMessage.value = null
- _fingerprintAcquisitionMessage.value = null
- _customMessage.value = null
- }
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt
index 9d98f94..482126d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt
@@ -25,6 +25,9 @@
import kotlinx.coroutines.flow.asStateFlow
class FakeTrustRepository : TrustRepository {
+ private val _isTrustUsuallyManaged = MutableStateFlow(false)
+ override val isCurrentUserTrustUsuallyManaged: StateFlow<Boolean>
+ get() = _isTrustUsuallyManaged
private val _isCurrentUserTrusted = MutableStateFlow(false)
override val isCurrentUserTrusted: Flow<Boolean>
get() = _isCurrentUserTrusted
@@ -55,4 +58,8 @@
fun setRequestDismissKeyguard(trustModel: TrustModel) {
_requestDismissKeyguard.value = trustModel
}
+
+ fun setTrustUsuallyManaged(value: Boolean) {
+ _isTrustUsuallyManaged.value = value
+ }
}