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

Commit 82d4272f authored by beverlyt's avatar beverlyt Committed by Beverly Tai
Browse files

For US-UDFPS: update shouldHandleTouches outside of view lifecycle

Ultrasonic UDFPS touches go directly to the HAL instead
of being forwarded via SystemUI. Therefore, for US-UDFPS,
SysUI doesn't need to wait until the UDFPS view is added
before informing the HAL that it can listen for touches.

As a result, when the device goes to sleep, ultrasonic UDFPS
can start updating shouldHandleTouches immediately before
the udfps-view is added. This decreases the delay
of starting UDFPS listening when the device initially starts
going to sleep.

Test: atest UdfpsOverlayInteractorTest
Test: enroll US-UDFPS; press power button (from home screen or
app), attempt UDFPS immediately, observe UDFPS can be used
quicker than before this CL
Flag: com.android.systemui.new_dozing_keyguard_states
Bug: 391771923

Change-Id: Id952649a71c341efbecce74e4470bef03378c6cd
parent 60b62275
Loading
Loading
Loading
Loading
+23 −2
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUAR
import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING
import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR
import android.hardware.biometrics.BiometricRequestConstants.RequestReason
import android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
import android.os.Build
import android.os.RemoteException
@@ -148,6 +149,12 @@ constructor(
        return show(params, null)
    }

    private fun setHandleTouchesDisregardingUdfpsOverlayViewLifecycle(): Boolean {
        return requestReason == REASON_AUTH_KEYGUARD &&
            com.android.systemui.Flags.newDozingKeyguardStates() &&
            overlayParams.sensorType == TYPE_UDFPS_ULTRASONIC
    }

    /** Show the overlay or return false and do nothing if it is already showing. */
    @SuppressLint("ClickableViewAccessibility")
    fun show(params: UdfpsOverlayParams, attachListener: OnAttachStateChangeListener?): Boolean {
@@ -155,6 +162,15 @@ constructor(
            overlayParams = params
            overlayAttachStateListener = attachListener
            sensorBounds = Rect(params.sensorBounds)
            val isSetHandleTouchesOutsideOfUdfpsViewLifecycle =
                setHandleTouchesDisregardingUdfpsOverlayViewLifecycle()
            if (isSetHandleTouchesOutsideOfUdfpsViewLifecycle) {
                // doesn't use the overlayTouchView to handle the lifecycle of forwarding
                // shouldHandleTouches to the HAL
                udfpsOverlayInteractor.updateSetHandleTouchesForKeyguard(
                    deviceEntryUdfpsTouchOverlayViewModel.get()
                )
            }
            try {
                overlayTouchView =
                    (inflater.inflate(R.layout.udfps_touch_overlay, null, false)
@@ -173,7 +189,12 @@ constructor(
                                    UdfpsTouchOverlayBinder.bind(
                                        view = this,
                                        viewModel = deviceEntryUdfpsTouchOverlayViewModel.get(),
                                        udfpsOverlayInteractor = udfpsOverlayInteractor,
                                        udfpsOverlayInteractor =
                                            if (isSetHandleTouchesOutsideOfUdfpsViewLifecycle) {
                                                null
                                            } else {
                                                udfpsOverlayInteractor
                                            },
                                    )
                                REASON_AUTH_BP ->
                                    UdfpsTouchOverlayBinder.bind(
@@ -263,7 +284,7 @@ constructor(
    fun hide(): Boolean {
        val wasShowing = isShowing
        Log.d(TAG, "hideUdfpsControllerOverlay wasShowing=$wasShowing")

        udfpsOverlayInteractor.stopSetHandleTouchesForKeyguard()
        udfpsDisplayModeProvider.disable(null)
        getTouchOverlay()?.apply {
            if (this.parent != null) {
+29 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.util.Log
import android.view.MotionEvent
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -31,6 +32,7 @@ import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import javax.inject.Inject
import kotlin.math.max
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -40,6 +42,7 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch

/** Encapsulates business logic for interacting with the UDFPS overlay. */
@SysUISingleton
@@ -50,7 +53,7 @@ constructor(
    private val authController: AuthController,
    private val selectedUserInteractor: SelectedUserInteractor,
    private val fingerprintManager: FingerprintManager?,
    @Application scope: CoroutineScope,
    @Application private val scope: CoroutineScope,
) {
    private fun calculateIconSize(): Int {
        val pixelPitch = context.resources.getFloat(R.dimen.pixel_pitch)
@@ -131,6 +134,31 @@ constructor(
            }
            .distinctUntilChanged()

    private var isUpdatingSetHandleTouchesForKeyguard: Job? = null

    fun stopSetHandleTouchesForKeyguard() {
        isUpdatingSetHandleTouchesForKeyguard?.cancel()
        isUpdatingSetHandleTouchesForKeyguard = null
    }

    fun updateSetHandleTouchesForKeyguard(
        deviceEntryUdfpsTouchOverlayViewModel: DeviceEntryUdfpsTouchOverlayViewModel
    ) {
        if (isUpdatingSetHandleTouchesForKeyguard == null) {
            isUpdatingSetHandleTouchesForKeyguard =
                scope.launch {
                    deviceEntryUdfpsTouchOverlayViewModel.shouldHandleTouches.collect {
                        Log.d("UdfpsOverlayInteractor", "update shouldHandleTouches=$it")
                        setHandleTouches(it)
                    }
                }
            isUpdatingSetHandleTouchesForKeyguard?.invokeOnCompletion {
                Log.d("UdfpsOverlayInteractor", "invokeOnCompletion shouldHandleTouches=false")
                setHandleTouches(false)
            }
        }
    }

    companion object {
        private const val TAG = "UdfpsOverlayInteractor"
    }
+24 −15
Original line number Diff line number Diff line
@@ -20,43 +20,52 @@ import android.util.Log
import androidx.core.view.isInvisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.biometrics.ui.view.UdfpsTouchOverlay
import com.android.systemui.biometrics.ui.viewmodel.UdfpsTouchOverlayViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.app.tracing.coroutines.launchTraced as launch

object UdfpsTouchOverlayBinder {

    /**
     * Updates visibility for the UdfpsTouchOverlay which controls whether the view will receive
     * touches or not. For some devices, this is instead handled by UdfpsOverlayInteractor, so this
     * viewBinder will send the information to the interactor.
     * Updates visibility for the UdfpsTouchOverlay. This controls whether the view will receive
     * touches or not. This is important for optical-UDFPS to receive the fingerprint-sensor touch
     * events.
     */
    @JvmStatic
    fun bind(
        view: UdfpsTouchOverlay,
        viewModel: UdfpsTouchOverlayViewModel,
        udfpsOverlayInteractor: UdfpsOverlayInteractor,
        udfpsOverlayInteractor: UdfpsOverlayInteractor? = null,
    ) {
        view.repeatWhenAttached {
            repeatOnLifecycle(Lifecycle.State.CREATED) {
                launch {
                        viewModel.shouldHandleTouches.collect { shouldHandleTouches ->
                            if (udfpsOverlayInteractor != null) {
                                Log.d(
                                    "UdfpsTouchOverlayBinder",
                                    "[$view]: update shouldHandleTouches=$shouldHandleTouches",
                                )
                            } else {
                                Log.d(
                                    "UdfpsTouchOverlayBinder",
                                    "[$view]: update isVisible=$shouldHandleTouches",
                                )
                            }
                            view.isInvisible = !shouldHandleTouches
                            udfpsOverlayInteractor.setHandleTouches(shouldHandleTouches)
                            udfpsOverlayInteractor?.setHandleTouches(shouldHandleTouches)
                        }
                    }
                    .invokeOnCompletion {
                        if (udfpsOverlayInteractor != null) {
                            Log.d(
                                "UdfpsTouchOverlayBinder",
                                "[$view-detached]: update shouldHandleTouches=false",
                            )
                        udfpsOverlayInteractor.setHandleTouches(false)
                            udfpsOverlayInteractor?.setHandleTouches(false)
                        }
                    }
            }
        }