summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2012-05-09 13:34:28 -0700
committerJeff Brown <jeffbrown@google.com>2012-05-09 13:34:28 -0700
commitfd23e3ed976b22b9a92ddb2cb3a46f9d2a0ce23f (patch)
treeb3d9f4d12fe2d93ba9979e6d66b1d9f4de016178
parentaf67fc65bf113b028ff33e71cd6a45810018c273 (diff)
downloadframeworks_base-fd23e3ed976b22b9a92ddb2cb3a46f9d2a0ce23f.zip
frameworks_base-fd23e3ed976b22b9a92ddb2cb3a46f9d2a0ce23f.tar.gz
frameworks_base-fd23e3ed976b22b9a92ddb2cb3a46f9d2a0ce23f.tar.bz2
Fix bugs in fallback key handling.
If a fallback key is generated using a key plus a modifier, then it's possible we might get a different fallback key generated if the modifier has changed. PhoneWindowManager needs to remember which fallback is last generated for a given key code so that it can apply the same fallback action. When generating cancellation events, it's important to have preserved the policyFlags of the original event. Otherwise we may not dispatch the cancellation properly. For example, some actions are not performed if the POLICY_FLAG_TRUSTED is not specified. Remember the metaState associated with a key event so we can include it when canceled. Tell the policy when a fallback is being cancelled so that it can clean up its state. After a SEARCH shortcut is invoked, clear the flag indicating that a shortcut is pending. This is to prevent SEARCH from getting stuck down in the case where we might forget to send the up. (Shouldn't happen anymore after the prior fixes.) Bug: 5616255 Change-Id: I68f0a9679c7af464eaf31c099f2aa50b53fecf1f
-rw-r--r--core/java/android/view/KeyCharacterMap.java55
-rw-r--r--core/java/android/view/ViewRootImpl.java29
-rw-r--r--include/androidfw/Input.h2
-rwxr-xr-xpolicy/src/com/android/internal/policy/impl/PhoneWindowManager.java103
-rw-r--r--services/input/InputDispatcher.cpp40
-rw-r--r--services/input/InputDispatcher.h3
6 files changed, 158 insertions, 74 deletions
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index 5b371eb..2cb724f 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -386,19 +386,19 @@ public class KeyCharacterMap implements Parcelable {
*
* @param keyCode The key code.
* @param metaState The meta key modifier state.
- * @param outFallbackAction The fallback action object to populate.
- * @return True if a fallback action was found, false otherwise.
+ * @return The fallback action, or null if none. Remember to recycle the fallback action.
*
* @hide
*/
- public boolean getFallbackAction(int keyCode, int metaState,
- FallbackAction outFallbackAction) {
- if (outFallbackAction == null) {
- throw new IllegalArgumentException("fallbackAction must not be null");
- }
-
+ public FallbackAction getFallbackAction(int keyCode, int metaState) {
+ FallbackAction action = FallbackAction.obtain();
metaState = KeyEvent.normalizeMetaState(metaState);
- return nativeGetFallbackAction(mPtr, keyCode, metaState, outFallbackAction);
+ if (nativeGetFallbackAction(mPtr, keyCode, metaState, action)) {
+ action.metaState = KeyEvent.normalizeMetaState(action.metaState);
+ return action;
+ }
+ action.recycle();
+ return null;
}
/**
@@ -727,7 +727,44 @@ public class KeyCharacterMap implements Parcelable {
* @hide
*/
public static final class FallbackAction {
+ private static final int MAX_RECYCLED = 10;
+ private static final Object sRecycleLock = new Object();
+ private static FallbackAction sRecycleBin;
+ private static int sRecycledCount;
+
+ private FallbackAction next;
+
public int keyCode;
public int metaState;
+
+ private FallbackAction() {
+ }
+
+ public static FallbackAction obtain() {
+ final FallbackAction target;
+ synchronized (sRecycleLock) {
+ if (sRecycleBin == null) {
+ target = new FallbackAction();
+ } else {
+ target = sRecycleBin;
+ sRecycleBin = target.next;
+ sRecycledCount--;
+ target.next = null;
+ }
+ }
+ return target;
+ }
+
+ public void recycle() {
+ synchronized (sRecycleLock) {
+ if (sRecycledCount < MAX_RECYCLED) {
+ next = sRecycleBin;
+ sRecycleBin = this;
+ sRecycledCount += 1;
+ } else {
+ next = null;
+ }
+ }
+ }
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ec6bd81..e782f71 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -328,8 +328,6 @@ public final class ViewRootImpl implements ViewParent,
private final int mDensity;
- final KeyCharacterMap.FallbackAction mFallbackAction = new KeyCharacterMap.FallbackAction();
-
/**
* Consistency verifier for debugging purposes.
*/
@@ -4446,20 +4444,19 @@ public final class ViewRootImpl implements ViewParent,
final int keyCode = event.getKeyCode();
final int metaState = event.getMetaState();
- KeyEvent fallbackEvent = null;
- synchronized (mFallbackAction) {
- // Check for fallback actions specified by the key character map.
- if (kcm.getFallbackAction(keyCode, metaState, mFallbackAction)) {
- int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
- fallbackEvent = KeyEvent.obtain(
- event.getDownTime(), event.getEventTime(),
- event.getAction(), mFallbackAction.keyCode,
- event.getRepeatCount(), mFallbackAction.metaState,
- event.getDeviceId(), event.getScanCode(),
- flags, event.getSource(), null);
- }
- }
- if (fallbackEvent != null) {
+ // Check for fallback actions specified by the key character map.
+ KeyCharacterMap.FallbackAction fallbackAction =
+ kcm.getFallbackAction(keyCode, metaState);
+ if (fallbackAction != null) {
+ final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
+ KeyEvent fallbackEvent = KeyEvent.obtain(
+ event.getDownTime(), event.getEventTime(),
+ event.getAction(), fallbackAction.keyCode,
+ event.getRepeatCount(), fallbackAction.metaState,
+ event.getDeviceId(), event.getScanCode(),
+ flags, event.getSource(), null);
+ fallbackAction.recycle();
+
dispatchKey(fallbackEvent);
}
}
diff --git a/include/androidfw/Input.h b/include/androidfw/Input.h
index 601a169..6d03fd6 100644
--- a/include/androidfw/Input.h
+++ b/include/androidfw/Input.h
@@ -262,6 +262,8 @@ public:
inline int32_t getFlags() const { return mFlags; }
+ inline void setFlags(int32_t flags) { mFlags = flags; }
+
inline int32_t getKeyCode() const { return mKeyCode; }
inline int32_t getScanCode() const { return mScanCode; }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 6348d37..794ed15 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -463,8 +463,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Intent mHomeIntent;
Intent mCarDockIntent;
Intent mDeskDockIntent;
- int mShortcutKeyPressed = -1;
- boolean mConsumeShortcutKeyUp;
+ boolean mSearchKeyShortcutPending;
+ boolean mConsumeSearchKeyUp;
// support for activating the lock screen while the screen is on
boolean mAllowLockscreenWhenOn;
@@ -509,7 +509,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
ShortcutManager mShortcutManager;
PowerManager.WakeLock mBroadcastWakeLock;
- final KeyCharacterMap.FallbackAction mFallbackAction = new KeyCharacterMap.FallbackAction();
+ // Fallback actions by key code.
+ private final SparseArray<KeyCharacterMap.FallbackAction> mFallbackActions =
+ new SparseArray<KeyCharacterMap.FallbackAction>();
private static final int MSG_ENABLE_POINTER_LOCATION = 1;
private static final int MSG_DISABLE_POINTER_LOCATION = 2;
@@ -1709,7 +1711,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (false) {
Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount="
- + repeatCount + " keyguardOn=" + keyguardOn + " mHomePressed=" + mHomePressed);
+ + repeatCount + " keyguardOn=" + keyguardOn + " mHomePressed=" + mHomePressed
+ + " canceled=" + canceled);
}
// If we think we might have a volume down & power key chord on the way
@@ -1842,13 +1845,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
} else if (keyCode == KeyEvent.KEYCODE_SEARCH) {
if (down) {
if (repeatCount == 0) {
- mShortcutKeyPressed = keyCode;
- mConsumeShortcutKeyUp = false;
+ mSearchKeyShortcutPending = true;
+ mConsumeSearchKeyUp = false;
}
- } else if (keyCode == mShortcutKeyPressed) {
- mShortcutKeyPressed = -1;
- if (mConsumeShortcutKeyUp) {
- mConsumeShortcutKeyUp = false;
+ } else {
+ mSearchKeyShortcutPending = false;
+ if (mConsumeSearchKeyUp) {
+ mConsumeSearchKeyUp = false;
return -1;
}
}
@@ -1865,10 +1868,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// even if no shortcut was invoked. This prevents text from being
// inadvertently inserted when using a keyboard that has built-in macro
// shortcut keys (that emit Search+x) and some of them are not registered.
- if (mShortcutKeyPressed != -1) {
+ if (mSearchKeyShortcutPending) {
final KeyCharacterMap kcm = event.getKeyCharacterMap();
if (kcm.isPrintingKey(keyCode)) {
- mConsumeShortcutKeyUp = true;
+ mConsumeSearchKeyUp = true;
+ mSearchKeyShortcutPending = false;
if (down && repeatCount == 0 && !keyguardOn) {
Intent shortcutIntent = mShortcutManager.getIntent(kcm, keyCode, metaState);
if (shortcutIntent != null) {
@@ -1878,13 +1882,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
} catch (ActivityNotFoundException ex) {
Slog.w(TAG, "Dropping shortcut key combination because "
+ "the activity to which it is registered was not found: "
- + KeyEvent.keyCodeToString(mShortcutKeyPressed)
- + "+" + KeyEvent.keyCodeToString(keyCode), ex);
+ + "SEARCH+" + KeyEvent.keyCodeToString(keyCode), ex);
}
} else {
Slog.i(TAG, "Dropping unregistered shortcut key combination: "
- + KeyEvent.keyCodeToString(mShortcutKeyPressed)
- + "+" + KeyEvent.keyCodeToString(keyCode));
+ + "SEARCH+" + KeyEvent.keyCodeToString(keyCode));
}
}
return -1;
@@ -1964,51 +1966,70 @@ public class PhoneWindowManager implements WindowManagerPolicy {
+ ", policyFlags=" + policyFlags);
}
+ KeyEvent fallbackEvent = null;
if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
final KeyCharacterMap kcm = event.getKeyCharacterMap();
final int keyCode = event.getKeyCode();
final int metaState = event.getMetaState();
+ final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN
+ && event.getRepeatCount() == 0;
// Check for fallback actions specified by the key character map.
- if (getFallbackAction(kcm, keyCode, metaState, mFallbackAction)) {
+ final FallbackAction fallbackAction;
+ if (initialDown) {
+ fallbackAction = kcm.getFallbackAction(keyCode, metaState);
+ } else {
+ fallbackAction = mFallbackActions.get(keyCode);
+ }
+
+ if (fallbackAction != null) {
if (DEBUG_FALLBACK) {
- Slog.d(TAG, "Fallback: keyCode=" + mFallbackAction.keyCode
- + " metaState=" + Integer.toHexString(mFallbackAction.metaState));
+ Slog.d(TAG, "Fallback: keyCode=" + fallbackAction.keyCode
+ + " metaState=" + Integer.toHexString(fallbackAction.metaState));
}
- int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
- KeyEvent fallbackEvent = KeyEvent.obtain(
+ final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
+ fallbackEvent = KeyEvent.obtain(
event.getDownTime(), event.getEventTime(),
- event.getAction(), mFallbackAction.keyCode,
- event.getRepeatCount(), mFallbackAction.metaState,
+ event.getAction(), fallbackAction.keyCode,
+ event.getRepeatCount(), fallbackAction.metaState,
event.getDeviceId(), event.getScanCode(),
flags, event.getSource(), null);
- int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags, true);
- if ((actions & ACTION_PASS_TO_USER) != 0) {
- long delayMillis = interceptKeyBeforeDispatching(
- win, fallbackEvent, policyFlags);
- if (delayMillis == 0) {
- if (DEBUG_FALLBACK) {
- Slog.d(TAG, "Performing fallback.");
- }
- return fallbackEvent;
- }
+
+ if (!interceptFallback(win, fallbackEvent, policyFlags)) {
+ fallbackEvent.recycle();
+ fallbackEvent = null;
+ }
+
+ if (initialDown) {
+ mFallbackActions.put(keyCode, fallbackAction);
+ } else if (event.getAction() == KeyEvent.ACTION_UP) {
+ mFallbackActions.remove(keyCode);
+ fallbackAction.recycle();
}
- fallbackEvent.recycle();
}
}
if (DEBUG_FALLBACK) {
- Slog.d(TAG, "No fallback.");
+ if (fallbackEvent == null) {
+ Slog.d(TAG, "No fallback.");
+ } else {
+ Slog.d(TAG, "Performing fallback: " + fallbackEvent);
+ }
}
- return null;
+ return fallbackEvent;
}
- private boolean getFallbackAction(KeyCharacterMap kcm, int keyCode, int metaState,
- FallbackAction outFallbackAction) {
- // Consult the key character map for specific fallback actions.
- // For example, map NUMPAD_1 to MOVE_HOME when NUMLOCK is not pressed.
- return kcm.getFallbackAction(keyCode, metaState, outFallbackAction);
+ private boolean interceptFallback(WindowState win, KeyEvent fallbackEvent, int policyFlags) {
+ int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags, true);
+ if ((actions & ACTION_PASS_TO_USER) != 0) {
+ long delayMillis = interceptKeyBeforeDispatching(
+ win, fallbackEvent, policyFlags);
+ if (delayMillis == 0) {
+ return true;
+ }
+ }
+ return false;
}
/**
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index ada9d9e..1062d68 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -3354,6 +3354,25 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con
// generated a fallback or if the window is not a foreground window,
// then cancel the associated fallback key, if any.
if (fallbackKeyCode != -1) {
+ // Dispatch the unhandled key to the policy with the cancel flag.
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ ALOGD("Unhandled key event: Asking policy to cancel fallback action. "
+ "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+ keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
+ keyEntry->policyFlags);
+#endif
+ KeyEvent event;
+ initializeKeyEvent(&event, keyEntry);
+ event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);
+
+ mLock.unlock();
+
+ mPolicy->dispatchUnhandledKey(connection->inputWindowHandle,
+ &event, keyEntry->policyFlags, &event);
+
+ mLock.lock();
+
+ // Cancel the fallback key.
if (fallbackKeyCode != AKEYCODE_UNKNOWN) {
CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
"application handled the original non-fallback key "
@@ -3374,8 +3393,9 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Unhandled key event: Skipping unhandled key event processing "
"since this is not an initial down. "
- "keyCode=%d, action=%d, repeatCount=%d",
- originalKeyCode, keyEntry->action, keyEntry->repeatCount);
+ "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+ originalKeyCode, keyEntry->action, keyEntry->repeatCount,
+ keyEntry->policyFlags);
#endif
return false;
}
@@ -3383,8 +3403,9 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con
// Dispatch the unhandled key to the policy.
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Unhandled key event: Asking policy to perform fallback action. "
- "keyCode=%d, action=%d, repeatCount=%d",
- keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount);
+ "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+ keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
+ keyEntry->policyFlags);
#endif
KeyEvent event;
initializeKeyEvent(&event, keyEntry);
@@ -3426,7 +3447,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con
"to send %d instead. Fallback canceled.",
event.getKeyCode(), originalKeyCode, fallbackKeyCode);
} else {
- ALOGD("Unhandled key event: Policy did not request fallback for %d,"
+ ALOGD("Unhandled key event: Policy did not request fallback for %d, "
"but on the DOWN it had requested to send %d. "
"Fallback canceled.",
originalKeyCode, fallbackKeyCode);
@@ -3903,8 +3924,10 @@ void InputDispatcher::InputState::addKeyMemento(const KeyEntry* entry, int32_t f
memento.source = entry->source;
memento.keyCode = entry->keyCode;
memento.scanCode = entry->scanCode;
+ memento.metaState = entry->metaState;
memento.flags = flags;
memento.downTime = entry->downTime;
+ memento.policyFlags = entry->policyFlags;
}
void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry,
@@ -3919,6 +3942,7 @@ void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry,
memento.downTime = entry->downTime;
memento.setPointers(entry);
memento.hovering = hovering;
+ memento.policyFlags = entry->policyFlags;
}
void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) {
@@ -3935,9 +3959,9 @@ void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTim
const KeyMemento& memento = mKeyMementos.itemAt(i);
if (shouldCancelKey(memento, options)) {
outEvents.push(new KeyEntry(currentTime,
- memento.deviceId, memento.source, 0,
+ memento.deviceId, memento.source, memento.policyFlags,
AKEY_EVENT_ACTION_UP, memento.flags | AKEY_EVENT_FLAG_CANCELED,
- memento.keyCode, memento.scanCode, 0, 0, memento.downTime));
+ memento.keyCode, memento.scanCode, memento.metaState, 0, memento.downTime));
}
}
@@ -3945,7 +3969,7 @@ void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTim
const MotionMemento& memento = mMotionMementos.itemAt(i);
if (shouldCancelMotion(memento, options)) {
outEvents.push(new MotionEntry(currentTime,
- memento.deviceId, memento.source, 0,
+ memento.deviceId, memento.source, memento.policyFlags,
memento.hovering
? AMOTION_EVENT_ACTION_HOVER_EXIT
: AMOTION_EVENT_ACTION_CANCEL,
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index 07ca9d5..06b8d64 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -732,8 +732,10 @@ private:
uint32_t source;
int32_t keyCode;
int32_t scanCode;
+ int32_t metaState;
int32_t flags;
nsecs_t downTime;
+ uint32_t policyFlags;
};
struct MotionMemento {
@@ -747,6 +749,7 @@ private:
PointerProperties pointerProperties[MAX_POINTERS];
PointerCoords pointerCoords[MAX_POINTERS];
bool hovering;
+ uint32_t policyFlags;
void setPointers(const MotionEntry* entry);
};