Block emergency gestures if the button speed is too fast

Fix: 228395041
Test: manual
Change-Id: I95bab0be66911f0758876db7eccbdd54d8e2c324
This commit is contained in:
Fan Zhang
2022-04-08 16:02:37 -07:00
parent 5f17b13a5d
commit bd672716b7
2 changed files with 98 additions and 25 deletions

View File

@@ -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

View File

@@ -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;
}