Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 78161225 authored by Chandru S's avatar Chandru S Committed by Android (Google) Code Review
Browse files

Merge "Use a single combine function to combine all boolean flows instead of...

Merge "Use a single combine function to combine all boolean flows instead of nested combines" into main
parents e90bb572 929f4b7f
Loading
Loading
Loading
Loading
+102 −115
Original line number Diff line number Diff line
@@ -50,7 +50,6 @@ import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.log.FaceAuthenticationLogger
import com.android.systemui.log.SessionTracker
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.UserRepository
@@ -66,9 +65,10 @@ import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOf
@@ -77,6 +77,7 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

@@ -153,7 +154,7 @@ constructor(
    private val faceAuthLogger: FaceAuthenticationLogger,
    private val biometricSettingsRepository: BiometricSettingsRepository,
    private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
    private val trustRepository: TrustRepository,
    trustRepository: TrustRepository,
    private val keyguardRepository: KeyguardRepository,
    private val keyguardInteractor: KeyguardInteractor,
    private val alternateBouncerInteractor: AlternateBouncerInteractor,
@@ -202,11 +203,9 @@ constructor(
    private val keyguardSessionId: InstanceId?
        get() = sessionTracker.getSessionId(StatusBarManager.SESSION_KEYGUARD)

    private val _canRunFaceAuth = MutableStateFlow(false)
    override val canRunFaceAuth: StateFlow<Boolean>
        get() = _canRunFaceAuth

    private val canRunDetection = MutableStateFlow(false)
    private val canRunDetection: StateFlow<Boolean>

    private val _isAuthenticated = MutableStateFlow(false)
    override val isAuthenticated: Flow<Boolean>
@@ -252,10 +251,58 @@ constructor(
        dumpManager.registerCriticalDumpable("DeviceEntryFaceAuthRepositoryImpl", this)

        if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) {
            canRunFaceAuth =
                listOf(
                        *gatingConditionsForAuthAndDetect(),
                        Pair(isLockedOut.isFalse(), "isNotInLockOutState"),
                        Pair(
                            trustRepository.isCurrentUserTrusted.isFalse(),
                            "currentUserIsNotTrusted"
                        ),
                        Pair(
                            biometricSettingsRepository.isFaceAuthCurrentlyAllowed,
                            "isFaceAuthCurrentlyAllowed"
                        ),
                        Pair(isAuthenticated.isFalse(), "faceNotAuthenticated"),
                    )
                    .andAllFlows("canFaceAuthRun", faceAuthLog)
                    .flowOn(mainDispatcher)
                    .stateIn(applicationScope, SharingStarted.Eagerly, false)

            // Face detection can run only when lockscreen bypass is enabled
            // & detection is supported
            //   & biometric unlock is not allowed
            //     or user is trusted by trust manager & we want to run face detect to dismiss
            // keyguard
            canRunDetection =
                listOf(
                        *gatingConditionsForAuthAndDetect(),
                        Pair(isBypassEnabled, "isBypassEnabled"),
                        Pair(
                            biometricSettingsRepository.isFaceAuthCurrentlyAllowed
                                .isFalse()
                                .or(trustRepository.isCurrentUserTrusted),
                            "faceAuthIsNotCurrentlyAllowedOrCurrentUserIsTrusted"
                        ),
                        // We don't want to run face detect if fingerprint can be used to unlock the
                        // device
                        // but it's not possible to authenticate with FP from the bouncer (UDFPS)
                        Pair(
                            and(isUdfps(), deviceEntryFingerprintAuthRepository.isRunning)
                                .isFalse(),
                            "udfpsAuthIsNotPossibleAnymore"
                        )
                    )
                    .andAllFlows("canFaceDetectRun", faceDetectLog)
                    .flowOn(mainDispatcher)
                    .stateIn(applicationScope, SharingStarted.Eagerly, false)
            observeFaceAuthGatingChecks()
            observeFaceDetectGatingChecks()
            observeFaceAuthResettingConditions()
            listenForSchedulingWatchdog()
        } else {
            canRunFaceAuth = MutableStateFlow(false).asStateFlow()
            canRunDetection = MutableStateFlow(false).asStateFlow()
        }
    }

@@ -298,39 +345,13 @@ constructor(
    }

    private fun observeFaceDetectGatingChecks() {
        // Face detection can run only when lockscreen bypass is enabled
        // & detection is supported
        //   & biometric unlock is not allowed
        //     or user is trusted by trust manager & we want to run face detect to dismiss keyguard
        listOf(
                canFaceAuthOrDetectRun(faceDetectLog),
                logAndObserve(isBypassEnabled, "isBypassEnabled", faceDetectLog),
                logAndObserve(
                    biometricSettingsRepository.isFaceAuthCurrentlyAllowed
                        .isFalse()
                        .or(trustRepository.isCurrentUserTrusted),
                    "faceAuthIsNotCurrentlyAllowedOrCurrentUserIsTrusted",
                    faceDetectLog
                ),
                // We don't want to run face detect if fingerprint can be used to unlock the device
                // but it's not possible to authenticate with FP from the bouncer (UDFPS)
                logAndObserve(
                    and(isUdfps(), deviceEntryFingerprintAuthRepository.isRunning).isFalse(),
                    "udfpsAuthIsNotPossibleAnymore",
                    faceDetectLog
                )
            )
            .reduce(::and)
            .distinctUntilChanged()
        canRunDetection
            .onEach {
                faceAuthLogger.canRunDetectionChanged(it)
                canRunDetection.value = it
                if (!it) {
                    cancelDetection()
                }
            }
            .flowOn(mainDispatcher)
            .logDiffsForTable(faceDetectLog, "", "canFaceDetectRun", false)
            .launchIn(applicationScope)
    }

@@ -339,25 +360,19 @@ constructor(
            it == BiometricType.UNDER_DISPLAY_FINGERPRINT
        }

    private fun canFaceAuthOrDetectRun(tableLogBuffer: TableLogBuffer): Flow<Boolean> {
        return listOf(
                logAndObserve(
    private fun gatingConditionsForAuthAndDetect(): Array<Pair<Flow<Boolean>, String>> {
        return arrayOf(
            Pair(
                biometricSettingsRepository.isFaceAuthEnrolledAndEnabled,
                    "isFaceAuthEnrolledAndEnabled",
                    tableLogBuffer
                ),
                logAndObserve(faceAuthPaused.isFalse(), "faceAuthIsNotPaused", tableLogBuffer),
                logAndObserve(
                    keyguardRepository.isKeyguardGoingAway.isFalse(),
                    "keyguardNotGoingAway",
                    tableLogBuffer
                "isFaceAuthEnrolledAndEnabled"
            ),
                logAndObserve(
            Pair(faceAuthPaused.isFalse(), "faceAuthIsNotPaused"),
            Pair(keyguardRepository.isKeyguardGoingAway.isFalse(), "keyguardNotGoingAway"),
            Pair(
                keyguardRepository.wakefulness.map { it.isStartingToSleep() }.isFalse(),
                    "deviceNotStartingToSleep",
                    tableLogBuffer
                "deviceNotStartingToSleep"
            ),
                logAndObserve(
            Pair(
                keyguardInteractor.isSecureCameraActive
                    .isFalse()
                    .or(
@@ -365,50 +380,24 @@ constructor(
                            keyguardInteractor.primaryBouncerShowing
                        )
                    ),
                    "secureCameraNotActiveOrAnyBouncerIsShowing",
                    tableLogBuffer
                "secureCameraNotActiveOrAnyBouncerIsShowing"
            ),
                logAndObserve(
            Pair(
                biometricSettingsRepository.isFaceAuthSupportedInCurrentPosture,
                    "isFaceAuthSupportedInCurrentPosture",
                    tableLogBuffer
                "isFaceAuthSupportedInCurrentPosture"
            ),
                logAndObserve(
            Pair(
                biometricSettingsRepository.isCurrentUserInLockdown.isFalse(),
                    "userHasNotLockedDownDevice",
                    tableLogBuffer
                "userHasNotLockedDownDevice"
            ),
                logAndObserve(
                    keyguardRepository.isKeyguardShowing,
                    "isKeyguardShowing",
                    tableLogBuffer
                )
            Pair(keyguardRepository.isKeyguardShowing, "isKeyguardShowing")
        )
            .reduce(::and)
    }

    private fun observeFaceAuthGatingChecks() {
        // Face auth can run only if all of the gating conditions are true.
        listOf(
                canFaceAuthOrDetectRun(faceAuthLog),
                logAndObserve(isLockedOut.isFalse(), "isNotInLockOutState", faceAuthLog),
                logAndObserve(
                    trustRepository.isCurrentUserTrusted.isFalse(),
                    "currentUserIsNotTrusted",
                    faceAuthLog
                ),
                logAndObserve(
                    biometricSettingsRepository.isFaceAuthCurrentlyAllowed,
                    "isFaceAuthCurrentlyAllowed",
                    faceAuthLog
                ),
                logAndObserve(isAuthenticated.isFalse(), "faceNotAuthenticated", faceAuthLog),
            )
            .reduce(::and)
            .distinctUntilChanged()
        canRunFaceAuth
            .onEach {
                faceAuthLogger.canFaceAuthRunChanged(it)
                _canRunFaceAuth.value = it
                if (!it) {
                    // Cancel currently running auth if any of the gating checks are false.
                    faceAuthLogger.cancellingFaceAuth()
@@ -416,7 +405,6 @@ constructor(
                }
            }
            .flowOn(mainDispatcher)
            .logDiffsForTable(faceAuthLog, "", "canFaceAuthRun", false)
            .launchIn(applicationScope)
    }

@@ -618,22 +606,6 @@ constructor(
        _isAuthRunning.value = false
    }

    private fun logAndObserve(
        cond: Flow<Boolean>,
        conditionName: String,
        logBuffer: TableLogBuffer
    ): Flow<Boolean> {
        return cond
            .distinctUntilChanged()
            .logDiffsForTable(
                logBuffer,
                columnName = conditionName,
                columnPrefix = "",
                initialValue = false
            )
            .onEach { faceAuthLogger.observedConditionChanged(it, conditionName) }
    }

    companion object {
        const val TAG = "DeviceEntryFaceAuthRepository"

@@ -688,3 +660,18 @@ private fun Flow<Boolean>.or(anotherFlow: Flow<Boolean>) =
private fun Flow<Boolean>.isFalse(): Flow<Boolean> {
    return this.map { !it }
}

private fun List<Pair<Flow<Boolean>, String>>.andAllFlows(
    combinedLoggingInfo: String,
    tableLogBuffer: TableLogBuffer
): Flow<Boolean> {
    return combine(this.map { it.first }) {
        val combinedValue =
            it.reduceIndexed { index, accumulator, current ->
                tableLogBuffer.logChange(prefix = "", columnName = this[index].second, current)
                return@reduceIndexed accumulator && current
            }
        tableLogBuffer.logChange(prefix = "", combinedLoggingInfo, combinedValue)
        return@combine combinedValue
    }
}