Revert "Introduce SingleKeyGestureDetector to PhoneWindowManager"

This reverts commit 29f27e8d8b.

Fixes: 181311329
Bug: 169058831
Bug: 127687575

Reason for revert: DroidMonitor: Potential culprit for 181311329 - verifying through Forrest before revert submission. This is part of the standard investigation process, and does not necessarily mean your CL will be reverted.

Change-Id: Ibebd4a2dcd5016ad2af7da5f296493214e06d171
This commit is contained in:
Adrian Roos
2021-02-26 13:30:41 +00:00
parent 29f27e8d8b
commit 14904f3bd6
4 changed files with 192 additions and 681 deletions

View File

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

View File

@@ -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;
@@ -597,13 +597,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;
@@ -614,7 +615,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;
@@ -622,7 +626,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
@@ -663,9 +668,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;
@@ -776,6 +794,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;
@@ -815,13 +840,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();
@@ -843,9 +883,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;
}
@@ -855,6 +896,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.
@@ -876,20 +922,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 {
@@ -897,38 +994,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 "
@@ -1079,7 +1206,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private void powerLongPress(long eventTime) {
final int behavior = getResolvedLongPressOnPowerBehavior();
switch (behavior) {
case LONG_PRESS_POWER_NOTHING:
break;
@@ -1718,6 +1844,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);
@@ -1811,7 +1939,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
});
initKeyCombinationRules();
initSingleKeyGestureRules();
}
private void initKeyCombinationRules() {
@@ -1824,7 +1951,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) {
@Override
void execute() {
mPowerKeyHandled = true;
cancelPendingPowerKeyAction();
interceptScreenshotChord();
}
@Override
@@ -1859,7 +1986,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
void execute() {
mPowerKeyHandled = true;
cancelPendingPowerKeyAction();
interceptRingerToggleChord();
}
@Override
@@ -1873,7 +2000,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
new TwoKeysCombinationRule(KEYCODE_BACK, KEYCODE_DPAD_DOWN) {
@Override
void execute() {
mBackKeyHandled = true;
cancelPendingBackKeyAction();
interceptAccessibilityGestureTv();
}
@@ -1887,7 +2014,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
new TwoKeysCombinationRule(KEYCODE_DPAD_CENTER, KEYCODE_BACK) {
@Override
void execute() {
mBackKeyHandled = true;
cancelPendingBackKeyAction();
interceptBugreportGestureTv();
}
@@ -1899,84 +2026,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.
@@ -3501,21 +3550,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
@@ -3530,13 +3566,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;
}
}
@@ -3648,17 +3683,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
@@ -3839,43 +3890,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
@@ -4875,6 +4889,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) {

View File

@@ -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<SingleKeyRule> 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.
* <pre class="prettyprint">
* 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. }
* };
* </pre>
*/
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;
}
}
}
}

View File

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