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

Commit 7f1d3ca5 authored by Chandru S's avatar Chandru S Committed by Automerger Merge Worker
Browse files

Add face auth triggers in FaceAuthInteractor am: f96f724e

parents 1c029098 f96f724e
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -75,6 +75,7 @@ import com.android.systemui.classifier.FalsingA11yDelegate;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
@@ -115,6 +116,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
    private final SessionTracker mSessionTracker;
    private final Optional<SideFpsController> mSideFpsController;
    private final FalsingA11yDelegate mFalsingA11yDelegate;
    private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
    private int mTranslationY;
    // Whether the volume keys should be handled by keyguard. If true, then
    // they will be handled here for specific media types such as music, otherwise
@@ -300,6 +302,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
        @Override
        public void onSwipeUp() {
            if (!mUpdateMonitor.isFaceDetectionRunning()) {
                mKeyguardFaceAuthInteractor.onSwipeUpOnBouncer();
                boolean didFaceAuthRun = mUpdateMonitor.requestFaceAuth(
                        FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER);
                mKeyguardSecurityCallback.userActivity();
@@ -389,7 +392,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
            FalsingA11yDelegate falsingA11yDelegate,
            TelephonyManager telephonyManager,
            ViewMediatorCallback viewMediatorCallback,
            AudioManager audioManager
            AudioManager audioManager,
            KeyguardFaceAuthInteractor keyguardFaceAuthInteractor
    ) {
        super(view);
        mLockPatternUtils = lockPatternUtils;
@@ -414,6 +418,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
        mTelephonyManager = telephonyManager;
        mViewMediatorCallback = viewMediatorCallback;
        mAudioManager = audioManager;
        mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
    }

    @Override
+6 −1
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
@@ -151,6 +152,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
    @NonNull private final DumpManager mDumpManager;
    @NonNull private final SystemUIDialogManager mDialogManager;
    @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
    @NonNull private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
    @NonNull private final VibratorHelper mVibrator;
    @NonNull private final FeatureFlags mFeatureFlags;
    @NonNull private final FalsingManager mFalsingManager;
@@ -818,7 +820,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
            @NonNull AlternateBouncerInteractor alternateBouncerInteractor,
            @NonNull SecureSettings secureSettings,
            @NonNull InputManager inputManager,
            @NonNull UdfpsUtils udfpsUtils) {
            @NonNull UdfpsUtils udfpsUtils,
            @NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) {
        mContext = context;
        mExecution = execution;
        mVibrator = vibrator;
@@ -882,6 +885,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
                    }
                    return Unit.INSTANCE;
                });
        mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;

        final UdfpsOverlayController mUdfpsOverlayController = new UdfpsOverlayController();
        mFingerprintManager.setUdfpsOverlayController(mUdfpsOverlayController);
@@ -1141,6 +1145,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
        if (!mOnFingerDown) {
            playStartHaptic();

            mKeyguardFaceAuthInteractor.onUdfpsSensorTouched();
            if (!mKeyguardUpdateMonitor.isFaceDetectionRunning()) {
                mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
            }
+35 −0
Original line number Diff line number Diff line
/*
 *   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.dagger

import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.keyguard.domain.interactor.NoopKeyguardFaceAuthInteractor
import dagger.Binds
import dagger.Module

/**
 * Module that provides bindings for face auth classes that are injected into SysUI components that
 * are used across different SysUI variants, where face auth is not supported.
 *
 * Some variants that do not support face authentication can install this module to provide a no-op
 * implementation of the interactor.
 */
@Module
interface KeyguardFaceAuthNotSupportedModule {
    @Binds
    fun keyguardFaceAuthInteractor(impl: NoopKeyguardFaceAuthInteractor): KeyguardFaceAuthInteractor
}
+8 −2
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ package com.android.systemui.keyguard.data.repository
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.keyguard.domain.interactor.SystemUIKeyguardFaceAuthInteractor
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.TableLogBufferFactory
import dagger.Binds
@@ -37,8 +38,13 @@ interface KeyguardFaceAuthModule {

    @Binds
    @IntoMap
    @ClassKey(KeyguardFaceAuthInteractor::class)
    fun bind(impl: KeyguardFaceAuthInteractor): CoreStartable
    @ClassKey(SystemUIKeyguardFaceAuthInteractor::class)
    fun bind(impl: SystemUIKeyguardFaceAuthInteractor): CoreStartable

    @Binds
    fun keyguardFaceAuthInteractor(
        impl: SystemUIKeyguardFaceAuthInteractor
    ): KeyguardFaceAuthInteractor

    @Binds fun trustRepository(impl: TrustRepositoryImpl): TrustRepository

+28 −168
Original line number Diff line number Diff line
@@ -16,181 +16,49 @@

package com.android.systemui.keyguard.domain.interactor

import com.android.keyguard.FaceAuthUiEvent
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.shared.model.AuthenticationStatus
import com.android.systemui.keyguard.shared.model.DetectionStatus
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.log.FaceAuthenticationLogger
import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch

/**
 * Encapsulates business logic related face authentication being triggered for device entry from
 * keyguard
 * Interactor that exposes API to get the face authentication status and handle any events that can
 * cause face authentication to run.
 */
@SysUISingleton
class KeyguardFaceAuthInteractor
@Inject
constructor(
    @Application private val applicationScope: CoroutineScope,
    @Main private val mainDispatcher: CoroutineDispatcher,
    private val repository: DeviceEntryFaceAuthRepository,
    private val primaryBouncerInteractor: PrimaryBouncerInteractor,
    private val alternateBouncerInteractor: AlternateBouncerInteractor,
    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
    private val featureFlags: FeatureFlags,
    private val faceAuthenticationLogger: FaceAuthenticationLogger,
    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
) : CoreStartable {
interface KeyguardFaceAuthInteractor {

    private val listeners: MutableList<FaceAuthenticationListener> = mutableListOf()
    /** Current authentication status */
    val authenticationStatus: Flow<AuthenticationStatus>

    override fun start() {
        if (!isEnabled()) {
            return
        }
        // This is required because fingerprint state required for the face auth repository is
        // backed by KeyguardUpdateMonitor. KeyguardUpdateMonitor constructor accesses the biometric
        // state which makes lazy injection not an option.
        keyguardUpdateMonitor.setFaceAuthInteractor(this)
        observeFaceAuthStateUpdates()
        faceAuthenticationLogger.interactorStarted()
        primaryBouncerInteractor.isShowing
            .whenItFlipsToTrue()
            .onEach {
                faceAuthenticationLogger.bouncerVisibilityChanged()
                runFaceAuth(
                    FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN,
                    fallbackToDetect = true
                )
            }
            .launchIn(applicationScope)
    /** Current detection status */
    val detectionStatus: Flow<DetectionStatus>

        alternateBouncerInteractor.isVisible
            .whenItFlipsToTrue()
            .onEach {
                faceAuthenticationLogger.alternateBouncerVisibilityChanged()
                runFaceAuth(
                    FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN,
                    fallbackToDetect = false
                )
            }
            .launchIn(applicationScope)

        merge(
                keyguardTransitionInteractor.aodToLockscreenTransition,
                keyguardTransitionInteractor.offToLockscreenTransition,
                keyguardTransitionInteractor.dozingToLockscreenTransition
            )
            .filter { it.transitionState == TransitionState.STARTED }
            .onEach {
                faceAuthenticationLogger.lockscreenBecameVisible(it)
                runFaceAuth(
                    FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED,
                    fallbackToDetect = true
                )
            }
            .launchIn(applicationScope)
    }
    /** Can face auth be run right now */
    fun canFaceAuthRun(): Boolean

    private fun runFaceAuth(uiEvent: FaceAuthUiEvent, fallbackToDetect: Boolean) {
        if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) {
            applicationScope.launch {
                faceAuthenticationLogger.authRequested(uiEvent)
                repository.authenticate(uiEvent, fallbackToDetection = fallbackToDetect)
            }
        } else {
            faceAuthenticationLogger.ignoredFaceAuthTrigger(
                uiEvent,
                ignoredReason = "Skipping face auth request because feature flag is false"
            )
        }
    }
    /** Whether face auth is currently running or not. */
    fun isRunning(): Boolean

    fun onSwipeUpOnBouncer() {
        runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, false)
    }
    /** Whether face auth is in lock out state. */
    fun isLockedOut(): Boolean

    fun onNotificationPanelClicked() {
        runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED, true)
    }

    fun onQsExpansionStared() {
        runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, true)
    }

    fun onDeviceLifted() {
        runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_PICK_UP_GESTURE_TRIGGERED, true)
    }

    fun onAssistantTriggeredOnLockScreen() {
        runFaceAuth(FaceAuthUiEvent.FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED, true)
    }

    fun onUdfpsSensorTouched() {
        runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_UDFPS_POINTER_DOWN, false)
    }

    fun registerListener(listener: FaceAuthenticationListener) {
        listeners.add(listener)
    }

    fun unregisterListener(listener: FaceAuthenticationListener) {
        listeners.remove(listener)
    }

    fun isLockedOut(): Boolean = repository.isLockedOut.value

    fun isRunning(): Boolean = repository.isAuthRunning.value

    fun canFaceAuthRun(): Boolean = repository.canRunFaceAuth.value

    fun isEnabled(): Boolean {
        return featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)
    }
    /**
     * Register listener for use from code that cannot use [authenticationStatus] or
     * [detectionStatus]
     */
    fun registerListener(listener: FaceAuthenticationListener)

    /** Provide the status of face authentication */
    val authenticationStatus = repository.authenticationStatus
    /** Unregister previously registered listener */
    fun unregisterListener(listener: FaceAuthenticationListener)

    /** Provide the status of face detection */
    val detectionStatus = repository.detectionStatus
    /** Whether the face auth interactor is enabled or not. */
    fun isEnabled(): Boolean

    private fun observeFaceAuthStateUpdates() {
        authenticationStatus
            .onEach { authStatusUpdate ->
                listeners.forEach { it.onAuthenticationStatusChanged(authStatusUpdate) }
            }
            .flowOn(mainDispatcher)
            .launchIn(applicationScope)
        detectionStatus
            .onEach { detectionStatusUpdate ->
                listeners.forEach { it.onDetectionStatusChanged(detectionStatusUpdate) }
            }
            .flowOn(mainDispatcher)
            .launchIn(applicationScope)
    }

    companion object {
        const val TAG = "KeyguardFaceAuthInteractor"
    }
    fun onUdfpsSensorTouched()
    fun onAssistantTriggeredOnLockScreen()
    fun onDeviceLifted()
    fun onQsExpansionStared()
    fun onNotificationPanelClicked()
    fun onSwipeUpOnBouncer()
}

/**
@@ -208,11 +76,3 @@ interface FaceAuthenticationListener {
    /** Receive status updates whenever face detection runs */
    fun onDetectionStatusChanged(status: DetectionStatus)
}

// Extension method that filters a generic Boolean flow to one that emits
// whenever there is flip from false -> true
private fun Flow<Boolean>.whenItFlipsToTrue(): Flow<Boolean> {
    return this.pairwise()
        .filter { pair -> !pair.previousValue && pair.newValue }
        .map { it.newValue }
}
Loading