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

Commit f4c44877 authored by Wu Ahan's avatar Wu Ahan
Browse files

Fix flicker while usudfps screen off unlock

The flicker dues to additional invocation of requestPulse(), this cl
skipes the invocation while auth succeeded and only request pulse while
auth failure.

Flag: EXEMPT bugfix
Bug: 384986206
Test: manually
Change-Id: Id109c7625cea1e7bee655cba9486ea1ac64baa28
parent c9e4e62d
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -31,6 +31,10 @@ enum class FingerprintSensorType {
        return (this == UDFPS_OPTICAL) || (this == UDFPS_ULTRASONIC)
    }

    fun isUltrasonic(): Boolean {
        return this == UDFPS_ULTRASONIC
    }

    fun isPowerButton(): Boolean {
        return this == POWER_BUTTON
    }
+11 −8
Original line number Diff line number Diff line
@@ -66,6 +66,9 @@ interface BiometricStatusRepository {

    /** The current status of an acquired fingerprint. */
    val fingerprintAcquiredStatus: Flow<FingerprintAuthenticationStatus>

    /** The authentication states of all biometric modalities. */
    val authenticationState: Flow<AuthenticationState>
}

@SysUISingleton
@@ -84,7 +87,7 @@ constructor(
     *   [FaceManager.FaceDetectionCallback], and onEnrollmentError, onEnrollmentHelp, and
     *   onAcquired in [FingerprintManager.EnrollmentCallback] and [FaceManager.EnrollmentCallback]
     */
    private val authenticationState: Flow<AuthenticationState> =
    override val authenticationState: Flow<AuthenticationState> =
        callbackFlow {
                val updateAuthenticationState = { state: AuthenticationState ->
                    Log.d(TAG, "authenticationState updated: $state")
@@ -100,7 +103,7 @@ constructor(
                                AuthenticationState.Acquired(
                                    authInfo.biometricSourceType,
                                    authInfo.requestReason.toAuthenticationReason(),
                                    authInfo.acquiredInfo
                                    authInfo.acquiredInfo,
                                )
                            )
                        }
@@ -111,7 +114,7 @@ constructor(
                                    authInfo.biometricSourceType,
                                    authInfo.errString,
                                    authInfo.errCode,
                                    authInfo.requestReason.toAuthenticationReason()
                                    authInfo.requestReason.toAuthenticationReason(),
                                )
                            )
                        }
@@ -121,7 +124,7 @@ constructor(
                                AuthenticationState.Failed(
                                    authInfo.biometricSourceType,
                                    authInfo.requestReason.toAuthenticationReason(),
                                    authInfo.userId
                                    authInfo.userId,
                                )
                            )
                        }
@@ -132,7 +135,7 @@ constructor(
                                    authInfo.biometricSourceType,
                                    authInfo.helpString,
                                    authInfo.helpCode,
                                    authInfo.requestReason.toAuthenticationReason()
                                    authInfo.requestReason.toAuthenticationReason(),
                                )
                            )
                        }
@@ -141,7 +144,7 @@ constructor(
                            updateAuthenticationState(
                                AuthenticationState.Started(
                                    authInfo.biometricSourceType,
                                    authInfo.requestReason.toAuthenticationReason()
                                    authInfo.requestReason.toAuthenticationReason(),
                                )
                            )
                        }
@@ -150,7 +153,7 @@ constructor(
                            updateAuthenticationState(
                                AuthenticationState.Stopped(
                                    authInfo.biometricSourceType,
                                    AuthenticationReason.NotRunning
                                    AuthenticationReason.NotRunning,
                                )
                            )
                        }
@@ -163,7 +166,7 @@ constructor(
                                    authInfo.biometricSourceType,
                                    authInfo.isIsStrongBiometric,
                                    authInfo.requestReason.toAuthenticationReason(),
                                    authInfo.userId
                                    authInfo.userId,
                                )
                            )
                        }
+16 −1
Original line number Diff line number Diff line
@@ -17,16 +17,20 @@
package com.android.systemui.biometrics.domain.interactor

import android.app.ActivityTaskManager
import android.hardware.biometrics.BiometricSourceType
import android.util.Log
import com.android.systemui.biometrics.data.repository.BiometricStatusRepository
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
import com.android.systemui.biometrics.shared.model.AuthenticationReason
import com.android.systemui.biometrics.shared.model.AuthenticationReason.SettingsOperations
import com.android.systemui.biometrics.shared.model.AuthenticationState
import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatus
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.onEach

/** Encapsulates business logic for interacting with biometric authentication state. */
@@ -39,6 +43,9 @@ interface BiometricStatusInteractor {

    /** The current status of an acquired fingerprint. */
    val fingerprintAcquiredStatus: Flow<FingerprintAuthenticationStatus>

    /** The events of fingerprint authentication failure while unlocking the device. */
    val fingerprintAuthFailureEventsForDeviceEntry: Flow<AuthenticationState.Failed>
}

class BiometricStatusInteractorImpl
@@ -52,7 +59,7 @@ constructor(
    override val sfpsAuthenticationReason: Flow<AuthenticationReason> =
        combine(
                biometricStatusRepository.fingerprintAuthenticationReason,
                fingerprintPropertyRepository.sensorType
                fingerprintPropertyRepository.sensorType,
            ) { reason: AuthenticationReason, sensorType ->
                if (
                    sensorType.isPowerButton() &&
@@ -69,6 +76,14 @@ constructor(
    override val fingerprintAcquiredStatus: Flow<FingerprintAuthenticationStatus> =
        biometricStatusRepository.fingerprintAcquiredStatus

    override val fingerprintAuthFailureEventsForDeviceEntry: Flow<AuthenticationState.Failed> =
        biometricStatusRepository.authenticationState
            .filter {
                it.biometricSourceType == BiometricSourceType.FINGERPRINT &&
                    it.requestReason == AuthenticationReason.DeviceEntryAuthentication
            }
            .filterIsInstance<AuthenticationState.Failed>()

    companion object {
        private const val TAG = "BiometricStatusInteractor"
    }
+43 −0
Original line number Diff line number Diff line
@@ -19,13 +19,20 @@ package com.android.systemui.biometrics.domain.interactor
import android.content.Context
import android.graphics.Rect
import android.hardware.biometrics.SensorLocationInternal
import android.provider.Settings
import com.android.internal.R
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.shared.customization.data.SensorLocation
import com.android.systemui.util.kotlin.emitOnStart
import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
@@ -33,6 +40,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

@@ -46,6 +54,8 @@ constructor(
    @Main private val configurationInteractor: ConfigurationInteractor,
    displayStateInteractor: DisplayStateInteractor,
    udfpsOverlayInteractor: UdfpsOverlayInteractor,
    private val secureSettings: SecureSettings,
    @Background private val backgroundDispatcher: CoroutineDispatcher,
) {
    val propertiesInitialized: Flow<Boolean> = repository.propertiesInitialized
    val isUdfps: StateFlow<Boolean> =
@@ -57,6 +67,39 @@ constructor(
                initialValue = repository.sensorType.value.isUdfps(),
            )

    /** True if it is ultrasonic udfps sensor, otherwise false. */
    val isUltrasonic: StateFlow<Boolean> =
        repository.sensorType
            .map { it.isUltrasonic() }
            .stateIn(
                scope = applicationScope,
                started = SharingStarted.Eagerly,
                initialValue = repository.sensorType.value.isUltrasonic(),
            )

    /** True if screen-off unlock is both supported and enabled, otherwise false. */
    val screenOffUnlockEnabled: StateFlow<Boolean> =
        secureSettings
            .observerFlow(context.userId, Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED)
            .emitOnStart()
            .map { isScreenOffUnlockEnabled() }
            .distinctUntilChanged()
            .flowOn(backgroundDispatcher)
            .stateIn(
                applicationScope,
                started = SharingStarted.Eagerly,
                initialValue = isScreenOffUnlockEnabled(),
            )

    private fun isScreenOffUnlockEnabled(): Boolean =
        context.resources.getBoolean(R.bool.config_screen_off_udfps_enabled) &&
            Settings.Secure.getIntForUser(
                context.contentResolver,
                Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED,
                0,
                context.userId,
            ) != 0

    /**
     * Devices with multiple physical displays use unique display ids to determine which sensor is
     * on the active physical display. This value represents a unique physical display id.
+54 −2
Original line number Diff line number Diff line
@@ -41,7 +41,10 @@ import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor;
import com.android.systemui.biometrics.domain.interactor.FingerprintPropertyInteractor;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.DozeMachine.State;
import com.android.systemui.doze.dagger.DozeScope;
@@ -52,14 +55,19 @@ import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.Assert;
import com.android.systemui.util.kotlin.JavaAdapterKt;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximityCheck;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.wakelock.WakeLock;

import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.Job;

import java.io.PrintWriter;
import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.function.Consumer;

import javax.inject.Inject;
@@ -104,10 +112,14 @@ public class DozeTriggers implements DozeMachine.Part {
    private final KeyguardStateController mKeyguardStateController;
    private final UserTracker mUserTracker;
    private final SelectedUserInteractor mSelectedUserInteractor;
    private final BiometricStatusInteractor mBiometricStatusInteractor;
    private final FingerprintPropertyInteractor mFingerprintPropertyInteractor;
    private final CoroutineScope mScope;
    private final UiEventLogger mUiEventLogger;

    private long mNotificationPulseTime;
    private Runnable mAodInterruptRunnable;
    private Job mScreenOffUnlockResultJob;

    /** see {@link #onProximityFar} prox for callback */
    private boolean mWantProxSensor;
@@ -204,7 +216,10 @@ public class DozeTriggers implements DozeMachine.Part {
            KeyguardStateController keyguardStateController,
            DevicePostureController devicePostureController,
            UserTracker userTracker,
            SelectedUserInteractor selectedUserInteractor) {
            SelectedUserInteractor selectedUserInteractor,
            BiometricStatusInteractor biometricStatusInteractor,
            FingerprintPropertyInteractor fingerprintPropertyInteractor,
            @Application CoroutineScope scope) {
        mContext = context;
        mDozeHost = dozeHost;
        mConfig = config;
@@ -226,6 +241,9 @@ public class DozeTriggers implements DozeMachine.Part {
        mKeyguardStateController = keyguardStateController;
        mUserTracker = userTracker;
        mSelectedUserInteractor = selectedUserInteractor;
        mBiometricStatusInteractor = biometricStatusInteractor;
        mFingerprintPropertyInteractor = fingerprintPropertyInteractor;
        mScope = scope;
    }

    @Override
@@ -352,7 +370,11 @@ public class DozeTriggers implements DozeMachine.Part {
                        mDozeLog.d("udfpsLongPress - Not sending aodInterrupt. "
                                + "Unsupported doze state.");
                    }
                    // We will only request pulse when the authentication is failed while doing
                    // screen-off unlock on usudfps, in other cases, immediately request pulse.
                    if (mScreenOffUnlockResultJob == null) {
                        requestPulse(DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS, true, null);
                    }
                } else {
                    mDozeHost.extendPulse(pulseReason);
                }
@@ -704,5 +726,35 @@ public class DozeTriggers implements DozeMachine.Part {
        public void onSideFingerprintAcquisitionStarted() {
            DozeTriggers.this.onSideFingerprintAcquisitionStarted();
        }

        @Override
        public void onDozingChanged(boolean isDozing) {
            // No need to handle screen-off unlock when aod is enabled.
            if (mInAod) {
                mScreenOffUnlockResultJob = null;
                return;
            }
            // Only do this for ultrasonic udfps.
            if (!mFingerprintPropertyInteractor.isUltrasonic().getValue()
                    || !mFingerprintPropertyInteractor.getScreenOffUnlockEnabled().getValue()) {
                return;
            }
            // Start collecting authentication failure events while entering doze.
            if (isDozing && mScreenOffUnlockResultJob == null) {
                mScreenOffUnlockResultJob = JavaAdapterKt.collectFlow(
                        mScope,
                        mScope.getCoroutineContext(),
                        mBiometricStatusInteractor.getFingerprintAuthFailureEventsForDeviceEntry(),
                        state ->
                            requestPulse(DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS, true, null)
                );
            }
            // Cancel the coroutine job while exiting doze.
            if (!isDozing && mScreenOffUnlockResultJob != null) {
                mScreenOffUnlockResultJob.cancel(
                        new CancellationException("Ask stopping monitoring"));
                mScreenOffUnlockResultJob = null;
            }
        }
    };
}
Loading