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

Commit 7f62909c authored by Patrick Huang's avatar Patrick Huang Committed by Android (Google) Code Review
Browse files

Merge "Add power button cooldown after emergency gesture is triggered." into sc-v2-dev

parents 8b65a252 263b03cb
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -9194,6 +9194,16 @@ public final class Settings {
        public static final String EMERGENCY_GESTURE_SOUND_ENABLED =
                "emergency_gesture_sound_enabled";
        /**
         * The power button "cooldown" period in milliseconds after the Emergency gesture is
         * triggered, during which single-key actions on the power button are suppressed. Cooldown
         * period is disabled if set to zero.
         *
         * @hide
         */
        public static final String EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS =
                "emergency_gesture_power_button_cooldown_period_ms";
        /**
         * Whether the camera launch gesture to double tap the power button when the screen is off
         * should be disabled.
+2 −0
Original line number Diff line number Diff line
@@ -272,6 +272,8 @@ public class SecureSettingsValidators {
        VALIDATORS.put(Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR);
        VALIDATORS.put(Secure.EMERGENCY_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
        VALIDATORS.put(Secure.EMERGENCY_GESTURE_SOUND_ENABLED, BOOLEAN_VALIDATOR);
        VALIDATORS.put(Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
                NON_NEGATIVE_INTEGER_VALIDATOR);
        VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR);
        VALIDATORS.put(
                Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, NONE_NEGATIVE_LONG_VALIDATOR);
+70 −0
Original line number Diff line number Diff line
@@ -82,6 +82,20 @@ public class GestureLauncherService extends SystemService {
     */
    private static final int EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD = 5;

    /**
     * Default value of the power button "cooldown" period after the Emergency gesture is triggered.
     * See {@link Settings.Secure#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS}
     */
    private static final int EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_DEFAULT = 3000;

    /**
     * Maximum value of the power button "cooldown" period after the Emergency gesture is triggered.
     * The value read from {@link Settings.Secure#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS}
     * is capped at this maximum.
     */
    @VisibleForTesting
    static final int EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX = 5000;

    /**
     * Number of taps required to launch camera shortcut.
     */
@@ -145,7 +159,14 @@ public class GestureLauncherService extends SystemService {
     */
    private boolean mEmergencyGestureEnabled;

    /**
     * Power button cooldown period in milliseconds, after emergency gesture is triggered. A zero
     * value means the cooldown period is disabled.
     */
    private int mEmergencyGesturePowerButtonCooldownPeriodMs;

    private long mLastPowerDown;
    private long mLastEmergencyGestureTriggered;
    private int mPowerButtonConsecutiveTaps;
    private int mPowerButtonSlowConsecutiveTaps;
    private final UiEventLogger mUiEventLogger;
@@ -210,6 +231,7 @@ public class GestureLauncherService extends SystemService {
            updateCameraRegistered();
            updateCameraDoubleTapPowerEnabled();
            updateEmergencyGestureEnabled();
            updateEmergencyGesturePowerButtonCooldownPeriodMs();

            mUserId = ActivityManager.getCurrentUser();
            mContext.registerReceiver(mUserReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED));
@@ -230,6 +252,10 @@ public class GestureLauncherService extends SystemService {
        mContext.getContentResolver().registerContentObserver(
                Settings.Secure.getUriFor(Settings.Secure.EMERGENCY_GESTURE_ENABLED),
                false, mSettingObserver, mUserId);
        mContext.getContentResolver().registerContentObserver(
                Settings.Secure.getUriFor(
                        Settings.Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS),
                false, mSettingObserver, mUserId);
    }

    private void updateCameraRegistered() {
@@ -263,6 +289,14 @@ public class GestureLauncherService extends SystemService {
        }
    }

    @VisibleForTesting
    void updateEmergencyGesturePowerButtonCooldownPeriodMs() {
        int cooldownPeriodMs = getEmergencyGesturePowerButtonCooldownPeriodMs(mContext, mUserId);
        synchronized (this) {
            mEmergencyGesturePowerButtonCooldownPeriodMs = cooldownPeriodMs;
        }
    }

    private void unregisterCameraLaunchGesture() {
        if (mCameraLaunchRegistered) {
            mCameraLaunchRegistered = false;
@@ -397,6 +431,20 @@ public class GestureLauncherService extends SystemService {
                Settings.Secure.EMERGENCY_GESTURE_ENABLED, 1, userId) != 0;
    }

    /**
     * Gets power button cooldown period in milliseconds after emergency gesture is triggered. The
     * value is capped at a maximum
     * {@link GestureLauncherService#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX}. If the
     * value is zero, it means the cooldown period is disabled.
     */
    @VisibleForTesting
    static int getEmergencyGesturePowerButtonCooldownPeriodMs(Context context, int userId) {
        int cooldown = Settings.Secure.getIntForUser(context.getContentResolver(),
                Settings.Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
                EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_DEFAULT, userId);
        return Math.min(cooldown, EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX);
    }

    /**
     * Whether to enable the camera launch gesture.
     */
@@ -445,10 +493,24 @@ public class GestureLauncherService extends SystemService {
     */
    public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive,
            MutableBoolean outLaunched) {
        if (mEmergencyGestureEnabled && mEmergencyGesturePowerButtonCooldownPeriodMs >= 0
                && event.getEventTime() - mLastEmergencyGestureTriggered
                < mEmergencyGesturePowerButtonCooldownPeriodMs) {
            Slog.i(TAG, String.format(
                    "Suppressing power button: within %dms cooldown period after Emergency "
                            + "Gesture. Begin=%dms, end=%dms.",
                    mEmergencyGesturePowerButtonCooldownPeriodMs,
                    mLastEmergencyGestureTriggered,
                    mLastEmergencyGestureTriggered + mEmergencyGesturePowerButtonCooldownPeriodMs));
            outLaunched.value = false;
            return true;
        }

        if (event.isLongPress()) {
            // Long presses are sent as a second key down. If the long press threshold is set lower
            // than the double tap of sequence interval thresholds, this could cause false double
            // taps or consecutive taps, so we want to ignore the long press event.
            outLaunched.value = false;
            return false;
        }
        boolean launchCamera = false;
@@ -509,6 +571,12 @@ public class GestureLauncherService extends SystemService {
            Slog.i(TAG, "Emergency gesture detected, launching.");
            launchEmergencyGesture = handleEmergencyGesture();
            mUiEventLogger.log(GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER);
            // Record emergency trigger time if emergency UI was launched
            if (launchEmergencyGesture) {
                synchronized (this) {
                    mLastEmergencyGestureTriggered = event.getEventTime();
                }
            }
        }
        mMetricsLogger.histogram("power_consecutive_short_tap_count",
                mPowerButtonSlowConsecutiveTaps);
@@ -600,6 +668,7 @@ public class GestureLauncherService extends SystemService {
                updateCameraRegistered();
                updateCameraDoubleTapPowerEnabled();
                updateEmergencyGestureEnabled();
                updateEmergencyGesturePowerButtonCooldownPeriodMs();
            }
        }
    };
@@ -610,6 +679,7 @@ public class GestureLauncherService extends SystemService {
                updateCameraRegistered();
                updateCameraDoubleTapPowerEnabled();
                updateEmergencyGestureEnabled();
                updateEmergencyGesturePowerButtonCooldownPeriodMs();
            }
        }
    };
+276 −0
Original line number Diff line number Diff line
@@ -186,6 +186,30 @@ public class GestureLauncherServiceTest {
                mContext, FAKE_USER_ID));
    }

    @Test
    public void testGetEmergencyGesturePowerButtonCooldownPeriodMs_enabled() {
        withEmergencyGesturePowerButtonCooldownPeriodMsValue(4000);
        assertEquals(4000,
                mGestureLauncherService.getEmergencyGesturePowerButtonCooldownPeriodMs(mContext,
                        FAKE_USER_ID));
    }

    @Test
    public void testGetEmergencyGesturePowerButtonCooldownPeriodMs_disabled() {
        withEmergencyGesturePowerButtonCooldownPeriodMsValue(0);
        assertEquals(0,
                mGestureLauncherService.getEmergencyGesturePowerButtonCooldownPeriodMs(mContext,
                        FAKE_USER_ID));
    }

    @Test
    public void testGetEmergencyGesturePowerButtonCooldownPeriodMs_cappedAtMaximum() {
        withEmergencyGesturePowerButtonCooldownPeriodMsValue(10000);
        assertEquals(GestureLauncherService.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX,
                mGestureLauncherService.getEmergencyGesturePowerButtonCooldownPeriodMs(mContext,
                        FAKE_USER_ID));
    }

    @Test
    public void testHandleCameraLaunchGesture_userSetupComplete() {
        withUserSetupCompleteValue(true);
@@ -644,6 +668,211 @@ public class GestureLauncherServiceTest {
        }
    }

    @Test
    public void testInterceptPowerKeyDown_triggerEmergency_singleTaps_cooldownTriggered() {
        // Enable power button cooldown
        withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000);
        mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();

        // Trigger emergency by tapping button 5 times
        long eventTime = triggerEmergencyGesture();

        // Add enough interval to reset consecutive tap count
        long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
        eventTime += interval;

        // Subsequent single tap is intercepted, but should not trigger any gesture
        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                IGNORED_REPEAT);
        boolean interactive = true;
        MutableBoolean outLaunched = new MutableBoolean(true);
        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
                outLaunched);
        assertTrue(intercepted);
        assertFalse(outLaunched.value);

        // Add enough interval to reset consecutive tap count
        interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
        eventTime += interval;

        // Another single tap should be the same (intercepted but should not trigger gesture)
        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                IGNORED_REPEAT);
        interactive = true;
        outLaunched = new MutableBoolean(true);
        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
                outLaunched);
        assertTrue(intercepted);
        assertFalse(outLaunched.value);
    }

    @Test
    public void
    testInterceptPowerKeyDown_triggerEmergency_cameraGestureEnabled_doubleTap_cooldownTriggered() {
        // Enable camera double tap gesture
        withCameraDoubleTapPowerEnableConfigValue(true);
        withCameraDoubleTapPowerDisableSettingValue(0);
        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();

        // Enable power button cooldown
        withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000);
        mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();

        // Trigger emergency by tapping button 5 times
        long eventTime = triggerEmergencyGesture();

        // Add enough interval to reset consecutive tap count
        long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
        eventTime += interval;

        // Subsequent double tap is intercepted, but should not trigger any gesture
        for (int i = 0; i < 2; i++) {
            KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION,
                    IGNORED_CODE, IGNORED_REPEAT);
            boolean interactive = true;
            MutableBoolean outLaunched = new MutableBoolean(true);
            boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent,
                    interactive, outLaunched);
            assertTrue(intercepted);
            assertFalse(outLaunched.value);
            interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
            eventTime += interval;
        }
    }

    @Test
    public void testInterceptPowerKeyDown_triggerEmergency_fiveTaps_cooldownTriggered() {
        // Enable power button cooldown
        withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000);
        mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();

        // Trigger emergency by tapping button 5 times
        long eventTime = triggerEmergencyGesture();

        // Add enough interval to reset consecutive tap count
        long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
        eventTime += interval;

        // Subsequent 5 taps are intercepted, but should not trigger any gesture
        for (int i = 0; i < 5; i++) {
            KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION,
                    IGNORED_CODE, IGNORED_REPEAT);
            boolean interactive = true;
            MutableBoolean outLaunched = new MutableBoolean(true);
            boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent,
                    interactive, outLaunched);
            assertTrue(intercepted);
            assertFalse(outLaunched.value);
            interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
            eventTime += interval;
        }
    }

    @Test
    public void testInterceptPowerKeyDown_triggerEmergency_longPress_cooldownTriggered() {
        // Enable power button cooldown
        withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000);
        mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();

        // Trigger emergency by tapping button 5 times
        long eventTime = triggerEmergencyGesture();

        // Add enough interval to reset consecutive tap count
        long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
        eventTime += interval;

        // 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);

        boolean interactive = true;
        MutableBoolean outLaunched = new MutableBoolean(true);
        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
                outLaunched);
        assertTrue(intercepted);
        assertFalse(outLaunched.value);
    }

    @Test
    public void testInterceptPowerKeyDown_triggerEmergency_cooldownDisabled_cooldownNotTriggered() {
        // Disable power button cooldown by setting cooldown period to 0
        withEmergencyGesturePowerButtonCooldownPeriodMsValue(0);
        mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();

        // Trigger emergency by tapping button 5 times
        long eventTime = triggerEmergencyGesture();

        // Add enough interval to reset consecutive tap count
        long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
        eventTime += interval;

        // Subsequent single tap is NOT intercepted
        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                IGNORED_REPEAT);
        boolean interactive = true;
        MutableBoolean outLaunched = new MutableBoolean(true);
        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
                outLaunched);
        assertFalse(intercepted);
        assertFalse(outLaunched.value);

        // Add enough interval to reset consecutive tap count
        interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
        eventTime += interval;

        // Long press also NOT intercepted
        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);
        interactive = true;
        outLaunched = new MutableBoolean(true);
        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
                outLaunched);
        assertFalse(intercepted);
        assertFalse(outLaunched.value);
    }

    @Test
    public void
    testInterceptPowerKeyDown_triggerEmergency_outsideCooldownPeriod_cooldownNotTriggered() {
        // Enable power button cooldown
        withEmergencyGesturePowerButtonCooldownPeriodMsValue(5000);
        mGestureLauncherService.updateEmergencyGesturePowerButtonCooldownPeriodMs();

        // Trigger emergency by tapping button 5 times
        long eventTime = triggerEmergencyGesture();

        // Add enough interval to be outside of cooldown period
        long interval = 5001;
        eventTime += interval;

        // Subsequent single tap is NOT intercepted
        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                IGNORED_REPEAT);
        boolean interactive = true;
        MutableBoolean outLaunched = new MutableBoolean(true);
        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
                outLaunched);
        assertFalse(intercepted);
        assertFalse(outLaunched.value);

        // Add enough interval to reset consecutive tap count
        interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS + 1;
        eventTime += interval;

        // Long press also NOT intercepted
        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);
        interactive = true;
        outLaunched = new MutableBoolean(true);
        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
                outLaunched);
        assertFalse(intercepted);
        assertFalse(outLaunched.value);
    }

    @Test
    public void testInterceptPowerKeyDown_longpress() {
        withCameraDoubleTapPowerEnableConfigValue(true);
@@ -1153,6 +1382,45 @@ public class GestureLauncherServiceTest {
        assertEquals(1, tapCounts.get(1).intValue());
    }

    /**
     * Helper method to trigger emergency gesture by pressing button for 5 times.
     * @return last event time.
     */
    private long triggerEmergencyGesture() {
        // Enable emergency power gesture
        withEmergencyGestureEnabledConfigValue(true);
        withEmergencyGestureEnabledSettingValue(true);
        mGestureLauncherService.updateEmergencyGestureEnabled();
        withUserSetupCompleteValue(true);

        // 4 button presses
        long eventTime = INITIAL_EVENT_TIME_MILLIS;
        boolean interactive = true;
        KeyEvent keyEvent;
        MutableBoolean outLaunched = new MutableBoolean(false);
        for (int i = 0; i < 4; i++) {
            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;
        }

        // 5th button press should trigger the emergency flow
        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                IGNORED_REPEAT);
        outLaunched.value = false;
        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
                outLaunched);
        assertTrue(outLaunched.value);
        assertTrue(intercepted);
        verify(mUiEventLogger, times(1))
                .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER);
        verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected();

        return eventTime;
    }

    private void withCameraDoubleTapPowerEnableConfigValue(boolean enableConfigValue) {
        when(mResources.getBoolean(
                com.android.internal.R.bool.config_cameraDoubleTapPowerGestureEnabled))
@@ -1181,6 +1449,14 @@ public class GestureLauncherServiceTest {
                UserHandle.USER_CURRENT);
    }

    private void withEmergencyGesturePowerButtonCooldownPeriodMsValue(int period) {
        Settings.Secure.putIntForUser(
                mContentResolver,
                Settings.Secure.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
                period,
                UserHandle.USER_CURRENT);
    }

    private void withUserSetupCompleteValue(boolean userSetupComplete) {
        int userSetupCompleteValue = userSetupComplete ? 1 : 0;
        Settings.Secure.putIntForUser(