diff --git a/services/core/java/com/android/server/policy/KeyCombinationManager.java b/services/core/java/com/android/server/policy/KeyCombinationManager.java index 7f55723cda0b2..84ac12497e71e 100644 --- a/services/core/java/com/android/server/policy/KeyCombinationManager.java +++ b/services/core/java/com/android/server/policy/KeyCombinationManager.java @@ -102,11 +102,9 @@ public class KeyCombinationManager { } /** - * Check if the key event could be intercepted by combination key rule before it is dispatched - * to a window. - * Return true if any active rule could be triggered by the key event, otherwise false. + * Check if the key event could be triggered by combine key rule before dispatching to a window. */ - boolean interceptKey(KeyEvent event, boolean interactive) { + void interceptKey(KeyEvent event, boolean interactive) { final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; final int keyCode = event.getKeyCode(); final int count = mActiveRules.size(); @@ -119,9 +117,9 @@ public class KeyCombinationManager { // exceed time from first key down. forAllRules(mActiveRules, (rule)-> rule.cancel()); mActiveRules.clear(); - return false; + return; } else if (count == 0) { // has some key down but no active rule exist. - return false; + return; } } @@ -129,7 +127,7 @@ public class KeyCombinationManager { mDownTimes.put(keyCode, eventTime); } else { // ignore old key, maybe a repeat key. - return false; + return; } if (mDownTimes.size() == 1) { @@ -143,7 +141,7 @@ public class KeyCombinationManager { } else { // Ignore if rule already triggered. if (mTriggeredRule != null) { - return true; + return; } // check if second key can trigger rule, or remove the non-match rule. @@ -158,7 +156,6 @@ public class KeyCombinationManager { mActiveRules.clear(); if (mTriggeredRule != null) { mActiveRules.add(mTriggeredRule); - return true; } } } else { @@ -171,7 +168,6 @@ public class KeyCombinationManager { } } } - return false; } /** diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 1b192e43c7b86..c50efc70a6bc4 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -73,8 +73,6 @@ import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY; -import static com.android.server.policy.SingleKeyGestureDetector.KEY_LONGPRESS; -import static com.android.server.policy.SingleKeyGestureDetector.KEY_VERYLONGPRESS; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED; @@ -458,6 +456,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { volatile boolean mPowerKeyHandled; volatile boolean mBackKeyHandled; volatile boolean mBeganFromNonInteractive; + volatile int mPowerKeyPressCounter; volatile boolean mEndCallKeyHandled; volatile boolean mCameraGestureTriggeredDuringGoingToSleep; volatile boolean mGoingToSleep; @@ -498,6 +497,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mHasSoftInput = false; boolean mHapticTextHandleEnabled; boolean mUseTvRouting; + int mVeryLongPressTimeout; boolean mAllowStartActivityForLongPressOnPowerDuringSetup; MetricsLogger mLogger; boolean mWakeOnDpadKeyPress; @@ -595,13 +595,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { private final com.android.internal.policy.LogDecelerateInterpolator mLogDecelerateInterpolator = new LogDecelerateInterpolator(100, 0); + private final MutableBoolean mTmpBoolean = new MutableBoolean(false); + private boolean mPerDisplayFocusEnabled = false; private volatile int mTopFocusedDisplayId = INVALID_DISPLAY; private int mPowerButtonSuppressionDelayMillis = POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS; private KeyCombinationManager mKeyCombinationManager; - private SingleKeyGestureDetector mSingleKeyGestureDetector; private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3; private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4; @@ -612,7 +613,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final int MSG_DISPATCH_SHOW_GLOBAL_ACTIONS = 10; private static final int MSG_HIDE_BOOT_MESSAGE = 11; private static final int MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK = 12; + private static final int MSG_POWER_DELAYED_PRESS = 13; + private static final int MSG_POWER_LONG_PRESS = 14; private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 15; + private static final int MSG_BACK_LONG_PRESS = 16; private static final int MSG_ACCESSIBILITY_SHORTCUT = 17; private static final int MSG_BUGREPORT_TV = 18; private static final int MSG_ACCESSIBILITY_TV = 19; @@ -620,7 +624,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final int MSG_SYSTEM_KEY_PRESS = 21; private static final int MSG_HANDLE_ALL_APPS = 22; private static final int MSG_LAUNCH_ASSIST = 23; - private static final int MSG_RINGER_TOGGLE_CHORD = 24; + private static final int MSG_POWER_VERY_LONG_PRESS = 25; + private static final int MSG_RINGER_TOGGLE_CHORD = 26; private class PolicyHandler extends Handler { @Override @@ -661,9 +666,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { case MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK: launchVoiceAssistWithWakeLock(); break; + case MSG_POWER_DELAYED_PRESS: + powerPress((Long) msg.obj, msg.arg1 != 0, msg.arg2); + finishPowerKeyPress(); + break; + case MSG_POWER_LONG_PRESS: + powerLongPress((Long) msg.obj /* eventTime */); + break; + case MSG_POWER_VERY_LONG_PRESS: + powerVeryLongPress(); + break; case MSG_SHOW_PICTURE_IN_PICTURE_MENU: showPictureInPictureMenuInternal(); break; + case MSG_BACK_LONG_PRESS: + backLongPress(); + break; case MSG_ACCESSIBILITY_SHORTCUT: accessibilityShortcutActivated(); break; @@ -774,6 +792,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { } }; + private Runnable mPossibleVeryLongPressReboot = new Runnable() { + @Override + public void run() { + mActivityManagerInternal.prepareForPossibleShutdown(); + } + }; + private void handleRingerChordGesture() { if (mRingerToggleChord == VOLUME_HUSH_OFF) { return; @@ -813,13 +838,28 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private void interceptBackKeyDown() { + mLogger.count("key_back_down", 1); + // Reset back key state for long press + mBackKeyHandled = false; + + if (hasLongPressOnBackBehavior()) { + Message msg = mHandler.obtainMessage(MSG_BACK_LONG_PRESS); + msg.setAsynchronous(true); + mHandler.sendMessageDelayed(msg, + ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout()); + } + } // returns true if the key was handled and should not be passed to the user - private boolean backKeyPress() { - mLogger.count("key_back_press", 1); + private boolean interceptBackKeyUp(KeyEvent event) { + mLogger.count("key_back_up", 1); // Cache handled state boolean handled = mBackKeyHandled; + // Reset back long press state + cancelPendingBackKeyAction(); + if (mHasFeatureWatch) { TelecomManager telecomManager = getTelecommService(); @@ -841,9 +881,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - if (mAutofillManagerInternal != null) { + if (mAutofillManagerInternal != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) { mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_BACK_KEY_TO_AUTOFILL)); } + return handled; } @@ -853,6 +894,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { mPowerKeyWakeLock.acquire(); } + // Cancel multi-press detection timeout. + if (mPowerKeyPressCounter != 0) { + mHandler.removeMessages(MSG_POWER_DELAYED_PRESS); + } + mWindowManagerFuncs.onPowerKeyDown(interactive); // Stop ringing or end call if configured to do so when power is pressed. @@ -874,20 +920,71 @@ public class PhoneWindowManager implements WindowManagerPolicy { final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event); + GestureLauncherService gestureService = LocalServices.getService( + GestureLauncherService.class); + boolean gesturedServiceIntercepted = false; + if (gestureService != null) { + gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive, + mTmpBoolean); + if (mTmpBoolean.value && mRequestedOrGoingToSleep) { + mCameraGestureTriggeredDuringGoingToSleep = true; + } + } + // Inform the StatusBar; but do not allow it to consume the event. sendSystemKeyToStatusBarAsync(event.getKeyCode()); + schedulePossibleVeryLongPressReboot(); + // If the power key has still not yet been handled, then detect short // press, long press, or multi press and decide what to do. - mPowerKeyHandled = mPowerKeyHandled || hungUp + mPowerKeyHandled = hungUp || gesturedServiceIntercepted || handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted(); if (!mPowerKeyHandled) { - if (!interactive) { + if (interactive) { + // When interactive, we're already awake. + // Wait for a long press or for the button to be released to decide what to do. + if (hasLongPressOnPowerBehavior()) { + if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) { + powerLongPress(event.getEventTime()); + } else { + Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS, + event.getEventTime()); + msg.setAsynchronous(true); + mHandler.sendMessageDelayed(msg, + ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout()); + + if (hasVeryLongPressOnPowerBehavior()) { + Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS); + longMsg.setAsynchronous(true); + mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout); + } + } + } + } else { wakeUpFromPowerKey(event.getDownTime()); + if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) { + if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) { + powerLongPress(event.getEventTime()); + } else { + Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS, + event.getEventTime()); + msg.setAsynchronous(true); + mHandler.sendMessageDelayed(msg, + ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout()); + + if (hasVeryLongPressOnPowerBehavior()) { + Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS); + longMsg.setAsynchronous(true); + mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout); + } + } + mBeganFromNonInteractive = true; } else { final int maxCount = getMaxMultiPressPowerCount(); + if (maxCount <= 1) { mPowerKeyHandled = true; } else { @@ -895,38 +992,68 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } } - } else { - // handled by another power key policy. - if (!mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) { - mSingleKeyGestureDetector.reset(); - } } } private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) { final boolean handled = canceled || mPowerKeyHandled; + cancelPendingPowerKeyAction(); if (!handled) { if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) == 0) { // Abort possibly stuck animations only when power key up without long press case. mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe); } - } else { - // handled by single key or another power key policy. - mSingleKeyGestureDetector.reset(); - finishPowerKeyPress(); + + // Figure out how to handle the key now that it has been released. + mPowerKeyPressCounter += 1; + + final int maxCount = getMaxMultiPressPowerCount(); + final long eventTime = event.getDownTime(); + if (mPowerKeyPressCounter < maxCount) { + // This could be a multi-press. Wait a little bit longer to confirm. + // Continue holding the wake lock. + Message msg = mHandler.obtainMessage(MSG_POWER_DELAYED_PRESS, + interactive ? 1 : 0, mPowerKeyPressCounter, eventTime); + msg.setAsynchronous(true); + mHandler.sendMessageDelayed(msg, ViewConfiguration.getMultiPressTimeout()); + return; + } + + // No other actions. Handle it immediately. + powerPress(eventTime, interactive, mPowerKeyPressCounter); } + // Done. Reset our state. + finishPowerKeyPress(); } private void finishPowerKeyPress() { mBeganFromNonInteractive = false; - mPowerKeyHandled = false; + mPowerKeyPressCounter = 0; if (mPowerKeyWakeLock.isHeld()) { mPowerKeyWakeLock.release(); } } + private void cancelPendingPowerKeyAction() { + if (!mPowerKeyHandled) { + mPowerKeyHandled = true; + mHandler.removeMessages(MSG_POWER_LONG_PRESS); + } + if (hasVeryLongPressOnPowerBehavior()) { + mHandler.removeMessages(MSG_POWER_VERY_LONG_PRESS); + } + cancelPossibleVeryLongPressReboot(); + } + + private void cancelPendingBackKeyAction() { + if (!mBackKeyHandled) { + mBackKeyHandled = true; + mHandler.removeMessages(MSG_BACK_LONG_PRESS); + } + } + private void powerPress(long eventTime, boolean interactive, int count) { if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) { Slog.i(TAG, "Suppressed redundant power key press while " @@ -1077,7 +1204,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { private void powerLongPress(long eventTime) { final int behavior = getResolvedLongPressOnPowerBehavior(); - switch (behavior) { case LONG_PRESS_POWER_NOTHING: break; @@ -1716,6 +1842,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.integer.config_triplePressOnPowerBehavior); mShortPressOnSleepBehavior = mContext.getResources().getInteger( com.android.internal.R.integer.config_shortPressOnSleepBehavior); + mVeryLongPressTimeout = mContext.getResources().getInteger( + com.android.internal.R.integer.config_veryLongPressTimeout); mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean( com.android.internal.R.bool.config_allowStartActivityForLongPressOnPowerInSetup); @@ -1809,7 +1937,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } }); initKeyCombinationRules(); - initSingleKeyGestureRules(); } private void initKeyCombinationRules() { @@ -1822,7 +1949,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) { @Override void execute() { - mPowerKeyHandled = true; + cancelPendingPowerKeyAction(); interceptScreenshotChord(); } @Override @@ -1857,7 +1984,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override void execute() { - mPowerKeyHandled = true; + cancelPendingPowerKeyAction(); interceptRingerToggleChord(); } @Override @@ -1871,7 +1998,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { new TwoKeysCombinationRule(KEYCODE_BACK, KEYCODE_DPAD_DOWN) { @Override void execute() { - mBackKeyHandled = true; + cancelPendingBackKeyAction(); interceptAccessibilityGestureTv(); } @@ -1885,7 +2012,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { new TwoKeysCombinationRule(KEYCODE_DPAD_CENTER, KEYCODE_BACK) { @Override void execute() { - mBackKeyHandled = true; + cancelPendingBackKeyAction(); interceptBugreportGestureTv(); } @@ -1897,84 +2024,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - /** - * Rule for single power key gesture. - */ - private final class PowerKeyRule extends SingleKeyGestureDetector.SingleKeyRule { - PowerKeyRule(int gestures) { - super(KEYCODE_POWER, gestures); - } - - @Override - int getMaxMultiPressCount() { - return getMaxMultiPressPowerCount(); - } - - @Override - void onPress(long downTime) { - powerPress(downTime, true, 1 /*count*/); - finishPowerKeyPress(); - } - - @Override - void onLongPress(long downTime) { - powerLongPress(downTime); - } - - @Override - void onVeryLongPress(long downTime) { - mActivityManagerInternal.prepareForPossibleShutdown(); - powerVeryLongPress(); - } - - @Override - void onMultiPress(long downTime, int count) { - powerPress(downTime, true, count); - finishPowerKeyPress(); - } - } - - /** - * Rule for single back key gesture. - */ - private final class BackKeyRule extends SingleKeyGestureDetector.SingleKeyRule { - BackKeyRule(int gestures) { - super(KEYCODE_BACK, gestures); - } - - @Override - int getMaxMultiPressCount() { - return 1; - } - - @Override - void onPress(long downTime) { - mBackKeyHandled |= backKeyPress(); - } - - @Override - void onLongPress(long downTime) { - backLongPress(); - } - } - - private void initSingleKeyGestureRules() { - mSingleKeyGestureDetector = new SingleKeyGestureDetector(mContext); - - int powerKeyGestures = 0; - if (hasVeryLongPressOnPowerBehavior()) { - powerKeyGestures |= KEY_VERYLONGPRESS; - } - if (hasLongPressOnPowerBehavior()) { - powerKeyGestures |= KEY_LONGPRESS; - } - mSingleKeyGestureDetector.addRule(new PowerKeyRule(powerKeyGestures)); - - if (hasLongPressOnBackBehavior()) { - mSingleKeyGestureDetector.addRule(new BackKeyRule(KEY_LONGPRESS)); - } - } - /** * Read values from config.xml that may be overridden depending on * the configuration of the device. @@ -3519,21 +3568,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { return result; } - // Alternate TV power to power key for Android TV device. - final HdmiControlManager hdmiControlManager = getHdmiControlManager(); - if (keyCode == KeyEvent.KEYCODE_TV_POWER && mHasFeatureLeanback - && (hdmiControlManager == null || !hdmiControlManager.shouldHandleTvPowerKey())) { - event = KeyEvent.obtain( - event.getDownTime(), event.getEventTime(), - event.getAction(), KeyEvent.KEYCODE_POWER, - event.getRepeatCount(), event.getMetaState(), - event.getDeviceId(), event.getScanCode(), - event.getFlags(), event.getSource(), event.getDisplayId(), null); - return interceptKeyBeforeQueueing(event, policyFlags); - } - if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { - handleKeyGesture(event, interactive); + mKeyCombinationManager.interceptKey(event, interactive); } // Enable haptics if down and virtual key without multiple repetitions. If this is a hard @@ -3548,13 +3584,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { switch (keyCode) { case KeyEvent.KEYCODE_BACK: { if (down) { - mBackKeyHandled = false; + interceptBackKeyDown(); } else { - if (!hasLongPressOnBackBehavior()) { - mBackKeyHandled |= backKeyPress(); - } + boolean handled = interceptBackKeyUp(event); + // Don't pass back press to app if we've already handled it via long press - if (mBackKeyHandled) { + if (handled) { result &= ~ACTION_PASS_TO_USER; } } @@ -3666,17 +3701,33 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_TV_POWER: { result &= ~ACTION_PASS_TO_USER; isWakeKey = false; // wake-up will be handled separately - if (down && hdmiControlManager != null) { - hdmiControlManager.toggleAndFollowTvPower(); + HdmiControlManager hdmiControlManager = getHdmiControlManager(); + if (hdmiControlManager != null && hdmiControlManager.shouldHandleTvPowerKey()) { + if (down) { + hdmiControlManager.toggleAndFollowTvPower(); + } + } else if (mHasFeatureLeanback) { + KeyEvent fallbackEvent = KeyEvent.obtain( + event.getDownTime(), event.getEventTime(), + event.getAction(), KeyEvent.KEYCODE_POWER, + event.getRepeatCount(), event.getMetaState(), + event.getDeviceId(), event.getScanCode(), + event.getFlags(), event.getSource(), event.getDisplayId(), null); + if (down) { + interceptPowerKeyDown(fallbackEvent, interactive); + } else { + interceptPowerKeyUp(fallbackEvent, interactive, canceled); + } } + // Ignore this key for any device that is not connected to a TV via HDMI and + // not an Android TV device. break; } case KeyEvent.KEYCODE_POWER: { EventLogTags.writeInterceptPower( KeyEvent.actionToString(event.getAction()), - mPowerKeyHandled ? 1 : 0, - mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER)); + mPowerKeyHandled ? 1 : 0, mPowerKeyPressCounter); // Any activity on the power button stops the accessibility shortcut result &= ~ACTION_PASS_TO_USER; isWakeKey = false; // wake-up will be handled separately @@ -3857,43 +3908,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { return result; } - private void handleKeyGesture(KeyEvent event, boolean interactive) { - if (mKeyCombinationManager.interceptKey(event, interactive)) { - // handled by combo keys manager. - mSingleKeyGestureDetector.reset(); - return; - } - - if (event.getKeyCode() == KEYCODE_POWER && event.getAction() == KeyEvent.ACTION_DOWN) { - mPowerKeyHandled = handleCameraGesture(event, interactive); - if (mPowerKeyHandled) { - // handled by camera gesture. - mSingleKeyGestureDetector.reset(); - return; - } - } - - mSingleKeyGestureDetector.interceptKey(event); - } - - // The camera gesture will be detected by GestureLauncherService. - private boolean handleCameraGesture(KeyEvent event, boolean interactive) { - // camera gesture. - GestureLauncherService gestureService = LocalServices.getService( - GestureLauncherService.class); - if (gestureService == null) { - return false; - } - - final MutableBoolean outLaunched = new MutableBoolean(false); - final boolean gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, - interactive, outLaunched); - if (outLaunched.value && mRequestedOrGoingToSleep) { - mCameraGestureTriggeredDuringGoingToSleep = true; - } - return gesturedServiceIntercepted; - } - /** * Handle statusbar expansion events. * @param event @@ -4893,6 +4907,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private void schedulePossibleVeryLongPressReboot() { + mHandler.removeCallbacks(mPossibleVeryLongPressReboot); + mHandler.postDelayed(mPossibleVeryLongPressReboot, mVeryLongPressTimeout); + } + + private void cancelPossibleVeryLongPressReboot() { + mHandler.removeCallbacks(mPossibleVeryLongPressReboot); + } + // TODO (multidisplay): Support multiple displays in WindowManagerPolicy. private void updateScreenOffSleepToken(boolean acquire) { if (acquire) { diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java deleted file mode 100644 index 3dafb0ce21ef0..0000000000000 --- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.policy; - -import android.annotation.IntDef; -import android.content.Context; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.util.Log; -import android.view.KeyEvent; -import android.view.ViewConfiguration; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; - -/** - * Detect single key gesture: press, long press, very long press and multi press. - * - * Call {@link #reset} if current {@link KeyEvent} has been handled by another policy - */ - -public final class SingleKeyGestureDetector { - private static final String TAG = "SingleKeyGesture"; - private static final boolean DEBUG = false; - - private static final int MSG_KEY_LONG_PRESS = 0; - private static final int MSG_KEY_VERY_LONG_PRESS = 1; - private static final int MSG_KEY_DELAYED_PRESS = 2; - - private final long mLongPressTimeout; - private final long mVeryLongPressTimeout; - - private volatile int mKeyPressCounter; - - private final ArrayList mRules = new ArrayList(); - private SingleKeyRule mActiveRule = null; - - // Key code of current key down event, reset when key up. - private int mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN; - private volatile boolean mHandledByLongPress = false; - private final Handler mHandler; - private static final long MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout(); - - - /** Supported gesture flags */ - public static final int KEY_LONGPRESS = 1 << 1; - public static final int KEY_VERYLONGPRESS = 1 << 2; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = { "KEY_" }, value = { - KEY_LONGPRESS, - KEY_VERYLONGPRESS, - }) - public @interface KeyGestureFlag {} - - /** - * Rule definition for single keys gesture. - * E.g : define power key. - *
-     *  SingleKeyRule rule =
-     *      new SingleKeyRule(KEYCODE_POWER, KEY_LONGPRESS|KEY_VERYLONGPRESS) {
-     *           int getMaxMultiPressCount() { // maximum multi press count. }
-     *           void onPress(long downTime) { // short press behavior. }
-     *           void onLongPress() { // long press behavior. }
-     *           void onVeryLongPress() { // very long press behavior. }
-     *           void onMultiPress(long downTime, int count) { // multi press behavior.  }
-     *       };
-     *  
- */ - abstract static class SingleKeyRule { - private final int mKeyCode; - private final int mSupportedGestures; - - SingleKeyRule(int keyCode, @KeyGestureFlag int supportedGestures) { - mKeyCode = keyCode; - mSupportedGestures = supportedGestures; - } - - /** - * True if the rule could intercept the key. - */ - private boolean shouldInterceptKey(int keyCode) { - return keyCode == mKeyCode; - } - - /** - * True if the rule support long press. - */ - private boolean supportLongPress() { - return (mSupportedGestures & KEY_LONGPRESS) != 0; - } - - /** - * True if the rule support very long press. - */ - private boolean supportVeryLongPress() { - return (mSupportedGestures & KEY_VERYLONGPRESS) != 0; - } - - /** - * Maximum count of multi presses. - * Return 1 will trigger onPress immediately when {@link KeyEvent.ACTION_UP}. - * Otherwise trigger onMultiPress immediately when reach max count when - * {@link KeyEvent.ACTION_DOWN}. - */ - int getMaxMultiPressCount() { - return 1; - } - - /** - * Called when short press has been detected. - */ - abstract void onPress(long downTime); - /** - * Callback when multi press (>= 2) has been detected. - */ - void onMultiPress(long downTime, int count) {} - /** - * Callback when long press has been detected. - */ - void onLongPress(long downTime) {} - /** - * Callback when very long press has been detected. - */ - void onVeryLongPress(long downTime) {} - - @Override - public String toString() { - return "KeyCode = " + KeyEvent.keyCodeToString(mKeyCode) - + ", long press : " + supportLongPress() - + ", very Long press : " + supportVeryLongPress() - + ", max multi press count : " + getMaxMultiPressCount(); - } - } - - public SingleKeyGestureDetector(Context context) { - mLongPressTimeout = ViewConfiguration.get(context).getDeviceGlobalActionKeyTimeout(); - mVeryLongPressTimeout = context.getResources().getInteger( - com.android.internal.R.integer.config_veryLongPressTimeout); - mHandler = new KeyHandler(); - } - - void addRule(SingleKeyRule rule) { - mRules.add(rule); - } - - void interceptKey(KeyEvent event) { - if (event.getAction() == KeyEvent.ACTION_DOWN) { - interceptKeyDown(event); - } else { - interceptKeyUp(event); - } - } - - private void interceptKeyDown(KeyEvent event) { - final int keyCode = event.getKeyCode(); - // same key down. - if (mDownKeyCode == keyCode) { - if (mActiveRule != null && (event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0 - && !mHandledByLongPress) { - if (DEBUG) { - Log.i(TAG, "Long press Key " + KeyEvent.keyCodeToString(keyCode)); - } - mHandledByLongPress = true; - mHandler.removeMessages(MSG_KEY_LONG_PRESS); - mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS); - mActiveRule.onLongPress(event.getEventTime()); - } - return; - } - - // When a different key is pressed, stop processing gestures for the currently active key. - if (mDownKeyCode != KeyEvent.KEYCODE_UNKNOWN - || (mActiveRule != null && !mActiveRule.shouldInterceptKey(keyCode))) { - if (DEBUG) { - Log.i(TAG, "Press another key " + KeyEvent.keyCodeToString(keyCode)); - } - - reset(); - } - mDownKeyCode = keyCode; - - // Picks a new rule, return if no rule picked. - if (mActiveRule == null) { - final int count = mRules.size(); - for (int index = 0; index < count; index++) { - final SingleKeyRule rule = mRules.get(index); - if (rule.shouldInterceptKey(keyCode)) { - mActiveRule = rule; - break; - } - } - } - if (mActiveRule == null) { - return; - } - - final long eventTime = event.getEventTime(); - if (mKeyPressCounter == 0) { - if (mActiveRule.supportLongPress()) { - final Message msg = mHandler.obtainMessage(MSG_KEY_LONG_PRESS, keyCode, 0, - eventTime); - msg.setAsynchronous(true); - mHandler.sendMessageDelayed(msg, mLongPressTimeout); - } - - if (mActiveRule.supportVeryLongPress()) { - final Message msg = mHandler.obtainMessage(MSG_KEY_VERY_LONG_PRESS, keyCode, 0, - eventTime); - msg.setAsynchronous(true); - mHandler.sendMessageDelayed(msg, mVeryLongPressTimeout); - } - } else { - mHandler.removeMessages(MSG_KEY_LONG_PRESS); - mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS); - mHandler.removeMessages(MSG_KEY_DELAYED_PRESS); - - // Trigger multi press immediately when reach max count.( > 1) - if (mKeyPressCounter == mActiveRule.getMaxMultiPressCount() - 1) { - if (DEBUG) { - Log.i(TAG, "Trigger multi press " + mActiveRule.toString() + " for it" - + " reach the max count " + mKeyPressCounter); - } - mActiveRule.onMultiPress(eventTime, mKeyPressCounter + 1); - mKeyPressCounter = 0; - } - } - } - - private boolean interceptKeyUp(KeyEvent event) { - mHandler.removeMessages(MSG_KEY_LONG_PRESS); - mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS); - mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN; - if (mActiveRule == null) { - return false; - } - - if (mHandledByLongPress) { - mHandledByLongPress = false; - return true; - } - - final long downTime = event.getDownTime(); - if (event.getKeyCode() == mActiveRule.mKeyCode) { - // Directly trigger short press when max count is 1. - if (mActiveRule.getMaxMultiPressCount() == 1) { - mActiveRule.onPress(downTime); - return true; - } - - // This could be a multi-press. Wait a little bit longer to confirm. - mKeyPressCounter++; - Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, mActiveRule.mKeyCode, - mKeyPressCounter, downTime); - msg.setAsynchronous(true); - mHandler.sendMessageDelayed(msg, MULTI_PRESS_TIMEOUT); - return true; - } - reset(); - return false; - } - - int getKeyPressCounter(int keyCode) { - if (mActiveRule != null && mActiveRule.mKeyCode == keyCode) { - return mKeyPressCounter; - } else { - return 0; - } - } - - void reset() { - if (mActiveRule != null) { - if (mDownKeyCode != KeyEvent.KEYCODE_UNKNOWN) { - mHandler.removeMessages(MSG_KEY_LONG_PRESS); - mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS); - } - - if (mKeyPressCounter > 0) { - mHandler.removeMessages(MSG_KEY_DELAYED_PRESS); - mKeyPressCounter = 0; - } - mActiveRule = null; - } - - mHandledByLongPress = false; - mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN; - } - - boolean isKeyIntercepted(int keyCode) { - if (mActiveRule != null && mActiveRule.shouldInterceptKey(keyCode)) { - return mHandledByLongPress; - } - return false; - } - - private class KeyHandler extends Handler { - KeyHandler() { - super(Looper.getMainLooper()); - } - - @Override - public void handleMessage(Message msg) { - if (mActiveRule == null) { - return; - } - final int keyCode = msg.arg1; - final long eventTime = (long) msg.obj; - switch(msg.what) { - case MSG_KEY_LONG_PRESS: - if (DEBUG) { - Log.i(TAG, "Detect long press " + KeyEvent.keyCodeToString(keyCode)); - } - mHandledByLongPress = true; - mActiveRule.onLongPress(eventTime); - break; - case MSG_KEY_VERY_LONG_PRESS: - if (DEBUG) { - Log.i(TAG, "Detect very long press " - + KeyEvent.keyCodeToString(keyCode)); - } - mHandledByLongPress = true; - mActiveRule.onVeryLongPress(eventTime); - break; - case MSG_KEY_DELAYED_PRESS: - if (DEBUG) { - Log.i(TAG, "Detect press " + KeyEvent.keyCodeToString(keyCode) - + ", count " + mKeyPressCounter); - } - if (mKeyPressCounter == 1) { - mActiveRule.onPress(eventTime); - } else { - mActiveRule.onMultiPress(eventTime, mKeyPressCounter); - } - mKeyPressCounter = 0; - break; - } - } - } -} diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java deleted file mode 100644 index 3025a95be98c4..0000000000000 --- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.policy; - -import static android.view.KeyEvent.ACTION_DOWN; -import static android.view.KeyEvent.ACTION_UP; -import static android.view.KeyEvent.KEYCODE_POWER; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static com.android.server.policy.SingleKeyGestureDetector.KEY_LONGPRESS; -import static com.android.server.policy.SingleKeyGestureDetector.KEY_VERYLONGPRESS; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import android.app.Instrumentation; -import android.content.Context; -import android.os.SystemClock; -import android.view.KeyEvent; -import android.view.ViewConfiguration; - -import org.junit.Before; -import org.junit.Test; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * Test class for {@link SingleKeyGestureDetector}. - * - * Build/Install/Run: - * atest WmTests:SingleKeyGestureTests - */ -public class SingleKeyGestureTests { - private SingleKeyGestureDetector mDetector; - - private int mMaxMultiPressPowerCount = 2; - - private CountDownLatch mShortPressed = new CountDownLatch(1); - private CountDownLatch mLongPressed = new CountDownLatch(1); - private CountDownLatch mVeryLongPressed = new CountDownLatch(1); - private CountDownLatch mMultiPressed = new CountDownLatch(1); - - private final Instrumentation mInstrumentation = getInstrumentation(); - private final Context mContext = mInstrumentation.getTargetContext(); - private long mWaitTimeout; - private long mLongPressTime; - private long mVeryLongPressTime; - - @Before - public void setUp() { - mDetector = new SingleKeyGestureDetector(mContext); - initSingleKeyGestureRules(); - mWaitTimeout = ViewConfiguration.getMultiPressTimeout() + 50; - mLongPressTime = ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout() + 50; - mVeryLongPressTime = mContext.getResources().getInteger( - com.android.internal.R.integer.config_veryLongPressTimeout) + 50; - } - - private void initSingleKeyGestureRules() { - mDetector.addRule(new SingleKeyGestureDetector.SingleKeyRule(KEYCODE_POWER, - KEY_LONGPRESS | KEY_VERYLONGPRESS) { - @Override - int getMaxMultiPressCount() { - return mMaxMultiPressPowerCount; - } - @Override - public void onPress(long downTime) { - mShortPressed.countDown(); - } - - @Override - void onLongPress(long downTime) { - mLongPressed.countDown(); - } - - @Override - void onVeryLongPress(long downTime) { - mVeryLongPressed.countDown(); - } - - @Override - void onMultiPress(long downTime, int count) { - mMultiPressed.countDown(); - assertEquals(mMaxMultiPressPowerCount, count); - } - }); - } - - private void pressKey(long eventTime, int keyCode, long pressTime) { - final KeyEvent keyDown = new KeyEvent(eventTime, eventTime, ACTION_DOWN, - keyCode, 0 /* repeat */, 0 /* metaState */); - mDetector.interceptKey(keyDown); - - // keep press down. - try { - Thread.sleep(pressTime); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - eventTime += pressTime; - final KeyEvent keyUp = new KeyEvent(eventTime, eventTime, ACTION_UP, - keyCode, 0 /* repeat */, 0 /* metaState */); - - mDetector.interceptKey(keyUp); - } - - @Test - public void testShortPress() throws InterruptedException { - final long eventTime = SystemClock.uptimeMillis(); - pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */); - assertTrue(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); - } - - @Test - public void testLongPress() throws InterruptedException { - final long eventTime = SystemClock.uptimeMillis(); - pressKey(eventTime, KEYCODE_POWER, mLongPressTime); - assertTrue(mLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); - } - - @Test - public void testVeryLongPress() throws InterruptedException { - final long eventTime = SystemClock.uptimeMillis(); - pressKey(eventTime, KEYCODE_POWER, mVeryLongPressTime); - assertTrue(mVeryLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); - } - - @Test - public void testMultiPress() throws InterruptedException { - final long eventTime = SystemClock.uptimeMillis(); - pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */); - pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */); - assertTrue(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); - } -}