Merge "Add FingerprintAuthenticationStatus" into udc-qpr-dev am: 7de73f27bb

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/24026553

Change-Id: I0da7cb7af5497e93a2dc98f0b08333a2b446db79
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Beverly Tai
2023-07-18 12:44:12 +00:00
committed by Automerger Merge Worker
19 changed files with 409 additions and 133 deletions

View File

@@ -164,13 +164,13 @@ import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener; import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.shared.constants.TrustAgentUiEvent; import com.android.systemui.keyguard.shared.constants.TrustAgentUiEvent;
import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus; import com.android.systemui.keyguard.shared.model.AcquiredFaceAuthenticationStatus;
import com.android.systemui.keyguard.shared.model.AuthenticationStatus; import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus;
import com.android.systemui.keyguard.shared.model.DetectionStatus; import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus;
import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus; import com.android.systemui.keyguard.shared.model.FaceDetectionStatus;
import com.android.systemui.keyguard.shared.model.FailedAuthenticationStatus; import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus;
import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus; import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus;
import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus; import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus;
import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions; import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions;
import com.android.systemui.log.SessionTracker; import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.WeatherData; import com.android.systemui.plugins.WeatherData;
@@ -1471,27 +1471,31 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private FaceAuthenticationListener mFaceAuthenticationListener = private FaceAuthenticationListener mFaceAuthenticationListener =
new FaceAuthenticationListener() { new FaceAuthenticationListener() {
@Override @Override
public void onAuthenticationStatusChanged(@NonNull AuthenticationStatus status) { public void onAuthenticationStatusChanged(
if (status instanceof AcquiredAuthenticationStatus) { @NonNull FaceAuthenticationStatus status
) {
if (status instanceof AcquiredFaceAuthenticationStatus) {
handleFaceAcquired( handleFaceAcquired(
((AcquiredAuthenticationStatus) status).getAcquiredInfo()); ((AcquiredFaceAuthenticationStatus) status).getAcquiredInfo());
} else if (status instanceof ErrorAuthenticationStatus) { } else if (status instanceof ErrorFaceAuthenticationStatus) {
ErrorAuthenticationStatus error = (ErrorAuthenticationStatus) status; ErrorFaceAuthenticationStatus error =
(ErrorFaceAuthenticationStatus) status;
handleFaceError(error.getMsgId(), error.getMsg()); handleFaceError(error.getMsgId(), error.getMsg());
} else if (status instanceof FailedAuthenticationStatus) { } else if (status instanceof FailedFaceAuthenticationStatus) {
handleFaceAuthFailed(); handleFaceAuthFailed();
} else if (status instanceof HelpAuthenticationStatus) { } else if (status instanceof HelpFaceAuthenticationStatus) {
HelpAuthenticationStatus helpMsg = (HelpAuthenticationStatus) status; HelpFaceAuthenticationStatus helpMsg =
(HelpFaceAuthenticationStatus) status;
handleFaceHelp(helpMsg.getMsgId(), helpMsg.getMsg()); handleFaceHelp(helpMsg.getMsgId(), helpMsg.getMsg());
} else if (status instanceof SuccessAuthenticationStatus) { } else if (status instanceof SuccessFaceAuthenticationStatus) {
FaceManager.AuthenticationResult result = FaceManager.AuthenticationResult result =
((SuccessAuthenticationStatus) status).getSuccessResult(); ((SuccessFaceAuthenticationStatus) status).getSuccessResult();
handleFaceAuthenticated(result.getUserId(), result.isStrongBiometric()); handleFaceAuthenticated(result.getUserId(), result.isStrongBiometric());
} }
} }
@Override @Override
public void onDetectionStatusChanged(@NonNull DetectionStatus status) { public void onDetectionStatusChanged(@NonNull FaceDetectionStatus status) {
handleFaceAuthenticated(status.getUserId(), status.isStrongBiometric()); handleFaceAuthenticated(status.getUserId(), status.isStrongBiometric());
} }
}; };

View File

@@ -16,10 +16,10 @@
package com.android.systemui.bouncer.domain.interactor package com.android.systemui.bouncer.domain.interactor
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.time.SystemClock import com.android.systemui.util.time.SystemClock
@@ -35,8 +35,8 @@ constructor(
private val keyguardStateController: KeyguardStateController, private val keyguardStateController: KeyguardStateController,
private val bouncerRepository: KeyguardBouncerRepository, private val bouncerRepository: KeyguardBouncerRepository,
private val biometricSettingsRepository: BiometricSettingsRepository, private val biometricSettingsRepository: BiometricSettingsRepository,
private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
private val systemClock: SystemClock, private val systemClock: SystemClock,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
) { ) {
var receivedDownTouch = false var receivedDownTouch = false
val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
@@ -78,7 +78,7 @@ constructor(
biometricSettingsRepository.isFingerprintEnrolled.value && biometricSettingsRepository.isFingerprintEnrolled.value &&
biometricSettingsRepository.isStrongBiometricAllowed.value && biometricSettingsRepository.isStrongBiometricAllowed.value &&
biometricSettingsRepository.isFingerprintEnabledByDevicePolicy.value && biometricSettingsRepository.isFingerprintEnabledByDevicePolicy.value &&
!deviceEntryFingerprintAuthRepository.isLockedOut.value && !keyguardUpdateMonitor.isFingerprintLockedOut &&
!keyguardStateController.isUnlocked && !keyguardStateController.isUnlocked &&
!statusBarStateController.isDozing !statusBarStateController.isDozing
} }

View File

@@ -38,13 +38,13 @@ import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus import com.android.systemui.keyguard.shared.model.AcquiredFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.AuthenticationStatus import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.DetectionStatus import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
import com.android.systemui.keyguard.shared.model.FailedAuthenticationStatus import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.log.FaceAuthenticationLogger import com.android.systemui.log.FaceAuthenticationLogger
import com.android.systemui.log.SessionTracker import com.android.systemui.log.SessionTracker
@@ -88,10 +88,10 @@ interface DeviceEntryFaceAuthRepository {
val canRunFaceAuth: StateFlow<Boolean> val canRunFaceAuth: StateFlow<Boolean>
/** Provide the current status of face authentication. */ /** Provide the current status of face authentication. */
val authenticationStatus: Flow<AuthenticationStatus> val authenticationStatus: Flow<FaceAuthenticationStatus>
/** Provide the current status of face detection. */ /** Provide the current status of face detection. */
val detectionStatus: Flow<DetectionStatus> val detectionStatus: Flow<FaceDetectionStatus>
/** Current state of whether face authentication is locked out or not. */ /** Current state of whether face authentication is locked out or not. */
val isLockedOut: StateFlow<Boolean> val isLockedOut: StateFlow<Boolean>
@@ -151,13 +151,13 @@ constructor(
private var cancelNotReceivedHandlerJob: Job? = null private var cancelNotReceivedHandlerJob: Job? = null
private var halErrorRetryJob: Job? = null private var halErrorRetryJob: Job? = null
private val _authenticationStatus: MutableStateFlow<AuthenticationStatus?> = private val _authenticationStatus: MutableStateFlow<FaceAuthenticationStatus?> =
MutableStateFlow(null) MutableStateFlow(null)
override val authenticationStatus: Flow<AuthenticationStatus> override val authenticationStatus: Flow<FaceAuthenticationStatus>
get() = _authenticationStatus.filterNotNull() get() = _authenticationStatus.filterNotNull()
private val _detectionStatus = MutableStateFlow<DetectionStatus?>(null) private val _detectionStatus = MutableStateFlow<FaceDetectionStatus?>(null)
override val detectionStatus: Flow<DetectionStatus> override val detectionStatus: Flow<FaceDetectionStatus>
get() = _detectionStatus.filterNotNull() get() = _detectionStatus.filterNotNull()
private val _isLockedOut = MutableStateFlow(false) private val _isLockedOut = MutableStateFlow(false)
@@ -396,18 +396,18 @@ constructor(
private val faceAuthCallback = private val faceAuthCallback =
object : FaceManager.AuthenticationCallback() { object : FaceManager.AuthenticationCallback() {
override fun onAuthenticationFailed() { override fun onAuthenticationFailed() {
_authenticationStatus.value = FailedAuthenticationStatus _authenticationStatus.value = FailedFaceAuthenticationStatus
_isAuthenticated.value = false _isAuthenticated.value = false
faceAuthLogger.authenticationFailed() faceAuthLogger.authenticationFailed()
onFaceAuthRequestCompleted() onFaceAuthRequestCompleted()
} }
override fun onAuthenticationAcquired(acquireInfo: Int) { override fun onAuthenticationAcquired(acquireInfo: Int) {
_authenticationStatus.value = AcquiredAuthenticationStatus(acquireInfo) _authenticationStatus.value = AcquiredFaceAuthenticationStatus(acquireInfo)
} }
override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) { override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
val errorStatus = ErrorAuthenticationStatus(errorCode, errString.toString()) val errorStatus = ErrorFaceAuthenticationStatus(errorCode, errString.toString())
if (errorStatus.isLockoutError()) { if (errorStatus.isLockoutError()) {
_isLockedOut.value = true _isLockedOut.value = true
} }
@@ -433,11 +433,11 @@ constructor(
if (faceAcquiredInfoIgnoreList.contains(code)) { if (faceAcquiredInfoIgnoreList.contains(code)) {
return return
} }
_authenticationStatus.value = HelpAuthenticationStatus(code, helpStr.toString()) _authenticationStatus.value = HelpFaceAuthenticationStatus(code, helpStr.toString())
} }
override fun onAuthenticationSucceeded(result: FaceManager.AuthenticationResult) { override fun onAuthenticationSucceeded(result: FaceManager.AuthenticationResult) {
_authenticationStatus.value = SuccessAuthenticationStatus(result) _authenticationStatus.value = SuccessFaceAuthenticationStatus(result)
_isAuthenticated.value = true _isAuthenticated.value = true
faceAuthLogger.faceAuthSuccess(result) faceAuthLogger.faceAuthSuccess(result)
onFaceAuthRequestCompleted() onFaceAuthRequestCompleted()
@@ -482,7 +482,7 @@ constructor(
private val detectionCallback = private val detectionCallback =
FaceManager.FaceDetectionCallback { sensorId, userId, isStrong -> FaceManager.FaceDetectionCallback { sensorId, userId, isStrong ->
faceAuthLogger.faceDetected() faceAuthLogger.faceDetected()
_detectionStatus.value = DetectionStatus(sensorId, userId, isStrong) _detectionStatus.value = FaceDetectionStatus(sensorId, userId, isStrong)
} }
private var cancellationInProgress = false private var cancellationInProgress = false

View File

@@ -21,27 +21,29 @@ import android.hardware.biometrics.BiometricAuthenticator.Modality
import android.hardware.biometrics.BiometricSourceType import android.hardware.biometrics.BiometricSourceType
import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Dumpable
import com.android.systemui.biometrics.AuthController import com.android.systemui.biometrics.AuthController
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
import java.io.PrintWriter import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.FailedFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
/** Encapsulates state about device entry fingerprint auth mechanism. */ /** Encapsulates state about device entry fingerprint auth mechanism. */
interface DeviceEntryFingerprintAuthRepository { interface DeviceEntryFingerprintAuthRepository {
/** Whether the device entry fingerprint auth is locked out. */ /** Whether the device entry fingerprint auth is locked out. */
val isLockedOut: StateFlow<Boolean> val isLockedOut: Flow<Boolean>
/** /**
* Whether the fingerprint sensor is currently listening, this doesn't mean that the user is * Whether the fingerprint sensor is currently listening, this doesn't mean that the user is
@@ -53,6 +55,9 @@ interface DeviceEntryFingerprintAuthRepository {
* Fingerprint sensor type present on the device, null if fingerprint sensor is not available. * Fingerprint sensor type present on the device, null if fingerprint sensor is not available.
*/ */
val availableFpSensorType: Flow<BiometricType?> val availableFpSensorType: Flow<BiometricType?>
/** Provide the current status of fingerprint authentication. */
val authenticationStatus: Flow<FingerprintAuthenticationStatus>
} }
/** /**
@@ -69,16 +74,7 @@ constructor(
val authController: AuthController, val authController: AuthController,
val keyguardUpdateMonitor: KeyguardUpdateMonitor, val keyguardUpdateMonitor: KeyguardUpdateMonitor,
@Application scope: CoroutineScope, @Application scope: CoroutineScope,
dumpManager: DumpManager, ) : DeviceEntryFingerprintAuthRepository {
) : DeviceEntryFingerprintAuthRepository, Dumpable {
init {
dumpManager.registerDumpable(this)
}
override fun dump(pw: PrintWriter, args: Array<String?>) {
pw.println("isLockedOut=${isLockedOut.value}")
}
override val availableFpSensorType: Flow<BiometricType?> override val availableFpSensorType: Flow<BiometricType?>
get() { get() {
@@ -114,7 +110,7 @@ constructor(
else if (authController.isRearFpsSupported) BiometricType.REAR_FINGERPRINT else null else if (authController.isRearFpsSupported) BiometricType.REAR_FINGERPRINT else null
} }
override val isLockedOut: StateFlow<Boolean> = override val isLockedOut: Flow<Boolean> =
conflatedCallbackFlow { conflatedCallbackFlow {
val sendLockoutUpdate = val sendLockoutUpdate =
fun() { fun() {
@@ -138,7 +134,7 @@ constructor(
sendLockoutUpdate() sendLockoutUpdate()
awaitClose { keyguardUpdateMonitor.removeCallback(callback) } awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
} }
.stateIn(scope, started = SharingStarted.Eagerly, initialValue = false) .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
override val isRunning: Flow<Boolean> override val isRunning: Flow<Boolean>
get() = conflatedCallbackFlow { get() = conflatedCallbackFlow {
@@ -166,6 +162,93 @@ constructor(
awaitClose { keyguardUpdateMonitor.removeCallback(callback) } awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
} }
override val authenticationStatus: Flow<FingerprintAuthenticationStatus>
get() = conflatedCallbackFlow {
val callback =
object : KeyguardUpdateMonitorCallback() {
override fun onBiometricAuthenticated(
userId: Int,
biometricSourceType: BiometricSourceType,
isStrongBiometric: Boolean,
) {
sendUpdateIfFingerprint(
biometricSourceType,
SuccessFingerprintAuthenticationStatus(
userId,
isStrongBiometric,
),
)
}
override fun onBiometricError(
msgId: Int,
errString: String?,
biometricSourceType: BiometricSourceType,
) {
sendUpdateIfFingerprint(
biometricSourceType,
ErrorFingerprintAuthenticationStatus(
msgId,
errString,
),
)
}
override fun onBiometricHelp(
msgId: Int,
helpString: String?,
biometricSourceType: BiometricSourceType,
) {
sendUpdateIfFingerprint(
biometricSourceType,
HelpFingerprintAuthenticationStatus(
msgId,
helpString,
),
)
}
override fun onBiometricAuthFailed(
biometricSourceType: BiometricSourceType,
) {
sendUpdateIfFingerprint(
biometricSourceType,
FailedFingerprintAuthenticationStatus,
)
}
override fun onBiometricAcquired(
biometricSourceType: BiometricSourceType,
acquireInfo: Int,
) {
sendUpdateIfFingerprint(
biometricSourceType,
AcquiredFingerprintAuthenticationStatus(
acquireInfo,
),
)
}
private fun sendUpdateIfFingerprint(
biometricSourceType: BiometricSourceType,
authenticationStatus: FingerprintAuthenticationStatus
) {
if (biometricSourceType != BiometricSourceType.FINGERPRINT) {
return
}
trySendWithFailureLogging(
authenticationStatus,
TAG,
"new fingerprint authentication status"
)
}
}
keyguardUpdateMonitor.registerCallback(callback)
awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
}
companion object { companion object {
const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl" const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl"
} }

View File

@@ -18,8 +18,8 @@ package com.android.systemui.keyguard.data.repository
import com.android.keyguard.FaceAuthUiEvent import com.android.keyguard.FaceAuthUiEvent
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.AuthenticationStatus import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.DetectionStatus import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@@ -40,10 +40,10 @@ class NoopDeviceEntryFaceAuthRepository @Inject constructor() : DeviceEntryFaceA
private val _canRunFaceAuth = MutableStateFlow(false) private val _canRunFaceAuth = MutableStateFlow(false)
override val canRunFaceAuth: StateFlow<Boolean> = _canRunFaceAuth override val canRunFaceAuth: StateFlow<Boolean> = _canRunFaceAuth
override val authenticationStatus: Flow<AuthenticationStatus> override val authenticationStatus: Flow<FaceAuthenticationStatus>
get() = emptyFlow() get() = emptyFlow()
override val detectionStatus: Flow<DetectionStatus> override val detectionStatus: Flow<FaceDetectionStatus>
get() = emptyFlow() get() = emptyFlow()
private val _isLockedOut = MutableStateFlow(false) private val _isLockedOut = MutableStateFlow(false)

View File

@@ -16,8 +16,8 @@
package com.android.systemui.keyguard.domain.interactor package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.keyguard.shared.model.AuthenticationStatus import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.DetectionStatus import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
/** /**
@@ -27,10 +27,10 @@ import kotlinx.coroutines.flow.Flow
interface KeyguardFaceAuthInteractor { interface KeyguardFaceAuthInteractor {
/** Current authentication status */ /** Current authentication status */
val authenticationStatus: Flow<AuthenticationStatus> val authenticationStatus: Flow<FaceAuthenticationStatus>
/** Current detection status */ /** Current detection status */
val detectionStatus: Flow<DetectionStatus> val detectionStatus: Flow<FaceDetectionStatus>
/** Can face auth be run right now */ /** Can face auth be run right now */
fun canFaceAuthRun(): Boolean fun canFaceAuthRun(): Boolean
@@ -72,8 +72,8 @@ interface KeyguardFaceAuthInteractor {
*/ */
interface FaceAuthenticationListener { interface FaceAuthenticationListener {
/** Receive face authentication status updates */ /** Receive face authentication status updates */
fun onAuthenticationStatusChanged(status: AuthenticationStatus) fun onAuthenticationStatusChanged(status: FaceAuthenticationStatus)
/** Receive status updates whenever face detection runs */ /** Receive status updates whenever face detection runs */
fun onDetectionStatusChanged(status: DetectionStatus) fun onDetectionStatusChanged(status: FaceDetectionStatus)
} }

View File

@@ -17,8 +17,8 @@
package com.android.systemui.keyguard.domain.interactor package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.AuthenticationStatus import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.DetectionStatus import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.emptyFlow
@@ -31,9 +31,9 @@ import kotlinx.coroutines.flow.emptyFlow
*/ */
@SysUISingleton @SysUISingleton
class NoopKeyguardFaceAuthInteractor @Inject constructor() : KeyguardFaceAuthInteractor { class NoopKeyguardFaceAuthInteractor @Inject constructor() : KeyguardFaceAuthInteractor {
override val authenticationStatus: Flow<AuthenticationStatus> override val authenticationStatus: Flow<FaceAuthenticationStatus>
get() = emptyFlow() get() = emptyFlow()
override val detectionStatus: Flow<DetectionStatus> override val detectionStatus: Flow<FaceDetectionStatus>
get() = emptyFlow() get() = emptyFlow()
override fun canFaceAuthRun(): Boolean = false override fun canFaceAuthRun(): Boolean = false

View File

@@ -30,8 +30,8 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.shared.model.AuthenticationStatus import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.log.FaceAuthenticationLogger import com.android.systemui.log.FaceAuthenticationLogger
import com.android.systemui.util.kotlin.pairwise import com.android.systemui.util.kotlin.pairwise
@@ -165,10 +165,10 @@ constructor(
repository.cancel() repository.cancel()
} }
private val _authenticationStatusOverride = MutableStateFlow<AuthenticationStatus?>(null) private val faceAuthenticationStatusOverride = MutableStateFlow<FaceAuthenticationStatus?>(null)
/** Provide the status of face authentication */ /** Provide the status of face authentication */
override val authenticationStatus = override val authenticationStatus =
merge(_authenticationStatusOverride.filterNotNull(), repository.authenticationStatus) merge(faceAuthenticationStatusOverride.filterNotNull(), repository.authenticationStatus)
/** Provide the status of face detection */ /** Provide the status of face detection */
override val detectionStatus = repository.detectionStatus override val detectionStatus = repository.detectionStatus
@@ -176,13 +176,13 @@ constructor(
private fun runFaceAuth(uiEvent: FaceAuthUiEvent, fallbackToDetect: Boolean) { private fun runFaceAuth(uiEvent: FaceAuthUiEvent, fallbackToDetect: Boolean) {
if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) { if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) {
if (repository.isLockedOut.value) { if (repository.isLockedOut.value) {
_authenticationStatusOverride.value = faceAuthenticationStatusOverride.value =
ErrorAuthenticationStatus( ErrorFaceAuthenticationStatus(
BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT, BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT,
context.resources.getString(R.string.keyguard_face_unlock_unavailable) context.resources.getString(R.string.keyguard_face_unlock_unavailable)
) )
} else { } else {
_authenticationStatusOverride.value = null faceAuthenticationStatusOverride.value = null
applicationScope.launch { applicationScope.launch {
faceAuthenticationLogger.authRequested(uiEvent) faceAuthenticationLogger.authRequested(uiEvent)
repository.authenticate(uiEvent, fallbackToDetection = fallbackToDetect) repository.authenticate(uiEvent, fallbackToDetection = fallbackToDetect)

View File

@@ -23,33 +23,35 @@ import android.os.SystemClock.elapsedRealtime
* Authentication status provided by * Authentication status provided by
* [com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository] * [com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository]
*/ */
sealed class AuthenticationStatus sealed class FaceAuthenticationStatus
/** Success authentication status. */ /** Success authentication status. */
data class SuccessAuthenticationStatus(val successResult: FaceManager.AuthenticationResult) : data class SuccessFaceAuthenticationStatus(val successResult: FaceManager.AuthenticationResult) :
AuthenticationStatus() FaceAuthenticationStatus()
/** Face authentication help message. */ /** Face authentication help message. */
data class HelpAuthenticationStatus(val msgId: Int, val msg: String?) : AuthenticationStatus() data class HelpFaceAuthenticationStatus(val msgId: Int, val msg: String?) :
FaceAuthenticationStatus()
/** Face acquired message. */ /** Face acquired message. */
data class AcquiredAuthenticationStatus(val acquiredInfo: Int) : AuthenticationStatus() data class AcquiredFaceAuthenticationStatus(val acquiredInfo: Int) : FaceAuthenticationStatus()
/** Face authentication failed message. */ /** Face authentication failed message. */
object FailedAuthenticationStatus : AuthenticationStatus() object FailedFaceAuthenticationStatus : FaceAuthenticationStatus()
/** Face authentication error message */ /** Face authentication error message */
data class ErrorAuthenticationStatus( data class ErrorFaceAuthenticationStatus(
val msgId: Int, val msgId: Int,
val msg: String? = null, val msg: String? = null,
// present to break equality check if the same error occurs repeatedly. // present to break equality check if the same error occurs repeatedly.
val createdAt: Long = elapsedRealtime() val createdAt: Long = elapsedRealtime()
) : AuthenticationStatus() { ) : FaceAuthenticationStatus() {
/** /**
* Method that checks if [msgId] is a lockout error. A lockout error means that face * Method that checks if [msgId] is a lockout error. A lockout error means that face
* authentication is locked out. * authentication is locked out.
*/ */
fun isLockoutError() = msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT fun isLockoutError() =
msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT || msgId == FaceManager.FACE_ERROR_LOCKOUT
/** /**
* Method that checks if [msgId] is a cancellation error. This means that face authentication * Method that checks if [msgId] is a cancellation error. This means that face authentication
@@ -64,4 +66,4 @@ data class ErrorAuthenticationStatus(
} }
/** Face detection success message. */ /** Face detection success message. */
data class DetectionStatus(val sensorId: Int, val userId: Int, val isStrongBiometric: Boolean) data class FaceDetectionStatus(val sensorId: Int, val userId: Int, val isStrongBiometric: Boolean)

View File

@@ -0,0 +1,52 @@
/*
* 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.keyguard.shared.model
import android.os.SystemClock.elapsedRealtime
/**
* Fingerprint authentication status provided by
* [com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository]
*/
sealed class FingerprintAuthenticationStatus
/** Fingerprint authentication success status. */
data class SuccessFingerprintAuthenticationStatus(
val userId: Int,
val isStrongBiometric: Boolean,
) : FingerprintAuthenticationStatus()
/** Fingerprint authentication help message. */
data class HelpFingerprintAuthenticationStatus(
val msgId: Int,
val msg: String?,
) : FingerprintAuthenticationStatus()
/** Fingerprint acquired message. */
data class AcquiredFingerprintAuthenticationStatus(val acquiredInfo: Int) :
FingerprintAuthenticationStatus()
/** Fingerprint authentication failed message. */
object FailedFingerprintAuthenticationStatus : FingerprintAuthenticationStatus()
/** Fingerprint authentication error message */
data class ErrorFingerprintAuthenticationStatus(
val msgId: Int,
val msg: String? = null,
// present to break equality check if the same error occurs repeatedly.
val createdAt: Long = elapsedRealtime(),
) : FingerprintAuthenticationStatus()

View File

@@ -4,7 +4,7 @@ import android.hardware.face.FaceManager
import android.hardware.face.FaceSensorPropertiesInternal import android.hardware.face.FaceSensorPropertiesInternal
import com.android.keyguard.FaceAuthUiEvent import com.android.keyguard.FaceAuthUiEvent
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.log.core.LogLevel.DEBUG import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.FaceAuthLog import com.android.systemui.log.dagger.FaceAuthLog
@@ -240,7 +240,7 @@ constructor(
) )
} }
fun hardwareError(errorStatus: ErrorAuthenticationStatus) { fun hardwareError(errorStatus: ErrorFaceAuthenticationStatus) {
logBuffer.log( logBuffer.log(
TAG, TAG,
DEBUG, DEBUG,

View File

@@ -48,6 +48,7 @@ import android.view.WindowMetrics
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.airbnb.lottie.LottieAnimationView import com.airbnb.lottie.LottieAnimationView
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.RoboPilotTest import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
@@ -64,7 +65,6 @@ import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepositor
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.dump.DumpManager import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.concurrency.FakeExecutor
@@ -153,8 +153,8 @@ class SideFpsControllerTest : SysuiTestCase() {
mock(KeyguardStateController::class.java), mock(KeyguardStateController::class.java),
keyguardBouncerRepository, keyguardBouncerRepository,
FakeBiometricSettingsRepository(), FakeBiometricSettingsRepository(),
FakeDeviceEntryFingerprintAuthRepository(),
FakeSystemClock(), FakeSystemClock(),
mock(KeyguardUpdateMonitor::class.java),
) )
displayStateInteractor = displayStateInteractor =
DisplayStateInteractorImpl( DisplayStateInteractorImpl(

View File

@@ -34,7 +34,6 @@ import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -106,8 +105,8 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest :
mock(KeyguardStateController::class.java), mock(KeyguardStateController::class.java),
keyguardBouncerRepository, keyguardBouncerRepository,
mock(BiometricSettingsRepository::class.java), mock(BiometricSettingsRepository::class.java),
mock(DeviceEntryFingerprintAuthRepository::class.java),
mock(SystemClock::class.java), mock(SystemClock::class.java),
mKeyguardUpdateMonitor,
) )
return createUdfpsKeyguardViewController( return createUdfpsKeyguardViewController(
/* useModernBouncer */ true, /* useExpandedOverlay */ /* useModernBouncer */ true, /* useExpandedOverlay */

View File

@@ -18,6 +18,7 @@ package com.android.systemui.bouncer.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.RoboPilotTest import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
@@ -54,6 +55,7 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
@Mock private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var systemClock: SystemClock @Mock private lateinit var systemClock: SystemClock
@Mock private lateinit var bouncerLogger: TableLogBuffer @Mock private lateinit var bouncerLogger: TableLogBuffer
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Before @Before
fun setup() { fun setup() {
@@ -72,8 +74,8 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
keyguardStateController, keyguardStateController,
bouncerRepository, bouncerRepository,
biometricSettingsRepository, biometricSettingsRepository,
deviceEntryFingerprintAuthRepository,
systemClock, systemClock,
keyguardUpdateMonitor,
) )
} }
@@ -118,7 +120,7 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
@Test @Test
fun canShowAlternateBouncerForFingerprint_fingerprintLockedOut() { fun canShowAlternateBouncerForFingerprint_fingerprintLockedOut() {
givenCanShowAlternateBouncer() givenCanShowAlternateBouncer()
deviceEntryFingerprintAuthRepository.setLockedOut(true) whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(true)
assertFalse(underTest.canShowAlternateBouncerForFingerprint()) assertFalse(underTest.canShowAlternateBouncerForFingerprint())
} }
@@ -168,7 +170,7 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
biometricSettingsRepository.setFingerprintEnrolled(true) biometricSettingsRepository.setFingerprintEnrolled(true)
biometricSettingsRepository.setStrongBiometricAllowed(true) biometricSettingsRepository.setStrongBiometricAllowed(true)
biometricSettingsRepository.setFingerprintEnabledByDevicePolicy(true) biometricSettingsRepository.setFingerprintEnabledByDevicePolicy(true)
deviceEntryFingerprintAuthRepository.setLockedOut(false) whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
whenever(keyguardStateController.isUnlocked).thenReturn(false) whenever(keyguardStateController.isUnlocked).thenReturn(false)
} }

View File

@@ -36,6 +36,7 @@ import com.android.internal.logging.UiEventLogger
import com.android.keyguard.FaceAuthUiEvent import com.android.keyguard.FaceAuthUiEvent
import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.RoboPilotTest import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
@@ -50,12 +51,12 @@ import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.AuthenticationStatus import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.DetectionStatus import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.shared.model.WakeSleepReason import com.android.systemui.keyguard.shared.model.WakeSleepReason
@@ -113,6 +114,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
@Mock private lateinit var sessionTracker: SessionTracker @Mock private lateinit var sessionTracker: SessionTracker
@Mock private lateinit var uiEventLogger: UiEventLogger @Mock private lateinit var uiEventLogger: UiEventLogger
@Mock private lateinit var dumpManager: DumpManager @Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Captor @Captor
private lateinit var authenticationCallback: ArgumentCaptor<FaceManager.AuthenticationCallback> private lateinit var authenticationCallback: ArgumentCaptor<FaceManager.AuthenticationCallback>
@@ -131,8 +133,8 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
private lateinit var testScope: TestScope private lateinit var testScope: TestScope
private lateinit var fakeUserRepository: FakeUserRepository private lateinit var fakeUserRepository: FakeUserRepository
private lateinit var authStatus: FlowValue<AuthenticationStatus?> private lateinit var authStatus: FlowValue<FaceAuthenticationStatus?>
private lateinit var detectStatus: FlowValue<DetectionStatus?> private lateinit var detectStatus: FlowValue<FaceDetectionStatus?>
private lateinit var authRunning: FlowValue<Boolean?> private lateinit var authRunning: FlowValue<Boolean?>
private lateinit var bypassEnabled: FlowValue<Boolean?> private lateinit var bypassEnabled: FlowValue<Boolean?>
private lateinit var lockedOut: FlowValue<Boolean?> private lateinit var lockedOut: FlowValue<Boolean?>
@@ -175,10 +177,10 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
AlternateBouncerInteractor( AlternateBouncerInteractor(
bouncerRepository = bouncerRepository, bouncerRepository = bouncerRepository,
biometricSettingsRepository = biometricSettingsRepository, biometricSettingsRepository = biometricSettingsRepository,
deviceEntryFingerprintAuthRepository = deviceEntryFingerprintAuthRepository,
systemClock = mock(SystemClock::class.java), systemClock = mock(SystemClock::class.java),
keyguardStateController = FakeKeyguardStateController(), keyguardStateController = FakeKeyguardStateController(),
statusBarStateController = mock(StatusBarStateController::class.java), statusBarStateController = mock(StatusBarStateController::class.java),
keyguardUpdateMonitor = keyguardUpdateMonitor,
) )
bypassStateChangedListener = bypassStateChangedListener =
@@ -262,7 +264,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
val successResult = successResult() val successResult = successResult()
authenticationCallback.value.onAuthenticationSucceeded(successResult) authenticationCallback.value.onAuthenticationSucceeded(successResult)
assertThat(authStatus()).isEqualTo(SuccessAuthenticationStatus(successResult)) assertThat(authStatus()).isEqualTo(SuccessFaceAuthenticationStatus(successResult))
assertThat(authenticated()).isTrue() assertThat(authenticated()).isTrue()
assertThat(authRunning()).isFalse() assertThat(authRunning()).isFalse()
assertThat(canFaceAuthRun()).isFalse() assertThat(canFaceAuthRun()).isFalse()
@@ -383,7 +385,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
detectionCallback.value.onFaceDetected(1, 1, true) detectionCallback.value.onFaceDetected(1, 1, true)
assertThat(detectStatus()).isEqualTo(DetectionStatus(1, 1, true)) assertThat(detectStatus()).isEqualTo(FaceDetectionStatus(1, 1, true))
} }
@Test @Test
@@ -423,7 +425,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
FACE_ERROR_CANCELED, FACE_ERROR_CANCELED,
"First auth attempt cancellation completed" "First auth attempt cancellation completed"
) )
val value = authStatus() as ErrorAuthenticationStatus val value = authStatus() as ErrorFaceAuthenticationStatus
assertThat(value.msgId).isEqualTo(FACE_ERROR_CANCELED) assertThat(value.msgId).isEqualTo(FACE_ERROR_CANCELED)
assertThat(value.msg).isEqualTo("First auth attempt cancellation completed") assertThat(value.msg).isEqualTo("First auth attempt cancellation completed")
@@ -465,7 +467,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
authenticationCallback.value.onAuthenticationHelp(10, "Ignored help msg") authenticationCallback.value.onAuthenticationHelp(10, "Ignored help msg")
authenticationCallback.value.onAuthenticationHelp(11, "Ignored help msg") authenticationCallback.value.onAuthenticationHelp(11, "Ignored help msg")
assertThat(authStatus()).isEqualTo(HelpAuthenticationStatus(9, "help msg")) assertThat(authStatus()).isEqualTo(HelpFaceAuthenticationStatus(9, "help msg"))
} }
@Test @Test

View File

@@ -26,7 +26,11 @@ import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController import com.android.systemui.biometrics.AuthController
import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.FailedFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.util.mockito.whenever import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -49,7 +53,6 @@ import org.mockito.MockitoAnnotations
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() { class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var authController: AuthController @Mock private lateinit var authController: AuthController
@Captor @Captor
private lateinit var updateMonitorCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback> private lateinit var updateMonitorCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback>
@@ -68,7 +71,6 @@ class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
authController, authController,
keyguardUpdateMonitor, keyguardUpdateMonitor,
testScope.backgroundScope, testScope.backgroundScope,
dumpManager,
) )
} }
@@ -177,4 +179,129 @@ class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
callback.value.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT) callback.value.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT)
assertThat(availableFpSensorType()).isEqualTo(BiometricType.UNDER_DISPLAY_FINGERPRINT) assertThat(availableFpSensorType()).isEqualTo(BiometricType.UNDER_DISPLAY_FINGERPRINT)
} }
@Test
fun onFingerprintSuccess_successAuthenticationStatus() =
testScope.runTest {
val authenticationStatus by collectLastValue(underTest.authenticationStatus)
runCurrent()
verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
updateMonitorCallback.value.onBiometricAuthenticated(
0,
BiometricSourceType.FINGERPRINT,
true,
)
val status = authenticationStatus as SuccessFingerprintAuthenticationStatus
assertThat(status.userId).isEqualTo(0)
assertThat(status.isStrongBiometric).isEqualTo(true)
}
@Test
fun onFingerprintFailed_failedAuthenticationStatus() =
testScope.runTest {
val authenticationStatus by collectLastValue(underTest.authenticationStatus)
runCurrent()
verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
updateMonitorCallback.value.onBiometricAuthFailed(
BiometricSourceType.FINGERPRINT,
)
assertThat(authenticationStatus)
.isInstanceOf(FailedFingerprintAuthenticationStatus::class.java)
}
@Test
fun onFingerprintError_errorAuthenticationStatus() =
testScope.runTest {
val authenticationStatus by collectLastValue(underTest.authenticationStatus)
runCurrent()
verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
updateMonitorCallback.value.onBiometricError(
1,
"test_string",
BiometricSourceType.FINGERPRINT,
)
val status = authenticationStatus as ErrorFingerprintAuthenticationStatus
assertThat(status.msgId).isEqualTo(1)
assertThat(status.msg).isEqualTo("test_string")
}
@Test
fun onFingerprintHelp_helpAuthenticationStatus() =
testScope.runTest {
val authenticationStatus by collectLastValue(underTest.authenticationStatus)
runCurrent()
verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
updateMonitorCallback.value.onBiometricHelp(
1,
"test_string",
BiometricSourceType.FINGERPRINT,
)
val status = authenticationStatus as HelpFingerprintAuthenticationStatus
assertThat(status.msgId).isEqualTo(1)
assertThat(status.msg).isEqualTo("test_string")
}
@Test
fun onFingerprintAcquired_acquiredAuthenticationStatus() =
testScope.runTest {
val authenticationStatus by collectLastValue(underTest.authenticationStatus)
runCurrent()
verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
updateMonitorCallback.value.onBiometricAcquired(
BiometricSourceType.FINGERPRINT,
5,
)
val status = authenticationStatus as AcquiredFingerprintAuthenticationStatus
assertThat(status.acquiredInfo).isEqualTo(5)
}
@Test
fun onFaceCallbacks_fingerprintAuthenticationStatusIsUnchanged() =
testScope.runTest {
val authenticationStatus by collectLastValue(underTest.authenticationStatus)
runCurrent()
verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
updateMonitorCallback.value.onBiometricAuthenticated(
0,
BiometricSourceType.FACE,
true,
)
assertThat(authenticationStatus).isNull()
updateMonitorCallback.value.onBiometricAuthFailed(
BiometricSourceType.FACE,
)
assertThat(authenticationStatus).isNull()
updateMonitorCallback.value.onBiometricHelp(
1,
"test_string",
BiometricSourceType.FACE,
)
assertThat(authenticationStatus).isNull()
updateMonitorCallback.value.onBiometricAcquired(
BiometricSourceType.FACE,
5,
)
assertThat(authenticationStatus).isNull()
updateMonitorCallback.value.onBiometricError(
1,
"test_string",
BiometricSourceType.FACE,
)
assertThat(authenticationStatus).isNull()
}
} }

View File

@@ -38,10 +38,9 @@ import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -121,8 +120,8 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
mock(KeyguardStateController::class.java), mock(KeyguardStateController::class.java),
bouncerRepository, bouncerRepository,
mock(BiometricSettingsRepository::class.java), mock(BiometricSettingsRepository::class.java),
FakeDeviceEntryFingerprintAuthRepository(),
FakeSystemClock(), FakeSystemClock(),
keyguardUpdateMonitor,
), ),
keyguardTransitionInteractor, keyguardTransitionInteractor,
featureFlags, featureFlags,
@@ -160,7 +159,7 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
underTest.onDeviceLifted() underTest.onDeviceLifted()
val outputValue = authenticationStatus()!! as ErrorAuthenticationStatus val outputValue = authenticationStatus()!! as ErrorFaceAuthenticationStatus
assertThat(outputValue.msgId) assertThat(outputValue.msgId)
.isEqualTo(BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT) .isEqualTo(BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT)
assertThat(outputValue.msg).isEqualTo("Face Unlock unavailable") assertThat(outputValue.msg).isEqualTo("Face Unlock unavailable")

View File

@@ -17,8 +17,8 @@
package com.android.systemui.keyguard.data.repository package com.android.systemui.keyguard.data.repository
import com.android.keyguard.FaceAuthUiEvent import com.android.keyguard.FaceAuthUiEvent
import com.android.systemui.keyguard.shared.model.AuthenticationStatus import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.DetectionStatus import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@@ -29,16 +29,16 @@ class FakeDeviceEntryFaceAuthRepository : DeviceEntryFaceAuthRepository {
override val isAuthenticated = MutableStateFlow(false) override val isAuthenticated = MutableStateFlow(false)
override val canRunFaceAuth = MutableStateFlow(false) override val canRunFaceAuth = MutableStateFlow(false)
private val _authenticationStatus = MutableStateFlow<AuthenticationStatus?>(null) private val _authenticationStatus = MutableStateFlow<FaceAuthenticationStatus?>(null)
override val authenticationStatus: Flow<AuthenticationStatus> = override val authenticationStatus: Flow<FaceAuthenticationStatus> =
_authenticationStatus.filterNotNull() _authenticationStatus.filterNotNull()
fun setAuthenticationStatus(status: AuthenticationStatus) { fun setAuthenticationStatus(status: FaceAuthenticationStatus) {
_authenticationStatus.value = status _authenticationStatus.value = status
} }
private val _detectionStatus = MutableStateFlow<DetectionStatus?>(null) private val _detectionStatus = MutableStateFlow<FaceDetectionStatus?>(null)
override val detectionStatus: Flow<DetectionStatus> override val detectionStatus: Flow<FaceDetectionStatus>
get() = _detectionStatus.filterNotNull() get() = _detectionStatus.filterNotNull()
fun setDetectionStatus(status: DetectionStatus) { fun setDetectionStatus(status: FaceDetectionStatus) {
_detectionStatus.value = status _detectionStatus.value = status
} }

View File

@@ -17,32 +17,38 @@
package com.android.systemui.keyguard.data.repository package com.android.systemui.keyguard.data.repository
import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatus
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filterNotNull
class FakeDeviceEntryFingerprintAuthRepository : DeviceEntryFingerprintAuthRepository { class FakeDeviceEntryFingerprintAuthRepository : DeviceEntryFingerprintAuthRepository {
private val _isLockedOut = MutableStateFlow(false) private val _isLockedOut = MutableStateFlow(false)
override val isLockedOut: StateFlow<Boolean> = _isLockedOut.asStateFlow() override val isLockedOut: StateFlow<Boolean> = _isLockedOut.asStateFlow()
private val _isRunning = MutableStateFlow(false)
override val isRunning: Flow<Boolean>
get() = _isRunning
private var fpSensorType = MutableStateFlow<BiometricType?>(null)
override val availableFpSensorType: Flow<BiometricType?>
get() = fpSensorType
fun setLockedOut(lockedOut: Boolean) { fun setLockedOut(lockedOut: Boolean) {
_isLockedOut.value = lockedOut _isLockedOut.value = lockedOut
} }
private val _isRunning = MutableStateFlow(false)
override val isRunning: Flow<Boolean>
get() = _isRunning
fun setIsRunning(value: Boolean) { fun setIsRunning(value: Boolean) {
_isRunning.value = value _isRunning.value = value
} }
private var fpSensorType = MutableStateFlow<BiometricType?>(null)
override val availableFpSensorType: Flow<BiometricType?>
get() = fpSensorType
fun setAvailableFpSensorType(value: BiometricType?) { fun setAvailableFpSensorType(value: BiometricType?) {
fpSensorType.value = value fpSensorType.value = value
} }
private var _authenticationStatus = MutableStateFlow<FingerprintAuthenticationStatus?>(null)
override val authenticationStatus: Flow<FingerprintAuthenticationStatus>
get() = _authenticationStatus.filterNotNull()
fun setAuthenticationStatus(status: FingerprintAuthenticationStatus) {
_authenticationStatus.value = status
}
} }