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

Commit c441a9a0 authored by Austin Delgado's avatar Austin Delgado
Browse files

Add ellipse detection to UdfpsController

Enable with: adb shell cmd statusbar flag 2200 on (ExpandedOverlay) and
adb shell cmd statusbar flag 2202 on (EllipseDetection)

Bug: 257118043
Test: atest SystemUITests:com.android.systemui.biometrics
Change-Id: I143f7e5df71d3668a79cb898f618a749eef6f263
parent e4ce54df
Loading
Loading
Loading
Loading
+32 −8
Original line number Diff line number Diff line
@@ -150,7 +150,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
    // TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
    @Nullable private Runnable mAuthControllerUpdateUdfpsLocation;
    @Nullable private final AlternateUdfpsTouchProvider mAlternateTouchProvider;
    @Nullable private UdfpsDisplayMode mUdfpsDisplayMode;
    @Nullable private UdfpsDisplayModeProvider mUdfpsDisplayMode;

    // Tracks the velocity of a touch to help filter out the touches that move too fast.
    @Nullable private VelocityTracker mVelocityTracker;
@@ -165,6 +165,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {

    // The current request from FingerprintService. Null if no current request.
    @Nullable UdfpsControllerOverlay mOverlay;
    @Nullable private UdfpsEllipseDetection mUdfpsEllipseDetection;

    // The fingerprint AOD trigger doesn't provide an ACTION_UP/ACTION_CANCEL event to tell us when
    // to turn off high brightness mode. To get around this limitation, the state of the AOD
@@ -321,6 +322,10 @@ public class UdfpsController implements DozeReceiver, Dumpable {
        if (!mOverlayParams.equals(overlayParams)) {
            mOverlayParams = overlayParams;

            if (mFeatureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
                mUdfpsEllipseDetection.updateOverlayParams(overlayParams);
            }

            final boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateBouncer();

            // When the bounds change it's always necessary to re-create the overlay's window with
@@ -462,13 +467,19 @@ public class UdfpsController implements DozeReceiver, Dumpable {

                boolean withinSensorArea;
                if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
                    if (mFeatureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
                        // Ellipse detection
                        withinSensorArea = mUdfpsEllipseDetection.isGoodEllipseOverlap(event);
                    } else {
                        // Centroid with expanded overlay
                        withinSensorArea =
                            isWithinSensorArea(udfpsView, event.getRawX(),
                                        event.getRawY(), fromUdfpsView);
                    }
                } else {
                    // Centroid with sensor sized view
                    withinSensorArea =
                            isWithinSensorArea(udfpsView, event.getX(),
                                    event.getY(), fromUdfpsView);
                        isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView);
                }

                if (withinSensorArea) {
@@ -503,14 +514,23 @@ public class UdfpsController implements DozeReceiver, Dumpable {
                if (idx == event.getActionIndex()) {
                    boolean actionMoveWithinSensorArea;
                    if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
                        if (mFeatureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
                            // Ellipse detection
                            actionMoveWithinSensorArea =
                                    mUdfpsEllipseDetection.isGoodEllipseOverlap(event);
                        } else {
                            // Centroid with expanded overlay
                            actionMoveWithinSensorArea =
                                isWithinSensorArea(udfpsView, event.getRawX(idx),
                                        event.getRawY(idx), fromUdfpsView);
                        }
                    } else {
                        // Centroid with sensor sized view
                        actionMoveWithinSensorArea =
                            isWithinSensorArea(udfpsView, event.getX(idx),
                                    event.getY(idx), fromUdfpsView);
                    }

                    if ((fromUdfpsView || actionMoveWithinSensorArea)
                            && shouldTryToDismissKeyguard()) {
                        Log.v(TAG, "onTouch | dismiss keyguard ACTION_MOVE");
@@ -708,6 +728,10 @@ public class UdfpsController implements DozeReceiver, Dumpable {

        udfpsHapticsSimulator.setUdfpsController(this);
        udfpsShell.setUdfpsOverlayController(mUdfpsOverlayController);

        if (featureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
            mUdfpsEllipseDetection = new UdfpsEllipseDetection(mOverlayParams);
        }
    }

    /**
+92 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.biometrics

import android.graphics.Point
import android.graphics.Rect
import android.util.RotationUtils
import android.view.MotionEvent
import kotlin.math.cos
import kotlin.math.pow
import kotlin.math.sin

private const val TAG = "UdfpsEllipseDetection"

private const val NEEDED_POINTS = 2

class UdfpsEllipseDetection(overlayParams: UdfpsOverlayParams) {
    var sensorRect = Rect()
    var points: Array<Point> = emptyArray()

    init {
        sensorRect = Rect(overlayParams.sensorBounds)

        points = calculateSensorPoints(sensorRect)
    }

    fun updateOverlayParams(params: UdfpsOverlayParams) {
        sensorRect = Rect(params.sensorBounds)

        val rot = params.rotation
        RotationUtils.rotateBounds(
            sensorRect,
            params.naturalDisplayWidth,
            params.naturalDisplayHeight,
            rot
        )

        points = calculateSensorPoints(sensorRect)
    }

    fun isGoodEllipseOverlap(event: MotionEvent): Boolean {
        return points.count { checkPoint(event, it) } >= NEEDED_POINTS
    }

    private fun checkPoint(event: MotionEvent, point: Point): Boolean {
        // Calculate if sensor point is within ellipse
        // Formula: ((cos(o)(xE - xS) + sin(o)(yE - yS))^2 / a^2) + ((sin(o)(xE - xS) + cos(o)(yE -
        // yS))^2 / b^2) <= 1
        val a: Float = cos(event.orientation) * (point.x - event.rawX)
        val b: Float = sin(event.orientation) * (point.y - event.rawY)
        val c: Float = sin(event.orientation) * (point.x - event.rawX)
        val d: Float = cos(event.orientation) * (point.y - event.rawY)
        val result =
            (a + b).pow(2) / (event.touchMinor / 2).pow(2) +
                (c - d).pow(2) / (event.touchMajor / 2).pow(2)

        return result <= 1
    }
}

fun calculateSensorPoints(sensorRect: Rect): Array<Point> {
    val sensorX = sensorRect.centerX()
    val sensorY = sensorRect.centerY()
    val cornerOffset: Int = sensorRect.width() / 4
    val sideOffset: Int = sensorRect.width() / 3

    return arrayOf(
        Point(sensorX - cornerOffset, sensorY - cornerOffset),
        Point(sensorX, sensorY - sideOffset),
        Point(sensorX + cornerOffset, sensorY - cornerOffset),
        Point(sensorX - sideOffset, sensorY),
        Point(sensorX, sensorY),
        Point(sensorX + sideOffset, sensorY),
        Point(sensorX - cornerOffset, sensorY + cornerOffset),
        Point(sensorX, sensorY + sideOffset),
        Point(sensorX + cornerOffset, sensorY + cornerOffset)
    )
}
+1 −0
Original line number Diff line number Diff line
@@ -235,6 +235,7 @@ constructor(
                    return
                }

                // Forwarding touches not needed with expanded overlay
                if (useExpandedOverlay) {
                    return
                } else {