From 34d34e0a8138fa15f531bbc7302c1139dcae4e30 Mon Sep 17 00:00:00 2001 From: Austin Delgado Date: Fri, 28 Apr 2023 14:06:06 -0700 Subject: [PATCH] Ignore touches outside of Udfps Overlay bounds Udfps overlay is fullscreen so unnecessary ellipse checks can be removed by only checking touches that are within the overlay bounds (bottom half of screen). These touches are likely way off from the sensor. Bug: 274162395 Test: atest SystemUITests:com.android.systemui.biometrics Change-Id: I56af14d9817b5b4719a20d40dcc69f961003b1be --- .../settingslib/udfps/UdfpsOverlayParams.kt | 3 +++ .../systemui/biometrics/UdfpsController.java | 2 +- .../udfps/BoundingBoxOverlapDetector.kt | 9 ++++++-- .../udfps/EllipseOverlapDetector.kt | 21 +++++++++++++------ .../biometrics/udfps/NormalizedTouchData.kt | 16 +++++++------- .../biometrics/udfps/OverlapDetector.kt | 6 +++++- .../udfps/SinglePointerTouchProcessor.kt | 8 ++++++- .../udfps/BoundingBoxOverlapDetectorTest.kt | 9 +++++--- .../udfps/EllipseOverlapDetectorTest.kt | 9 +++++--- .../udfps/NormalizedTouchDataTest.kt | 2 +- .../biometrics/udfps/FakeOverlapDetector.kt | 6 +++++- 11 files changed, 64 insertions(+), 27 deletions(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/udfps/UdfpsOverlayParams.kt b/packages/SettingsLib/src/com/android/settingslib/udfps/UdfpsOverlayParams.kt index d55a027c2374b..b386e5e12504a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/udfps/UdfpsOverlayParams.kt +++ b/packages/SettingsLib/src/com/android/settingslib/udfps/UdfpsOverlayParams.kt @@ -36,6 +36,9 @@ data class UdfpsOverlayParams( /** Same as [sensorBounds], but in native resolution. */ val nativeSensorBounds = Rect(sensorBounds).apply { scale(1f / scaleFactor) } + /** Same as [overlayBounds], but in native resolution. */ + val nativeOverlayBounds = Rect(overlayBounds).apply { scale(1f / scaleFactor) } + /** See [android.view.DisplayInfo.logicalWidth] */ val logicalDisplayWidth = if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index bc44df488d906..493762275ee06 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -636,7 +636,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { mOverlay.getOverlayView().getViewRootImpl().getInputToken()); } - return processedTouch.getTouchData().isWithinSensor(mOverlayParams.getNativeSensorBounds()); + return processedTouch.getTouchData().isWithinBounds(mOverlayParams.getNativeSensorBounds()); } private boolean oldOnTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt index 79a0acb8bbc19..cf6044f146b02 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt @@ -22,6 +22,11 @@ import com.android.systemui.dagger.SysUISingleton /** Returns whether the touch coordinates are within the sensor's bounding box. */ @SysUISingleton class BoundingBoxOverlapDetector : OverlapDetector { - override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean = - touchData.isWithinSensor(nativeSensorBounds) + override fun isGoodOverlap( + touchData: NormalizedTouchData, + nativeSensorBounds: Rect, + nativeOverlayBounds: Rect, + ): Boolean = + touchData.isWithinBounds(nativeOverlayBounds) && + touchData.isWithinBounds(nativeSensorBounds) } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetector.kt index baf8d743938d7..f70e01dc9050d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetector.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetector.kt @@ -30,8 +30,8 @@ private enum class SensorPixelPosition { TARGET // Pixel within sensor center target } -private val isDebug = true -private val TAG = "EllipseOverlapDetector" +private const val isDebug = false +private const val TAG = "EllipseOverlapDetector" /** * Approximates the touch as an ellipse and determines whether the ellipse has a sufficient overlap @@ -39,12 +39,21 @@ private val TAG = "EllipseOverlapDetector" */ @SysUISingleton class EllipseOverlapDetector(private val params: EllipseOverlapDetectorParams) : OverlapDetector { - override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean { - // First, check if touch is within bounding box, - if (nativeSensorBounds.contains(touchData.x.toInt(), touchData.y.toInt())) { + override fun isGoodOverlap( + touchData: NormalizedTouchData, + nativeSensorBounds: Rect, + nativeOverlayBounds: Rect, + ): Boolean { + // First, check if touch is within bounding box to exit early + if (touchData.isWithinBounds(nativeSensorBounds)) { return true } + // Check touch is within overlay bounds, not worth checking if outside + if (!touchData.isWithinBounds(nativeOverlayBounds)) { + return false + } + var isTargetTouched = false var sensorPixels = 0 var coveredPixels = 0 @@ -77,7 +86,7 @@ class EllipseOverlapDetector(private val params: EllipseOverlapDetectorParams) : val percentage: Float = coveredPixels.toFloat() / sensorPixels if (isDebug) { - Log.v( + Log.d( TAG, "covered: $coveredPixels, sensor: $sensorPixels, " + "percentage: $percentage, isCenterTouched: $isTargetTouched" diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt index 6854b50370ba0..1b4062a4e9bc3 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt @@ -51,16 +51,16 @@ data class NormalizedTouchData( ) { /** - * [nativeSensorBounds] contains the location and dimensions of the sensor area in native - * resolution and natural orientation. + * [nativeBounds] contains the location and dimensions of the area in native resolution and + * natural orientation. * - * Returns whether the coordinates of the given pointer are within the sensor's bounding box. + * Returns whether the coordinates of the given pointer are within the bounding box. */ - fun isWithinSensor(nativeSensorBounds: Rect): Boolean { - return nativeSensorBounds.left <= x && - nativeSensorBounds.right >= x && - nativeSensorBounds.top <= y && - nativeSensorBounds.bottom >= y + fun isWithinBounds(nativeBounds: Rect): Boolean { + return nativeBounds.left <= x && + nativeBounds.right >= x && + nativeBounds.top <= y && + nativeBounds.bottom >= y } @JvmOverloads diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/OverlapDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/OverlapDetector.kt index 0fec8ffbaa0aa..f16347159010b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/OverlapDetector.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/OverlapDetector.kt @@ -20,5 +20,9 @@ import android.graphics.Rect /** Determines whether the touch has a sufficient overlap with the sensor. */ interface OverlapDetector { - fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean + fun isGoodOverlap( + touchData: NormalizedTouchData, + nativeSensorBounds: Rect, + nativeOverlayBounds: Rect + ): Boolean } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt index 6c9390ddad7d8..eeb0f4c7bb134 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt @@ -46,7 +46,13 @@ class SinglePointerTouchProcessor @Inject constructor(val overlapDetector: Overl val touchData = List(event.pointerCount) { event.normalize(it, overlayParams) } val pointersOnSensor = touchData - .filter { overlapDetector.isGoodOverlap(it, overlayParams.nativeSensorBounds) } + .filter { + overlapDetector.isGoodOverlap( + it, + overlayParams.nativeSensorBounds, + overlayParams.nativeOverlayBounds + ) + } .map { it.pointerId } return PreprocessedTouch(touchData, previousPointerOnSensorId, pointersOnSensor) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt index 4f89b69108f43..da55d5a099b74 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt @@ -33,7 +33,7 @@ class BoundingBoxOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() { @Test fun isGoodOverlap() { val touchData = TOUCH_DATA.copy(x = testCase.x.toFloat(), y = testCase.y.toFloat()) - val actual = underTest.isGoodOverlap(touchData, SENSOR) + val actual = underTest.isGoodOverlap(touchData, SENSOR, OVERLAY) assertThat(actual).isEqualTo(testCase.expected) } @@ -50,8 +50,10 @@ class BoundingBoxOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() { validYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY()) ), genNegativeTestCases( - invalidXs = listOf(SENSOR.left - 1, SENSOR.right + 1), - invalidYs = listOf(SENSOR.top - 1, SENSOR.bottom + 1), + invalidXs = + listOf(SENSOR.left - 1, SENSOR.right + 1, OVERLAY.left, OVERLAY.right), + invalidYs = + listOf(SENSOR.top - 1, SENSOR.bottom + 1, OVERLAY.top, OVERLAY.bottom), validXs = listOf(SENSOR.left, SENSOR.right, SENSOR.centerX()), validYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY()) ) @@ -82,6 +84,7 @@ private val TOUCH_DATA = ) private val SENSOR = Rect(100 /* left */, 200 /* top */, 300 /* right */, 500 /* bottom */) +private val OVERLAY = Rect(0 /* left */, 100 /* top */, 400 /* right */, 600 /* bottom */) private fun genTestCases( xs: List, diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt index fb3c1854e9966..317141ba42ddd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt @@ -43,7 +43,7 @@ class EllipseOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() { minor = testCase.minor, major = testCase.major ) - val actual = underTest.isGoodOverlap(touchData, SENSOR) + val actual = underTest.isGoodOverlap(touchData, SENSOR, OVERLAY) assertThat(actual).isEqualTo(testCase.expected) } @@ -71,8 +71,10 @@ class EllipseOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() { expected = true ), genNegativeTestCase( - outerXs = listOf(SENSOR.left - 1, SENSOR.right + 1), - outerYs = listOf(SENSOR.top - 1, SENSOR.bottom + 1), + outerXs = + listOf(SENSOR.left - 1, SENSOR.right + 1, OVERLAY.left, OVERLAY.right), + outerYs = + listOf(SENSOR.top - 1, SENSOR.bottom + 1, OVERLAY.top, OVERLAY.bottom), minor = 100f, major = 100f, expected = false @@ -104,6 +106,7 @@ private val TOUCH_DATA = ) private val SENSOR = Rect(100 /* left */, 200 /* top */, 300 /* right */, 400 /* bottom */) +private val OVERLAY = Rect(0 /* left */, 100 /* top */, 400 /* right */, 600 /* bottom */) private fun genPositiveTestCases( innerXs: List, diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt index 834d0a69e427e..3e5c43a334742 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt @@ -16,7 +16,7 @@ class NormalizedTouchDataTest(val testCase: TestCase) : SysuiTestCase() { @Test fun isWithinSensor() { val touchData = TOUCH_DATA.copy(x = testCase.x.toFloat(), y = testCase.y.toFloat()) - val actual = touchData.isWithinSensor(SENSOR) + val actual = touchData.isWithinBounds(SENSOR) assertThat(actual).isEqualTo(testCase.expected) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt index 1bdee3667d04e..e3e7933405a4f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt @@ -21,7 +21,11 @@ import android.graphics.Rect class FakeOverlapDetector : OverlapDetector { var shouldReturn: Map = mapOf() - override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean { + override fun isGoodOverlap( + touchData: NormalizedTouchData, + nativeSensorBounds: Rect, + nativeOverlayBounds: Rect + ): Boolean { return shouldReturn[touchData.pointerId] ?: error("Unexpected PointerId not declared in TestCase currentPointers") }