From bd672716b7c26b0c858762fc842f091fb3b84e91 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Fri, 8 Apr 2022 16:02:37 -0700 Subject: [PATCH] Block emergency gestures if the button speed is too fast Fix: 228395041 Test: manual Change-Id: I95bab0be66911f0758876db7eccbdd54d8e2c324 --- .../server/GestureLauncherService.java | 31 ++++++- .../server/GestureLauncherServiceTest.java | 92 ++++++++++++++----- 2 files changed, 98 insertions(+), 25 deletions(-) diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java index f944d4fd8478b..e529010e6b7d4 100644 --- a/services/core/java/com/android/server/GestureLauncherService.java +++ b/services/core/java/com/android/server/GestureLauncherService.java @@ -74,6 +74,13 @@ public class GestureLauncherService extends SystemService { */ @VisibleForTesting static final long CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS = 300; + /** + * Min time in milliseconds to complete the emergency gesture for it count. If the gesture is + * completed faster than this, we assume it's not performed by human and the + * event gets ignored. + */ + @VisibleForTesting static final int EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS = 160; + /** * Interval in milliseconds in which the power button must be depressed in succession to be * considered part of an extended sequence of taps. Note that this is a looser threshold than @@ -184,6 +191,7 @@ public class GestureLauncherService extends SystemService { private int mEmergencyGesturePowerButtonCooldownPeriodMs; private long mLastPowerDown; + private long mFirstPowerDown; private long mLastEmergencyGestureTriggered; private int mPowerButtonConsecutiveTaps; private int mPowerButtonSlowConsecutiveTaps; @@ -553,10 +561,12 @@ public class GestureLauncherService extends SystemService { mLastPowerDown = event.getEventTime(); if (powerTapInterval >= POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS) { // Tap too slow, reset consecutive tap counts. + mFirstPowerDown = event.getEventTime(); mPowerButtonConsecutiveTaps = 1; mPowerButtonSlowConsecutiveTaps = 1; } else if (powerTapInterval >= CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) { // Tap too slow for shortcuts + mFirstPowerDown = event.getEventTime(); mPowerButtonConsecutiveTaps = 1; mPowerButtonSlowConsecutiveTaps++; } else { @@ -575,7 +585,26 @@ public class GestureLauncherService extends SystemService { intercept = interactive; } if (mPowerButtonConsecutiveTaps == EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD) { - launchEmergencyGesture = true; + long emergencyGestureSpentTime = event.getEventTime() - mFirstPowerDown; + long emergencyGestureTapDetectionMinTimeMs = Settings.Global.getInt( + mContext.getContentResolver(), + Settings.Global.EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS, + EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS); + if (emergencyGestureSpentTime <= emergencyGestureTapDetectionMinTimeMs) { + Slog.i(TAG, "Emergency gesture detected but it's too fast. Gesture time: " + + emergencyGestureSpentTime + " ms"); + // Reset consecutive tap counts. + mFirstPowerDown = event.getEventTime(); + mPowerButtonConsecutiveTaps = 1; + mPowerButtonSlowConsecutiveTaps = 1; + } else { + Slog.i(TAG, "Emergency gesture detected. Gesture time: " + + emergencyGestureSpentTime + " ms"); + launchEmergencyGesture = true; + mMetricsLogger.histogram("emergency_gesture_spent_time", + (int) emergencyGestureSpentTime); + + } } } if (mCameraDoubleTapPowerEnabled diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java index 3d3c1abb3e915..4e059b4b7432e 100644 --- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java @@ -16,6 +16,9 @@ package com.android.server; +import static com.android.server.GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; +import static com.android.server.GestureLauncherService.EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -248,7 +251,7 @@ public class GestureLauncherServiceTest { mGestureLauncherService.updateCameraDoubleTapPowerEnabled(); long eventTime = INITIAL_EVENT_TIME_MILLIS + - GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); boolean interactive = true; @@ -296,7 +299,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -341,7 +344,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -435,7 +438,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -489,7 +492,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; // 2nd button triggers camera eventTime += interval; @@ -578,7 +581,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; // 3 more button presses which should not trigger any gesture (camera gesture disabled) for (int i = 0; i < 3; i++) { eventTime += interval; @@ -632,7 +635,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; // 3 more button presses which should not trigger any gesture, but intercepts action. for (int i = 0; i < 3; i++) { eventTime += interval; @@ -735,7 +738,7 @@ public class GestureLauncherServiceTest { interactive, outLaunched); assertTrue(intercepted); assertFalse(outLaunched.value); - interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; } } @@ -763,11 +766,32 @@ public class GestureLauncherServiceTest { interactive, outLaunched); assertTrue(intercepted); assertFalse(outLaunched.value); - interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; } } + @Test + public void testInterceptPowerKeyDown_triggerEmergency_fiveFastTaps_gestureIgnored() { + // Trigger emergency by tapping button 5 times + long eventTime = triggerEmergencyGesture(/* tapIntervalMs= */ 1); + + // Add 1 more millisecond and send the event again. + eventTime += 1; + + // Subsequent long press is intercepted, but should not trigger any gesture + KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, + IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE, + KeyEvent.FLAG_LONG_PRESS); + MutableBoolean outLaunched = new MutableBoolean(true); + boolean intercepted = mGestureLauncherService.interceptPowerKeyDown( + keyEvent, + /* interactive= */ true, + outLaunched); + assertFalse(intercepted); + assertFalse(outLaunched.value); + } + @Test public void testInterceptPowerKeyDown_triggerEmergency_longPress_cooldownTriggered() { // Enable power button cooldown @@ -890,7 +914,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE, @@ -936,7 +960,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -983,7 +1007,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -1075,7 +1099,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -1120,7 +1144,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -1212,7 +1236,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -1261,7 +1285,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -1306,7 +1330,7 @@ public class GestureLauncherServiceTest { assertFalse(intercepted); assertFalse(outLaunched.value); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; + final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS; eventTime += interval; keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); @@ -1384,9 +1408,20 @@ public class GestureLauncherServiceTest { /** * Helper method to trigger emergency gesture by pressing button for 5 times. + * * @return last event time. */ private long triggerEmergencyGesture() { + return triggerEmergencyGesture(CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1); + } + + /** + * Helper method to trigger emergency gesture by pressing button for 5 times with + * specified interval between each tap + * + * @return last event time. + */ + private long triggerEmergencyGesture(long tapIntervalMs) { // Enable emergency power gesture withEmergencyGestureEnabledConfigValue(true); withEmergencyGestureEnabledSettingValue(true); @@ -1402,8 +1437,7 @@ public class GestureLauncherServiceTest { keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT); mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, outLaunched); - final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; - eventTime += interval; + eventTime += tapIntervalMs; } // 5th button press should trigger the emergency flow @@ -1412,12 +1446,22 @@ public class GestureLauncherServiceTest { outLaunched.value = false; boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, outLaunched); - assertTrue(outLaunched.value); + long emergencyGestureTapDetectionMinTimeMs = Settings.Global.getInt( + mContext.getContentResolver(), + Settings.Global.EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS, + EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS); assertTrue(intercepted); - verify(mUiEventLogger, times(1)) - .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER); - verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected(); - + if (tapIntervalMs * 4 > emergencyGestureTapDetectionMinTimeMs) { + assertTrue(outLaunched.value); + verify(mUiEventLogger, times(1)) + .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER); + verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected(); + } else { + assertFalse(outLaunched.value); + verify(mUiEventLogger, never()) + .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER); + verify(mStatusBarManagerInternal, never()).onEmergencyActionLaunchGestureDetected(); + } return eventTime; }