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

Commit aec54aad authored by burakov's avatar burakov
Browse files

[flexiglass] Implement lockscreen bypass.

When bypass is enabled, the lock screen will be automatically dismissed
once the authentication challenge is completed. For example, completing
a biometric authentication challenge via face unlock or fingerprint
sensor can automatically bypass the lock screen.

It is deliberately not implemented as a Flow, since its state only needs
to be checked on demand.

Note: to enable this feature, the "Face auth modern arch" flag needs to
be enabled via Flag Flipper.

Fix: 290771600
Fix: 290404894
Test: new unit tests added
Test: manually verified in system UI that the lockscreen is skipped when
bypass is enabled in settings, and that it is not skipped when bypass is
disabled. To reach this setting, go to Settings > Security & privacy >
Device unlock > Face & Fingerprint unlock > Face Unlock > Skip lock
screen.

Change-Id: I393b9bcb1fed1299efaf8c426d269b4b83b36476
parent 0e61b8a1
Loading
Loading
Loading
Loading
+1 −19
Original line number Diff line number Diff line
@@ -63,14 +63,6 @@ interface AuthenticationRepository {
     */
    val isUnlocked: StateFlow<Boolean>

    /**
     * Whether lock screen bypass is enabled. When enabled, the lock screen will be automatically
     * dismisses once the authentication challenge is completed. For example, completing a biometric
     * authentication challenge via face unlock or fingerprint sensor can automatically bypass the
     * lock screen.
     */
    val isBypassEnabled: StateFlow<Boolean>

    /**
     * Whether the auto confirm feature is enabled for the currently-selected user.
     *
@@ -113,9 +105,6 @@ interface AuthenticationRepository {
     */
    suspend fun isLockscreenEnabled(): Boolean

    /** See [isBypassEnabled]. */
    fun setBypassEnabled(isBypassEnabled: Boolean)

    /** Reports an authentication attempt. */
    suspend fun reportAuthenticationAttempt(isSuccessful: Boolean)

@@ -157,7 +146,7 @@ constructor(
    private val lockPatternUtils: LockPatternUtils,
) : AuthenticationRepository {

    override val isUnlocked: StateFlow<Boolean> = keyguardRepository.isKeyguardUnlocked
    override val isUnlocked = keyguardRepository.isKeyguardUnlocked

    override suspend fun isLockscreenEnabled(): Boolean {
        return withContext(backgroundDispatcher) {
@@ -166,9 +155,6 @@ constructor(
        }
    }

    private val _isBypassEnabled = MutableStateFlow(false)
    override val isBypassEnabled: StateFlow<Boolean> = _isBypassEnabled.asStateFlow()

    override val isAutoConfirmEnabled: StateFlow<Boolean> =
        userRepository.selectedUserInfo
            .map { it.id }
@@ -225,10 +211,6 @@ constructor(
        }
    }

    override fun setBypassEnabled(isBypassEnabled: Boolean) {
        _isBypassEnabled.value = isBypassEnabled
    }

    override suspend fun reportAuthenticationAttempt(isSuccessful: Boolean) {
        val selectedUserId = userRepository.selectedUserId
        withContext(backgroundDispatcher) {
+12 −13
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import com.android.systemui.authentication.shared.model.AuthenticationThrottling
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -51,6 +52,7 @@ constructor(
    private val repository: AuthenticationRepository,
    @Background private val backgroundDispatcher: CoroutineDispatcher,
    private val userRepository: UserRepository,
    private val keyguardRepository: KeyguardRepository,
    private val clock: SystemClock,
) {
    /**
@@ -76,14 +78,6 @@ constructor(
                initialValue = true,
            )

    /**
     * Whether lock screen bypass is enabled. When enabled, the lock screen will be automatically
     * dismisses once the authentication challenge is completed. For example, completing a biometric
     * authentication challenge via face unlock or fingerprint sensor can automatically bypass the
     * lock screen.
     */
    val isBypassEnabled: StateFlow<Boolean> = repository.isBypassEnabled

    /** The current authentication throttling state, only meaningful if [isThrottled] is `true`. */
    val throttling: StateFlow<AuthenticationThrottlingModel> = repository.throttling

@@ -155,6 +149,16 @@ constructor(
        return !isUnlocked.value && getAuthenticationMethod().isSecure
    }

    /**
     * Whether lock screen bypass is enabled. When enabled, the lock screen will be automatically
     * dismisses once the authentication challenge is completed. For example, completing a biometric
     * authentication challenge via face unlock or fingerprint sensor can automatically bypass the
     * lock screen.
     */
    fun isBypassEnabled(): Boolean {
        return keyguardRepository.isBypassEnabled()
    }

    /**
     * Attempts to authenticate the user and unlock the device.
     *
@@ -218,11 +222,6 @@ constructor(
        return authenticationResult.isSuccessful
    }

    /** See [isBypassEnabled]. */
    fun toggleBypassEnabled() {
        repository.setBypassEnabled(!repository.isBypassEnabled.value)
    }

    /** Starts refreshing the throttling state every second. */
    private suspend fun startThrottlingCountdown() {
        cancelCountdown()
+21 −13
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.BiometricUnlockController.WakeAndUnlockMode
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -171,6 +172,14 @@ interface KeyguardRepository {
     */
    fun isKeyguardShowing(): Boolean

    /**
     * Whether lock screen bypass is enabled. When enabled, the lock screen will be automatically
     * dismissed once the authentication challenge is completed. For example, completing a biometric
     * authentication challenge via face unlock or fingerprint sensor can automatically bypass the
     * lock screen.
     */
    fun isBypassEnabled(): Boolean

    /** Sets whether the bottom area UI should animate the transition out of doze state. */
    fun setAnimateDozingTransitions(animate: Boolean)

@@ -206,6 +215,7 @@ constructor(
    wakefulnessLifecycle: WakefulnessLifecycle,
    biometricUnlockController: BiometricUnlockController,
    private val keyguardStateController: KeyguardStateController,
    private val keyguardBypassController: KeyguardBypassController,
    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
    private val dozeTransitionListener: DozeTransitionListener,
    private val dozeParameters: DozeParameters,
@@ -252,23 +262,17 @@ constructor(
    override val isAodAvailable: Flow<Boolean> =
        conflatedCallbackFlow {
                val callback =
                    object : DozeParameters.Callback {
                        override fun onAlwaysOnChange() {
                    DozeParameters.Callback {
                        trySendWithFailureLogging(
                                dozeParameters.getAlwaysOn(),
                            dozeParameters.alwaysOn,
                            TAG,
                            "updated isAodAvailable"
                        )
                    }
                    }

                dozeParameters.addCallback(callback)
                // Adding the callback does not send an initial update.
                trySendWithFailureLogging(
                    dozeParameters.getAlwaysOn(),
                    TAG,
                    "initial isAodAvailable"
                )
                trySendWithFailureLogging(dozeParameters.alwaysOn, TAG, "initial isAodAvailable")

                awaitClose { dozeParameters.removeCallback(callback) }
            }
@@ -464,6 +468,10 @@ constructor(
        return keyguardStateController.isShowing
    }

    override fun isBypassEnabled(): Boolean {
        return keyguardBypassController.bypassEnabled
    }

    override val statusBarState: Flow<StatusBarState> = conflatedCallbackFlow {
        val callback =
            object : StatusBarStateController.StateListener {
+1 −1
Original line number Diff line number Diff line
@@ -77,7 +77,7 @@ constructor(
            authenticationInteractor.isUnlocked
                .map { isUnlocked ->
                    val currentSceneKey = sceneInteractor.currentScene(CONTAINER_NAME).value.key
                    val isBypassEnabled = authenticationInteractor.isBypassEnabled.value
                    val isBypassEnabled = authenticationInteractor.isBypassEnabled()
                    when {
                        isUnlocked ->
                            when (currentSceneKey) {
+2 −12
Original line number Diff line number Diff line
@@ -50,12 +50,6 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr
    @DevicePostureInt private var postureState: Int = DEVICE_POSTURE_UNKNOWN
    private var pendingUnlock: PendingUnlock? = null
    private val listeners = mutableListOf<OnBypassStateChangedListener>()
    private val postureCallback = DevicePostureController.Callback { posture ->
        if (postureState != posture) {
            postureState = posture
            notifyListeners()
        }
    }
    private val faceAuthEnabledChangedCallback = object : KeyguardStateController.Callback {
        override fun onFaceAuthEnabledChanged() = notifyListeners()
    }
@@ -162,10 +156,8 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr

        val dismissByDefault = if (context.resources.getBoolean(
                        com.android.internal.R.bool.config_faceAuthDismissesKeyguard)) 1 else 0
        tunerService.addTunable(object : TunerService.Tunable {
            override fun onTuningChanged(key: String?, newValue: String?) {
        tunerService.addTunable({ key, _ ->
            bypassEnabled = tunerService.getValue(key, dismissByDefault) != 0
            }
        }, Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD)
        lockscreenUserManager.addUserChangedListener(
                object : NotificationLockscreenUserManager.UserChangedListener {
@@ -281,8 +273,6 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr
    }

    companion object {
        const val BYPASS_FADE_DURATION = 67

        private const val FACE_UNLOCK_BYPASS_NO_OVERRIDE = 0
        private const val FACE_UNLOCK_BYPASS_ALWAYS = 1
        private const val FACE_UNLOCK_BYPASS_NEVER = 2
Loading