summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2011-03-29 15:11:34 -0700
committerJeff Brown <jeffbrown@android.com>2011-05-24 15:21:46 -0700
commit524ee64b91bc123e1ccfc881a0f1a1e84722251d (patch)
treec3f358e5a7afc91dd63265e01ecbf3db781f58eb /services
parent22d789d580a4ab5bed83c0ed698d2f5bc721e176 (diff)
downloadframeworks_base-524ee64b91bc123e1ccfc881a0f1a1e84722251d.zip
frameworks_base-524ee64b91bc123e1ccfc881a0f1a1e84722251d.tar.gz
frameworks_base-524ee64b91bc123e1ccfc881a0f1a1e84722251d.tar.bz2
Support chorded fallback keys. (DO NOT MERGE)
Also be more careful about canceling fallback keys during focus transitions, when the application handles the key, or when the policy decides to do something different. Fixed a crash due to JNI CallObjectMethod returning an undefined value (not null) when an exception is thrown. Fixed a crash due to the policy trying to create a Dialog for recent apps on the dispatcher thread. It should happen on the policy's Looper instead. Bug: 4187302 Change-Id: I043f82913830f411b3bb4018d6422467b6ca454f
Diffstat (limited to 'services')
-rw-r--r--services/input/InputDispatcher.cpp265
-rw-r--r--services/input/InputDispatcher.h55
-rw-r--r--services/jni/com_android_server_InputManager.cpp8
3 files changed, 227 insertions, 101 deletions
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index b67929a..7ac2f13 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -565,18 +565,19 @@ void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropR
}
switch (entry->type) {
- case EventEntry::TYPE_KEY:
- synthesizeCancelationEventsForAllConnectionsLocked(
- InputState::CANCEL_NON_POINTER_EVENTS, reason);
+ case EventEntry::TYPE_KEY: {
+ CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
+ synthesizeCancelationEventsForAllConnectionsLocked(options);
break;
+ }
case EventEntry::TYPE_MOTION: {
MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
- synthesizeCancelationEventsForAllConnectionsLocked(
- InputState::CANCEL_POINTER_EVENTS, reason);
+ CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason);
+ synthesizeCancelationEventsForAllConnectionsLocked(options);
} else {
- synthesizeCancelationEventsForAllConnectionsLocked(
- InputState::CANCEL_NON_POINTER_EVENTS, reason);
+ CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
+ synthesizeCancelationEventsForAllConnectionsLocked(options);
}
break;
}
@@ -869,8 +870,9 @@ bool InputDispatcher::dispatchMotionLocked(
// Dispatch the motion.
if (conflictingPointerActions) {
- synthesizeCancelationEventsForAllConnectionsLocked(
- InputState::CANCEL_POINTER_EVENTS, "Conflicting pointer actions.");
+ CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ "conflicting pointer actions");
+ synthesizeCancelationEventsForAllConnectionsLocked(options);
}
dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
return true;
@@ -1036,9 +1038,9 @@ void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
if (connection->status == Connection::STATUS_NORMAL) {
- synthesizeCancelationEventsForConnectionLocked(
- connection, InputState::CANCEL_ALL_EVENTS,
+ CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
"application not responding");
+ synthesizeCancelationEventsForConnectionLocked(connection, options);
}
}
}
@@ -2080,26 +2082,24 @@ int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data
}
void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
- InputState::CancelationOptions options, const char* reason) {
+ const CancelationOptions& options) {
for (size_t i = 0; i < mConnectionsByReceiveFd.size(); i++) {
synthesizeCancelationEventsForConnectionLocked(
- mConnectionsByReceiveFd.valueAt(i), options, reason);
+ mConnectionsByReceiveFd.valueAt(i), options);
}
}
void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked(
- const sp<InputChannel>& channel, InputState::CancelationOptions options,
- const char* reason) {
+ const sp<InputChannel>& channel, const CancelationOptions& options) {
ssize_t index = getConnectionIndexLocked(channel);
if (index >= 0) {
synthesizeCancelationEventsForConnectionLocked(
- mConnectionsByReceiveFd.valueAt(index), options, reason);
+ mConnectionsByReceiveFd.valueAt(index), options);
}
}
void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
- const sp<Connection>& connection, InputState::CancelationOptions options,
- const char* reason) {
+ const sp<Connection>& connection, const CancelationOptions& options) {
nsecs_t currentTime = now();
mTempCancelationEvents.clear();
@@ -2110,8 +2110,9 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
&& connection->status != Connection::STATUS_BROKEN) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
LOGD("channel '%s' ~ Synthesized %d cancelation events to bring channel back in sync "
- "with reality: %s, options=%d.",
- connection->getInputChannelName(), mTempCancelationEvents.size(), reason, options);
+ "with reality: %s, mode=%d.",
+ connection->getInputChannelName(), mTempCancelationEvents.size(),
+ options.reason, options.mode);
#endif
for (size_t i = 0; i < mTempCancelationEvents.size(); i++) {
EventEntry* cancelationEventEntry = mTempCancelationEvents.itemAt(i);
@@ -2751,8 +2752,9 @@ void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {
LOGD("Focus left window: %s",
oldFocusedWindowChannel->getName().string());
#endif
- synthesizeCancelationEventsForInputChannelLocked(oldFocusedWindowChannel,
- InputState::CANCEL_NON_POINTER_EVENTS, "focus left window");
+ CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
+ "focus left window");
+ synthesizeCancelationEventsForInputChannelLocked(oldFocusedWindowChannel, options);
oldFocusedWindowChannel.clear();
}
}
@@ -2773,8 +2775,9 @@ void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {
#if DEBUG_FOCUS
LOGD("Touched window was removed: %s", touchedWindow.channel->getName().string());
#endif
- synthesizeCancelationEventsForInputChannelLocked(touchedWindow.channel,
- InputState::CANCEL_POINTER_EVENTS, "touched window was removed");
+ CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ "touched window was removed");
+ synthesizeCancelationEventsForInputChannelLocked(touchedWindow.channel, options);
mTouchState.windows.removeAt(i);
}
}
@@ -2910,9 +2913,9 @@ bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel,
sp<Connection> toConnection = mConnectionsByReceiveFd.valueAt(toConnectionIndex);
fromConnection->inputState.copyPointerStateTo(toConnection->inputState);
- synthesizeCancelationEventsForConnectionLocked(fromConnection,
- InputState::CANCEL_POINTER_EVENTS,
+ CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
"transferring touch focus from this window to another window");
+ synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
}
#if DEBUG_FOCUS
@@ -2930,7 +2933,8 @@ void InputDispatcher::resetAndDropEverythingLocked(const char* reason) {
LOGD("Resetting and dropping all events (%s).", reason);
#endif
- synthesizeCancelationEventsForAllConnectionsLocked(InputState::CANCEL_ALL_EVENTS, reason);
+ CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, reason);
+ synthesizeCancelationEventsForAllConnectionsLocked(options);
resetKeyRepeatLocked();
releasePendingEventLocked();
@@ -3261,58 +3265,49 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
if (!connection->outboundQueue.isEmpty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
if (dispatchEntry->inProgress
- && dispatchEntry->hasForegroundTarget()
&& dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
if (!(keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK)) {
- if (handled) {
- // If the application handled a non-fallback key, then immediately
- // cancel all fallback keys previously dispatched to the application.
- // This behavior will prevent chording with fallback keys (so they cannot
- // be used as modifiers) but it will ensure that fallback keys do not
- // get stuck. This takes care of the case where the application does not handle
- // the original DOWN so we generate a fallback DOWN but it does handle
- // the original UP in which case we want to send a fallback CANCEL.
- synthesizeCancelationEventsForConnectionLocked(connection,
- InputState::CANCEL_FALLBACK_EVENTS,
- "application handled a non-fallback event, "
- "canceling all fallback events");
- connection->originalKeyCodeForFallback = -1;
+ // Get the fallback key state.
+ // Clear it out after dispatching the UP.
+ int32_t originalKeyCode = keyEntry->keyCode;
+ int32_t fallbackKeyCode = connection->inputState.getFallbackKey(originalKeyCode);
+ if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
+ connection->inputState.removeFallbackKey(originalKeyCode);
+ }
+
+ if (handled || !dispatchEntry->hasForegroundTarget()) {
+ // If the application handles the original key for which we previously
+ // generated a fallback or if the window is not a foreground window,
+ // then cancel the associated fallback key, if any.
+ if (fallbackKeyCode != -1) {
+ if (fallbackKeyCode != AKEYCODE_UNKNOWN) {
+ CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
+ "application handled the original non-fallback key "
+ "or is no longer a foreground target, "
+ "canceling previously dispatched fallback key");
+ options.keyCode = fallbackKeyCode;
+ synthesizeCancelationEventsForConnectionLocked(connection, options);
+ }
+ connection->inputState.removeFallbackKey(originalKeyCode);
+ }
} else {
// If the application did not handle a non-fallback key, first check
- // that we are in a good state to handle the fallback key. Then ask
- // the policy what to do with it.
- if (connection->originalKeyCodeForFallback < 0) {
- if (keyEntry->action != AKEY_EVENT_ACTION_DOWN
- || keyEntry->repeatCount != 0) {
+ // that we are in a good state to perform unhandled key event processing
+ // Then ask the policy what to do with it.
+ bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN
+ && keyEntry->repeatCount == 0;
+ if (fallbackKeyCode == -1 && !initialDown) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
- LOGD("Unhandled key event: Skipping fallback since this "
- "is not an initial down. "
- "keyCode=%d, action=%d, repeatCount=%d",
- keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount);
+ LOGD("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);
#endif
- goto SkipFallback;
- }
-
- // Start handling the fallback key on DOWN.
- connection->originalKeyCodeForFallback = keyEntry->keyCode;
- } else {
- if (keyEntry->keyCode != connection->originalKeyCodeForFallback) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- LOGD("Unhandled key event: Skipping fallback since there is "
- "already a different fallback in progress. "
- "keyCode=%d, originalKeyCodeForFallback=%d",
- keyEntry->keyCode, connection->originalKeyCodeForFallback);
-#endif
- goto SkipFallback;
- }
-
- // Finish handling the fallback key on UP.
- if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
- connection->originalKeyCodeForFallback = -1;
- }
+ goto SkipFallback;
}
+ // Dispatch the unhandled key to the policy.
#if DEBUG_OUTBOUND_EVENT_DETAILS
LOGD("Unhandled key event: Asking policy to perform fallback action. "
"keyCode=%d, action=%d, repeatCount=%d",
@@ -3329,18 +3324,78 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
mLock.lock();
if (connection->status != Connection::STATUS_NORMAL) {
+ connection->inputState.removeFallbackKey(originalKeyCode);
return;
}
assert(connection->outboundQueue.headSentinel.next == dispatchEntry);
+ // Latch the fallback keycode for this key on an initial down.
+ // The fallback keycode cannot change at any other point in the lifecycle.
+ if (initialDown) {
+ if (fallback) {
+ fallbackKeyCode = event.getKeyCode();
+ } else {
+ fallbackKeyCode = AKEYCODE_UNKNOWN;
+ }
+ connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode);
+ }
+
+ assert(fallbackKeyCode != -1);
+
+ // Cancel the fallback key if the policy decides not to send it anymore.
+ // We will continue to dispatch the key to the policy but we will no
+ // longer dispatch a fallback key to the application.
+ if (fallbackKeyCode != AKEYCODE_UNKNOWN
+ && (!fallback || fallbackKeyCode != event.getKeyCode())) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ if (fallback) {
+ LOGD("Unhandled key event: Policy requested to send key %d"
+ "as a fallback for %d, but on the DOWN it had requested "
+ "to send %d instead. Fallback canceled.",
+ event.getKeyCode(), originalKeyCode, fallbackKeyCode);
+ } else {
+ LOGD("Unhandled key event: Policy did not request fallback for %d,"
+ "but on the DOWN it had requested to send %d. "
+ "Fallback canceled.",
+ originalKeyCode, fallbackKeyCode);
+ }
+#endif
+
+ CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
+ "canceling fallback, policy no longer desires it");
+ options.keyCode = fallbackKeyCode;
+ synthesizeCancelationEventsForConnectionLocked(connection, options);
+
+ fallback = false;
+ fallbackKeyCode = AKEYCODE_UNKNOWN;
+ if (keyEntry->action != AKEY_EVENT_ACTION_UP) {
+ connection->inputState.setFallbackKey(originalKeyCode,
+ fallbackKeyCode);
+ }
+ }
+
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ {
+ String8 msg;
+ const KeyedVector<int32_t, int32_t>& fallbackKeys =
+ connection->inputState.getFallbackKeys();
+ for (size_t i = 0; i < fallbackKeys.size(); i++) {
+ msg.appendFormat(", %d->%d", fallbackKeys.keyAt(i),
+ fallbackKeys.valueAt(i));
+ }
+ LOGD("Unhandled key event: %d currently tracked fallback keys%s.",
+ fallbackKeys.size(), msg.string());
+ }
+#endif
+
if (fallback) {
// Restart the dispatch cycle using the fallback key.
keyEntry->eventTime = event.getEventTime();
keyEntry->deviceId = event.getDeviceId();
keyEntry->source = event.getSource();
keyEntry->flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK;
- keyEntry->keyCode = event.getKeyCode();
+ keyEntry->keyCode = fallbackKeyCode;
keyEntry->scanCode = event.getScanCode();
keyEntry->metaState = event.getMetaState();
keyEntry->repeatCount = event.getRepeatCount();
@@ -3349,13 +3404,17 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
#if DEBUG_OUTBOUND_EVENT_DETAILS
LOGD("Unhandled key event: Dispatching fallback key. "
- "fallbackKeyCode=%d, fallbackMetaState=%08x",
- keyEntry->keyCode, keyEntry->metaState);
+ "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
+ originalKeyCode, fallbackKeyCode, keyEntry->metaState);
#endif
dispatchEntry->inProgress = false;
startDispatchCycleLocked(now(), connection);
return;
+ } else {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ LOGD("Unhandled key event: No fallback key.");
+#endif
}
}
}
@@ -3649,6 +3708,17 @@ void InputDispatcher::InputState::trackEvent(
void InputDispatcher::InputState::trackKey(
const KeyEntry* entry) {
int32_t action = entry->action;
+ if (action == AKEY_EVENT_ACTION_UP
+ && (entry->flags & AKEY_EVENT_FLAG_FALLBACK)) {
+ for (size_t i = 0; i < mFallbackKeys.size(); ) {
+ if (mFallbackKeys.valueAt(i) == entry->keyCode) {
+ mFallbackKeys.removeItemsAt(i);
+ } else {
+ i += 1;
+ }
+ }
+ }
+
for (size_t i = 0; i < mKeyMementos.size(); i++) {
KeyMemento& memento = mKeyMementos.editItemAt(i);
if (memento.deviceId == entry->deviceId
@@ -3736,7 +3806,7 @@ void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry*
void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTime,
Allocator* allocator, Vector<EventEntry*>& outEvents,
- CancelationOptions options) {
+ const CancelationOptions& options) {
for (size_t i = 0; i < mKeyMementos.size(); ) {
const KeyMemento& memento = mKeyMementos.itemAt(i);
if (shouldCancelKey(memento, options)) {
@@ -3768,6 +3838,7 @@ void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTim
void InputDispatcher::InputState::clear() {
mKeyMementos.clear();
mMotionMementos.clear();
+ mFallbackKeys.clear();
}
void InputDispatcher::InputState::copyPointerStateTo(InputState& other) const {
@@ -3788,13 +3859,36 @@ void InputDispatcher::InputState::copyPointerStateTo(InputState& other) const {
}
}
+int32_t InputDispatcher::InputState::getFallbackKey(int32_t originalKeyCode) {
+ ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
+ return index >= 0 ? mFallbackKeys.valueAt(index) : -1;
+}
+
+void InputDispatcher::InputState::setFallbackKey(int32_t originalKeyCode,
+ int32_t fallbackKeyCode) {
+ ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
+ if (index >= 0) {
+ mFallbackKeys.replaceValueAt(index, fallbackKeyCode);
+ } else {
+ mFallbackKeys.add(originalKeyCode, fallbackKeyCode);
+ }
+}
+
+void InputDispatcher::InputState::removeFallbackKey(int32_t originalKeyCode) {
+ mFallbackKeys.removeItem(originalKeyCode);
+}
+
bool InputDispatcher::InputState::shouldCancelKey(const KeyMemento& memento,
- CancelationOptions options) {
- switch (options) {
- case CANCEL_ALL_EVENTS:
- case CANCEL_NON_POINTER_EVENTS:
+ const CancelationOptions& options) {
+ if (options.keyCode != -1 && memento.keyCode != options.keyCode) {
+ return false;
+ }
+
+ switch (options.mode) {
+ case CancelationOptions::CANCEL_ALL_EVENTS:
+ case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
return true;
- case CANCEL_FALLBACK_EVENTS:
+ case CancelationOptions::CANCEL_FALLBACK_EVENTS:
return memento.flags & AKEY_EVENT_FLAG_FALLBACK;
default:
return false;
@@ -3802,13 +3896,13 @@ bool InputDispatcher::InputState::shouldCancelKey(const KeyMemento& memento,
}
bool InputDispatcher::InputState::shouldCancelMotion(const MotionMemento& memento,
- CancelationOptions options) {
- switch (options) {
- case CANCEL_ALL_EVENTS:
+ const CancelationOptions& options) {
+ switch (options.mode) {
+ case CancelationOptions::CANCEL_ALL_EVENTS:
return true;
- case CANCEL_POINTER_EVENTS:
+ case CancelationOptions::CANCEL_POINTER_EVENTS:
return memento.source & AINPUT_SOURCE_CLASS_POINTER;
- case CANCEL_NON_POINTER_EVENTS:
+ case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
return !(memento.source & AINPUT_SOURCE_CLASS_POINTER);
default:
return false;
@@ -3822,8 +3916,7 @@ InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle) :
status(STATUS_NORMAL), inputChannel(inputChannel), inputWindowHandle(inputWindowHandle),
inputPublisher(inputChannel),
- lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX),
- originalKeyCodeForFallback(-1) {
+ lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX) {
}
InputDispatcher::Connection::~Connection() {
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index dd89328..27c5950 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -594,18 +594,32 @@ private:
void releaseEventEntryInjectionState(EventEntry* entry);
};
- /* Tracks dispatched key and motion event state so that cancelation events can be
- * synthesized when events are dropped. */
- class InputState {
- public:
- // Specifies the sources to cancel.
- enum CancelationOptions {
+ /* Specifies which events are to be canceled and why. */
+ struct CancelationOptions {
+ enum Mode {
CANCEL_ALL_EVENTS = 0,
CANCEL_POINTER_EVENTS = 1,
CANCEL_NON_POINTER_EVENTS = 2,
CANCEL_FALLBACK_EVENTS = 3,
};
+ // The criterion to use to determine which events should be canceled.
+ Mode mode;
+
+ // Descriptive reason for the cancelation.
+ const char* reason;
+
+ // The specific keycode of the key event to cancel, or -1 to cancel any key event.
+ int32_t keyCode;
+
+ CancelationOptions(Mode mode, const char* reason) :
+ mode(mode), reason(reason), keyCode(-1) { }
+ };
+
+ /* Tracks dispatched key and motion event state so that cancelation events can be
+ * synthesized when events are dropped. */
+ class InputState {
+ public:
InputState();
~InputState();
@@ -623,7 +637,7 @@ private:
// Synthesizes cancelation events for the current state and resets the tracked state.
void synthesizeCancelationEvents(nsecs_t currentTime, Allocator* allocator,
- Vector<EventEntry*>& outEvents, CancelationOptions options);
+ Vector<EventEntry*>& outEvents, const CancelationOptions& options);
// Clears the current state.
void clear();
@@ -631,6 +645,21 @@ private:
// Copies pointer-related parts of the input state to another instance.
void copyPointerStateTo(InputState& other) const;
+ // Gets the fallback key associated with a keycode.
+ // Returns -1 if none.
+ // Returns AKEYCODE_UNKNOWN if we are only dispatching the unhandled key to the policy.
+ int32_t getFallbackKey(int32_t originalKeyCode);
+
+ // Sets the fallback key for a particular keycode.
+ void setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode);
+
+ // Removes the fallback key for a particular keycode.
+ void removeFallbackKey(int32_t originalKeyCode);
+
+ inline const KeyedVector<int32_t, int32_t>& getFallbackKeys() const {
+ return mFallbackKeys;
+ }
+
private:
struct KeyMemento {
int32_t deviceId;
@@ -656,11 +685,12 @@ private:
Vector<KeyMemento> mKeyMementos;
Vector<MotionMemento> mMotionMementos;
+ KeyedVector<int32_t, int32_t> mFallbackKeys;
static bool shouldCancelKey(const KeyMemento& memento,
- CancelationOptions options);
+ const CancelationOptions& options);
static bool shouldCancelMotion(const MotionMemento& memento,
- CancelationOptions options);
+ const CancelationOptions& options);
};
/* Manages the dispatch state associated with a single input channel. */
@@ -687,7 +717,6 @@ private:
nsecs_t lastEventTime; // the time when the event was originally captured
nsecs_t lastDispatchTime; // the time when the last event was dispatched
- int32_t originalKeyCodeForFallback; // original keycode for fallback in progress, -1 if none
explicit Connection(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle);
@@ -929,11 +958,11 @@ private:
static int handleReceiveCallback(int receiveFd, int events, void* data);
void synthesizeCancelationEventsForAllConnectionsLocked(
- InputState::CancelationOptions options, const char* reason);
+ const CancelationOptions& options);
void synthesizeCancelationEventsForInputChannelLocked(const sp<InputChannel>& channel,
- InputState::CancelationOptions options, const char* reason);
+ const CancelationOptions& options);
void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection,
- InputState::CancelationOptions options, const char* reason);
+ const CancelationOptions& options);
// Splitting motion events across windows.
MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds);
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 3fb76e4..426197a 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -795,7 +795,9 @@ bool NativeInputManager::dispatchUnhandledKey(const sp<InputWindowHandle>& input
jobject fallbackKeyEventObj = env->CallObjectMethod(mCallbacksObj,
gCallbacksClassInfo.dispatchUnhandledKey,
inputWindowHandleObj, keyEventObj, policyFlags);
- checkAndClearExceptionFromCallback(env, "dispatchUnhandledKey");
+ if (checkAndClearExceptionFromCallback(env, "dispatchUnhandledKey")) {
+ fallbackKeyEventObj = NULL;
+ }
android_view_KeyEvent_recycle(env, keyEventObj);
env->DeleteLocalRef(keyEventObj);
@@ -826,7 +828,9 @@ bool NativeInputManager::checkInjectEventsPermissionNonReentrant(
JNIEnv* env = jniEnv();
jboolean result = env->CallBooleanMethod(mCallbacksObj,
gCallbacksClassInfo.checkInjectEventsPermission, injectorPid, injectorUid);
- checkAndClearExceptionFromCallback(env, "checkInjectEventsPermission");
+ if (checkAndClearExceptionFromCallback(env, "checkInjectEventsPermission")) {
+ result = false;
+ }
return result;
}