am 571d4cbe: Merge "Fix bugs in fallback key handling." into jb-dev
* commit '571d4cbeec4adad050b8e188770e7e7dedc558f1': Fix bugs in fallback key handling.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -4481,20 +4479,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user