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

Commit bd672716 authored by Fan Zhang's avatar Fan Zhang
Browse files

Block emergency gestures if the button speed is too fast

Fix: 228395041
Test: manual
Change-Id: I95bab0be66911f0758876db7eccbdd54d8e2c324
parent 5f17b13a
Loading
Loading
Loading
Loading
+30 −1
Original line number Diff line number Diff line
@@ -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) {
                    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
+68 −24
Original line number Diff line number Diff line
@@ -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);
        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;
    }