diff options
-rw-r--r-- | core/java/android/os/MessageQueue.java | 7 | ||||
-rw-r--r-- | core/java/android/view/InputChannel.java | 2 | ||||
-rw-r--r-- | core/java/android/view/InputTarget.java | 2 | ||||
-rw-r--r-- | include/ui/Input.h | 4 | ||||
-rw-r--r-- | include/ui/InputDispatcher.h | 133 | ||||
-rw-r--r-- | include/ui/InputManager.h | 12 | ||||
-rw-r--r-- | libs/ui/InputDispatcher.cpp | 457 | ||||
-rw-r--r-- | libs/ui/InputManager.cpp | 5 | ||||
-rw-r--r-- | libs/ui/InputReader.cpp | 14 | ||||
-rw-r--r-- | services/java/com/android/server/InputManager.java | 114 | ||||
-rw-r--r-- | services/java/com/android/server/InputTargetList.java | 7 | ||||
-rw-r--r-- | services/java/com/android/server/WindowManagerService.java | 610 | ||||
-rw-r--r-- | services/jni/com_android_server_InputManager.cpp | 357 |
13 files changed, 1229 insertions, 495 deletions
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java index df30c76..adb11c8 100644 --- a/core/java/android/os/MessageQueue.java +++ b/core/java/android/os/MessageQueue.java @@ -341,11 +341,4 @@ public class MessageQueue { } } */ - - void poke() - { - synchronized (this) { - nativeWake(); - } - } } diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java index 66a83b8..e5ebc69 100644 --- a/core/java/android/view/InputChannel.java +++ b/core/java/android/view/InputChannel.java @@ -26,7 +26,7 @@ import android.util.Slog; * to the ViewRoot through a Binder transaction as part of registering the Window. * @hide */ -public class InputChannel implements Parcelable { +public final class InputChannel implements Parcelable { private static final String TAG = "InputChannel"; public static final Parcelable.Creator<InputChannel> CREATOR diff --git a/core/java/android/view/InputTarget.java b/core/java/android/view/InputTarget.java index e56e03c..6ff7305 100644 --- a/core/java/android/view/InputTarget.java +++ b/core/java/android/view/InputTarget.java @@ -25,7 +25,7 @@ package android.view; * These parameters are used by the native input dispatching code. * @hide */ -public class InputTarget { +public final class InputTarget { public InputChannel mInputChannel; public int mFlags; public long mTimeoutNanos; diff --git a/include/ui/Input.h b/include/ui/Input.h index 92ff872..979d6e8 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -58,8 +58,6 @@ struct RawEvent { /* * Flags that flow alongside events in the input dispatch system to help with certain * policy decisions such as waking from device sleep. - * - * TODO This enumeration should probably be split up or relabeled for clarity. */ enum { /* These flags originate in RawEvents and are generally set in the key map. */ @@ -73,6 +71,8 @@ enum { POLICY_FLAG_MENU = 0x00000040, POLICY_FLAG_LAUNCHER = 0x00000080, + POLICY_FLAG_RAW_MASK = 0x0000ffff, + /* These flags are set by the input reader policy as it intercepts each event. */ // Indicates that the screen was off when the event was received and the event diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h index 80a20c9..511ad20 100644 --- a/include/ui/InputDispatcher.h +++ b/include/ui/InputDispatcher.h @@ -35,6 +35,28 @@ namespace android { /* + * Constants used to report the outcome of input event injection. + */ +enum { + /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */ + INPUT_EVENT_INJECTION_PENDING = -1, + + /* Injection succeeded. */ + INPUT_EVENT_INJECTION_SUCCEEDED = 0, + + /* Injection failed because the injector did not have permission to inject + * into the application with input focus. */ + INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1, + + /* Injection failed because there were no available input targets. */ + INPUT_EVENT_INJECTION_FAILED = 2, + + /* Injection failed due to a timeout. */ + INPUT_EVENT_INJECTION_TIMED_OUT = 3 +}; + + +/* * An input target specifies how an input event is to be dispatched to a particular window * including the window's input channel, control flags, a timeout, and an X / Y offset to * be added to input event coordinates to compensate for the absolute position of the @@ -70,6 +92,7 @@ struct InputTarget { float xOffset, yOffset; }; + /* * Input dispatcher policy interface. * @@ -91,8 +114,11 @@ public: /* Notifies the system that an input channel is unrecoverably broken. */ virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel) = 0; - /* Notifies the system that an input channel is not responding. */ - virtual void notifyInputChannelANR(const sp<InputChannel>& inputChannel) = 0; + /* Notifies the system that an input channel is not responding. + * Returns true and a new timeout value if the dispatcher should keep waiting. + * Otherwise returns false. */ + virtual bool notifyInputChannelANR(const sp<InputChannel>& inputChannel, + nsecs_t& outNewTimeout) = 0; /* Notifies the system that an input channel recovered from ANR. */ virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) = 0; @@ -100,12 +126,22 @@ public: /* Gets the key repeat timeout or -1 if automatic key repeating is disabled. */ virtual nsecs_t getKeyRepeatTimeout() = 0; - /* Gets the input targets for a key event. */ - virtual void getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, + /* Gets the input targets for a key event. + * If the event is being injected, injectorPid and injectorUid should specify the + * process id and used id of the injecting application, otherwise they should both + * be -1. + * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */ + virtual int32_t getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, + int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) = 0; - /* Gets the input targets for a motion event. */ - virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, + /* Gets the input targets for a motion event. + * If the event is being injected, injectorPid and injectorUid should specify the + * process id and used id of the injecting application, otherwise they should both + * be -1. + * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */ + virtual int32_t getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, + int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) = 0; }; @@ -139,6 +175,17 @@ public: uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords, float xPrecision, float yPrecision, nsecs_t downTime) = 0; + /* Injects an input event and optionally waits for sync. + * This method may block even if sync is false because it must wait for previous events + * to be dispatched before it can determine whether input event injection will be + * permitted based on the current input focus. + * Returns one of the INPUT_EVENT_INJECTION_XXX constants. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual int32_t injectInputEvent(const InputEvent* event, + int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0; + /* Registers or unregister input channels that may be used as targets for input events. * * These methods may be called on any thread (usually by the input manager). @@ -183,6 +230,9 @@ public: uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords, float xPrecision, float yPrecision, nsecs_t downTime); + virtual int32_t injectInputEvent(const InputEvent* event, + int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis); + virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel); virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel); @@ -205,7 +255,13 @@ private: int32_t type; nsecs_t eventTime; + int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING + int32_t injectorPid; // -1 if not injected + int32_t injectorUid; // -1 if not injected + bool dispatchInProgress; // initially false, set to true while dispatching + + inline bool isInjected() { return injectorPid >= 0; } }; struct ConfigurationChangedEntry : EventEntry { @@ -293,6 +349,7 @@ private: struct CommandEntry; typedef void (InputDispatcher::*Command)(CommandEntry* commandEntry); + class Connection; struct CommandEntry : Link<CommandEntry> { CommandEntry(); ~CommandEntry(); @@ -300,7 +357,7 @@ private: Command command; // parameters for the command (usage varies by command) - sp<InputChannel> inputChannel; + sp<Connection> connection; }; // Generic queue implementation. @@ -353,9 +410,16 @@ private: public: Allocator(); - ConfigurationChangedEntry* obtainConfigurationChangedEntry(); - KeyEntry* obtainKeyEntry(); - MotionEntry* obtainMotionEntry(); + ConfigurationChangedEntry* obtainConfigurationChangedEntry(nsecs_t eventTime); + KeyEntry* obtainKeyEntry(nsecs_t eventTime, + int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action, + int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, + int32_t repeatCount, nsecs_t downTime); + MotionEntry* obtainMotionEntry(nsecs_t eventTime, + int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action, + int32_t metaState, int32_t edgeFlags, float xPrecision, float yPrecision, + nsecs_t downTime, uint32_t pointerCount, + const int32_t* pointerIds, const PointerCoords* pointerCoords); DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry); CommandEntry* obtainCommandEntry(Command command); @@ -367,7 +431,7 @@ private: void releaseCommandEntry(CommandEntry* entry); void appendMotionSample(MotionEntry* motionEntry, - nsecs_t eventTime, int32_t pointerCount, const PointerCoords* pointerCoords); + nsecs_t eventTime, const PointerCoords* pointerCoords); private: Pool<ConfigurationChangedEntry> mConfigurationChangeEntryPool; @@ -376,6 +440,8 @@ private: Pool<MotionSample> mMotionSamplePool; Pool<DispatchEntry> mDispatchEntryPool; Pool<CommandEntry> mCommandEntryPool; + + void initializeEventEntry(EventEntry* entry, int32_t type, nsecs_t eventTime); }; /* Manages the dispatch state associated with a single input channel. */ @@ -439,6 +505,8 @@ private: } status_t initialize(); + + void setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout); }; sp<InputDispatcherPolicyInterface> mPolicy; @@ -455,8 +523,17 @@ private: KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd; // Active connections are connections that have a non-empty outbound queue. + // We don't use a ref-counted pointer here because we explicitly abort connections + // during unregistration which causes the connection's outbound queue to be cleared + // and the connection itself to be deactivated. Vector<Connection*> mActiveConnections; + // List of connections that have timed out. Only used by dispatchOnce() + // We don't use a ref-counted pointer here because it is not possible for a connection + // to be unregistered while processing timed out connections since we hold the lock for + // the duration. + Vector<Connection*> mTimedOutConnections; + // Preallocated key and motion event objects used only to ask the input dispatcher policy // for the targets of an event that is to be dispatched. KeyEvent mReusableKeyEvent; @@ -468,6 +545,13 @@ private: Vector<InputTarget> mCurrentInputTargets; bool mCurrentInputTargetsValid; // false while targets are being recomputed + // Event injection and synchronization. + Condition mInjectionResultAvailableCondition; + Condition mFullySynchronizedCondition; + bool isFullySynchronizedLocked(); + EventEntry* createEntryFromInputEventLocked(const InputEvent* event); + void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult); + // Key repeat tracking. // XXX Move this up to the input reader instead. struct KeyRepeatState { @@ -500,13 +584,18 @@ private: nsecs_t currentTime, EventEntry* entry, bool resumeWithAppendedMotionSample); // Manage the dispatch cycle for a single connection. - void prepareDispatchCycleLocked(nsecs_t currentTime, Connection* connection, + // These methods are deliberately not Interruptible because doing all of the work + // with the mutex held makes it easier to ensure that connection invariants are maintained. + // If needed, the methods post commands to run later once the critical bits are done. + void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget, bool resumeWithAppendedMotionSample); - void startDispatchCycleLocked(nsecs_t currentTime, Connection* connection); - void finishDispatchCycleLocked(nsecs_t currentTime, Connection* connection); - bool timeoutDispatchCycleLocked(nsecs_t currentTime, Connection* connection); - bool abortDispatchCycleLocked(nsecs_t currentTime, Connection* connection, + void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection); + void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection); + void timeoutDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection); + void resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime, + const sp<Connection>& connection, nsecs_t newTimeout); + void abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, bool broken); static bool handleReceiveCallback(int receiveFd, int events, void* data); @@ -514,19 +603,17 @@ private: void activateConnectionLocked(Connection* connection); void deactivateConnectionLocked(Connection* connection); - // Outbound policy interactions. - void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry); - // Interesting events that we might like to log or tell the framework about. void onDispatchCycleStartedLocked( - nsecs_t currentTime, Connection* connection); + nsecs_t currentTime, const sp<Connection>& connection); void onDispatchCycleFinishedLocked( - nsecs_t currentTime, Connection* connection, bool recoveredFromANR); + nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR); void onDispatchCycleANRLocked( - nsecs_t currentTime, Connection* connection); + nsecs_t currentTime, const sp<Connection>& connection); void onDispatchCycleBrokenLocked( - nsecs_t currentTime, Connection* connection); + nsecs_t currentTime, const sp<Connection>& connection); + // Outbound policy interactions. void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry); void doNotifyInputChannelANRLockedInterruptible(CommandEntry* commandEntry); void doNotifyInputChannelRecoveredFromANRLockedInterruptible(CommandEntry* commandEntry); diff --git a/include/ui/InputManager.h b/include/ui/InputManager.h index 3872c26..7509dd2 100644 --- a/include/ui/InputManager.h +++ b/include/ui/InputManager.h @@ -78,6 +78,15 @@ public: /* Unregisters an input channel. */ virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0; + /* Injects an input event and optionally waits for sync. + * This method may block even if sync is false because it must wait for previous events + * to be dispatched before it can determine whether input event injection will be + * permitted based on the current input focus. + * Returns one of the INPUT_EVENT_INJECTION_XXX constants. + */ + virtual int32_t injectInputEvent(const InputEvent* event, + int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0; + /* Gets input device configuration. */ virtual void getInputConfiguration(InputConfiguration* outConfiguration) const = 0; @@ -118,6 +127,9 @@ public: virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel); virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel); + virtual int32_t injectInputEvent(const InputEvent* event, + int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis); + virtual void getInputConfiguration(InputConfiguration* outConfiguration) const; virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses, int32_t scanCode) const; diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index 14dcada..2ad3382 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -25,6 +25,9 @@ // Log debug messages about performance statistics. #define DEBUG_PERFORMANCE_STATISTICS 1 +// Log debug messages about input event injection. +#define DEBUG_INJECTION 1 + #include <cutils/log.h> #include <ui/InputDispatcher.h> @@ -43,6 +46,10 @@ static inline bool isMovementKey(int32_t keyCode) { || keyCode == KEYCODE_DPAD_RIGHT; } +static inline nsecs_t now() { + return systemTime(SYSTEM_TIME_MONOTONIC); +} + // --- InputDispatcher --- InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) : @@ -84,7 +91,7 @@ void InputDispatcher::dispatchOnce() { nsecs_t nextWakeupTime = LONG_LONG_MAX; { // acquire lock AutoMutex _l(mLock); - currentTime = systemTime(SYSTEM_TIME_MONOTONIC); + currentTime = now(); // Reset the key repeat timer whenever we disallow key events, even if the next event // is not a key. This is to ensure that we abort a key repeat if the device is just coming @@ -94,32 +101,32 @@ void InputDispatcher::dispatchOnce() { resetKeyRepeatLocked(); } - // Process timeouts for all connections and determine if there are any synchronous - // event dispatches pending. + // Detect and process timeouts for all connections and determine if there are any + // synchronous event dispatches pending. This step is entirely non-interruptible. bool hasPendingSyncTarget = false; - for (size_t i = 0; i < mActiveConnections.size(); ) { + size_t activeConnectionCount = mActiveConnections.size(); + for (size_t i = 0; i < activeConnectionCount; i++) { Connection* connection = mActiveConnections.itemAt(i); - nsecs_t connectionTimeoutTime = connection->nextTimeoutTime; - if (connectionTimeoutTime <= currentTime) { - bool deactivated = timeoutDispatchCycleLocked(currentTime, connection); - if (deactivated) { - // Don't increment i because the connection has been removed - // from mActiveConnections (hence, deactivated). - continue; - } + if (connection->hasPendingSyncTarget()) { + hasPendingSyncTarget = true; } - if (connectionTimeoutTime < nextWakeupTime) { + nsecs_t connectionTimeoutTime = connection->nextTimeoutTime; + if (connectionTimeoutTime <= currentTime) { + mTimedOutConnections.add(connection); + } else if (connectionTimeoutTime < nextWakeupTime) { nextWakeupTime = connectionTimeoutTime; } + } - if (connection->hasPendingSyncTarget()) { - hasPendingSyncTarget = true; - } - - i += 1; + size_t timedOutConnectionCount = mTimedOutConnections.size(); + for (size_t i = 0; i < timedOutConnectionCount; i++) { + Connection* connection = mTimedOutConnections.itemAt(i); + timeoutDispatchCycleLocked(currentTime, connection); + skipPoll = true; } + mTimedOutConnections.clear(); // If we don't have a pending sync target, then we can begin delivering a new event. // (Otherwise we wait for dispatch to complete for that target.) @@ -177,6 +184,11 @@ void InputDispatcher::dispatchOnce() { // Run any deferred commands. skipPoll |= runCommandsLockedInterruptible(); + + // Wake up synchronization waiters, if needed. + if (isFullySynchronizedLocked()) { + mFullySynchronizedCondition.broadcast(); + } } // release lock // If we dispatched anything, don't poll just now. Wait for the next iteration. @@ -202,6 +214,7 @@ bool InputDispatcher::runCommandsLockedInterruptible() { Command command = commandEntry->command; (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible' + commandEntry->connection.clear(); mAllocator.releaseCommandEntry(commandEntry); } while (! mCommandQueue.isEmpty()); return true; @@ -272,28 +285,23 @@ void InputDispatcher::processKeyRepeatLockedInterruptible( // Synthesize a key repeat after the repeat timeout expired. // We reuse the previous key entry if otherwise unreferenced. KeyEntry* entry = mKeyRepeatState.lastKeyEntry; + uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK; if (entry->refCount == 1) { + entry->eventTime = currentTime; + entry->downTime = currentTime; + entry->policyFlags = policyFlags; entry->repeatCount += 1; } else { - KeyEntry* newEntry = mAllocator.obtainKeyEntry(); - newEntry->deviceId = entry->deviceId; - newEntry->nature = entry->nature; - newEntry->policyFlags = entry->policyFlags; - newEntry->action = entry->action; - newEntry->flags = entry->flags; - newEntry->keyCode = entry->keyCode; - newEntry->scanCode = entry->scanCode; - newEntry->metaState = entry->metaState; - newEntry->repeatCount = entry->repeatCount + 1; + KeyEntry* newEntry = mAllocator.obtainKeyEntry(currentTime, + entry->deviceId, entry->nature, policyFlags, + entry->action, entry->flags, entry->keyCode, entry->scanCode, + entry->metaState, entry->repeatCount + 1, currentTime); mKeyRepeatState.lastKeyEntry = newEntry; mAllocator.releaseKeyEntry(entry); entry = newEntry; } - entry->eventTime = currentTime; - entry->downTime = currentTime; - entry->policyFlags = 0; mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatTimeout; @@ -358,12 +366,15 @@ void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible( entry->downTime, entry->eventTime); mCurrentInputTargets.clear(); - mPolicy->getKeyEventTargets(& mReusableKeyEvent, entry->policyFlags, + int32_t injectionResult = mPolicy->getKeyEventTargets(& mReusableKeyEvent, + entry->policyFlags, entry->injectorPid, entry->injectorUid, mCurrentInputTargets); mLock.lock(); mCurrentInputTargetsValid = true; + setInjectionResultLocked(entry, injectionResult); + dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); } @@ -384,12 +395,15 @@ void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible( entry->firstSample.pointerCoords); mCurrentInputTargets.clear(); - mPolicy->getMotionEventTargets(& mReusableMotionEvent, entry->policyFlags, + int32_t injectionResult = mPolicy->getMotionEventTargets(& mReusableMotionEvent, + entry->policyFlags, entry->injectorPid, entry->injectorUid, mCurrentInputTargets); mLock.lock(); mCurrentInputTargetsValid = true; + setInjectionResultLocked(entry, injectionResult); + dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); } @@ -410,7 +424,7 @@ void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTi inputTarget.inputChannel->getReceivePipeFd()); if (connectionIndex >= 0) { sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); - prepareDispatchCycleLocked(currentTime, connection.get(), eventEntry, & inputTarget, + prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget, resumeWithAppendedMotionSample); } else { LOGW("Framework requested delivery of an input event to channel '%s' but it " @@ -420,8 +434,8 @@ void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTi } } -void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, Connection* connection, - EventEntry* eventEntry, const InputTarget* inputTarget, +void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, + const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget, bool resumeWithAppendedMotionSample) { #if DEBUG_DISPATCH_CYCLE LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, timeout=%lldns, " @@ -547,12 +561,13 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, Connection // If the outbound queue was previously empty, start the dispatch cycle going. if (wasEmpty) { - activateConnectionLocked(connection); + activateConnectionLocked(connection.get()); startDispatchCycleLocked(currentTime, connection); } } -void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, Connection* connection) { +void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, + const sp<Connection>& connection) { #if DEBUG_DISPATCH_CYCLE LOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName()); @@ -682,13 +697,14 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, Connection* connection->lastDispatchTime = currentTime; nsecs_t timeout = dispatchEntry->timeout; - connection->nextTimeoutTime = (timeout >= 0) ? currentTime + timeout : LONG_LONG_MAX; + connection->setNextTimeoutTime(currentTime, timeout); // Notify other system components. onDispatchCycleStartedLocked(currentTime, connection); } -void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, Connection* connection) { +void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, + const sp<Connection>& connection) { #if DEBUG_DISPATCH_CYCLE LOGD("channel '%s' ~ finishDispatchCycle - %01.1fms since event, " "%01.1fms since dispatch", @@ -756,31 +772,48 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, Connection* } // Outbound queue is empty, deactivate the connection. - deactivateConnectionLocked(connection); + deactivateConnectionLocked(connection.get()); } -bool InputDispatcher::timeoutDispatchCycleLocked(nsecs_t currentTime, Connection* connection) { +void InputDispatcher::timeoutDispatchCycleLocked(nsecs_t currentTime, + const sp<Connection>& connection) { #if DEBUG_DISPATCH_CYCLE LOGD("channel '%s' ~ timeoutDispatchCycle", connection->getInputChannelName()); #endif if (connection->status != Connection::STATUS_NORMAL) { - return false; + return; } // Enter the not responding state. connection->status = Connection::STATUS_NOT_RESPONDING; connection->lastANRTime = currentTime; - bool deactivated = abortDispatchCycleLocked(currentTime, connection, false /*(not) broken*/); // Notify other system components. + // This enqueues a command which will eventually either call + // resumeAfterTimeoutDispatchCycleLocked or abortDispatchCycleLocked. onDispatchCycleANRLocked(currentTime, connection); - return deactivated; } -bool InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, Connection* connection, - bool broken) { +void InputDispatcher::resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime, + const sp<Connection>& connection, nsecs_t newTimeout) { +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked", + connection->getInputChannelName()); +#endif + + if (connection->status != Connection::STATUS_NOT_RESPONDING) { + return; + } + + // Resume normal dispatch. + connection->status = Connection::STATUS_NORMAL; + connection->setNextTimeoutTime(currentTime, newTimeout); +} + +void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, + const sp<Connection>& connection, bool broken) { #if DEBUG_DISPATCH_CYCLE LOGD("channel '%s' ~ abortDispatchCycle - broken=%s", connection->getInputChannelName(), broken ? "true" : "false"); @@ -790,14 +823,13 @@ bool InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, Connection* connection->nextTimeoutTime = LONG_LONG_MAX; // Clear the outbound queue. - bool deactivated = ! connection->outboundQueue.isEmpty(); - if (deactivated) { + if (! connection->outboundQueue.isEmpty()) { do { DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead(); mAllocator.releaseDispatchEntry(dispatchEntry); } while (! connection->outboundQueue.isEmpty()); - deactivateConnectionLocked(connection); + deactivateConnectionLocked(connection.get()); } // Handle the case where the connection appears to be unrecoverably broken. @@ -811,8 +843,6 @@ bool InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, Connection* onDispatchCycleBrokenLocked(currentTime, connection); } } - - return deactivated; } bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) { @@ -828,13 +858,13 @@ bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* dat return false; // remove the callback } - nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t currentTime = now(); sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex); if (events & (POLLERR | POLLHUP | POLLNVAL)) { LOGE("channel '%s' ~ Consumer closed input channel or an error occurred. " "events=0x%x", connection->getInputChannelName(), events); - d->abortDispatchCycleLocked(currentTime, connection.get(), true /*broken*/); + d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/); d->runCommandsLockedInterruptible(); return false; // remove the callback } @@ -849,12 +879,12 @@ bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* dat if (status) { LOGE("channel '%s' ~ Failed to receive finished signal. status=%d", connection->getInputChannelName(), status); - d->abortDispatchCycleLocked(currentTime, connection.get(), true /*broken*/); + d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/); d->runCommandsLockedInterruptible(); return false; // remove the callback } - d->finishDispatchCycleLocked(currentTime, connection.get()); + d->finishDispatchCycleLocked(currentTime, connection); d->runCommandsLockedInterruptible(); return true; } // release lock @@ -869,8 +899,7 @@ void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) { { // acquire lock AutoMutex _l(mLock); - ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry(); - newEntry->eventTime = eventTime; + ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry(eventTime); wasEmpty = mInboundQueue.isEmpty(); mInboundQueue.enqueueAtTail(newEntry); @@ -902,6 +931,9 @@ void InputDispatcher::notifyAppSwitchComing(nsecs_t eventTime) { LOGV("Dropping movement key during app switch: keyCode=%d, action=%d", keyEntry->keyCode, keyEntry->action); mInboundQueue.dequeue(keyEntry); + + setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED); + mAllocator.releaseKeyEntry(keyEntry); } else { // stop at last non-movement key @@ -928,18 +960,10 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t nat { // acquire lock AutoMutex _l(mLock); - KeyEntry* newEntry = mAllocator.obtainKeyEntry(); - newEntry->eventTime = eventTime; - newEntry->deviceId = deviceId; - newEntry->nature = nature; - newEntry->policyFlags = policyFlags; - newEntry->action = action; - newEntry->flags = flags; - newEntry->keyCode = keyCode; - newEntry->scanCode = scanCode; - newEntry->metaState = metaState; - newEntry->repeatCount = 0; - newEntry->downTime = downTime; + int32_t repeatCount = 0; + KeyEntry* newEntry = mAllocator.obtainKeyEntry(eventTime, + deviceId, nature, policyFlags, action, flags, keyCode, scanCode, + metaState, repeatCount, downTime); wasEmpty = mInboundQueue.isEmpty(); mInboundQueue.enqueueAtTail(newEntry); @@ -992,7 +1016,8 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t } if (motionEntry->action != MOTION_EVENT_ACTION_MOVE - || motionEntry->pointerCount != pointerCount) { + || motionEntry->pointerCount != pointerCount + || motionEntry->isInjected()) { // Last motion event in the queue for this device is not compatible for // appending new samples. Stop here. goto NoBatchingOrStreaming; @@ -1000,7 +1025,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t // The last motion event is a move and is compatible for appending. // Do the batching magic. - mAllocator.appendMotionSample(motionEntry, eventTime, pointerCount, pointerCoords); + mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords); #if DEBUG_BATCHING LOGD("Appended motion sample onto batch for most recent " "motion event for this device in the inbound queue."); @@ -1053,18 +1078,19 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t dispatchEntry->eventEntry); if (syncedMotionEntry->action != MOTION_EVENT_ACTION_MOVE || syncedMotionEntry->deviceId != deviceId - || syncedMotionEntry->pointerCount != pointerCount) { + || syncedMotionEntry->pointerCount != pointerCount + || syncedMotionEntry->isInjected()) { goto NoBatchingOrStreaming; } // Found synced move entry. Append sample and resume dispatch. mAllocator.appendMotionSample(syncedMotionEntry, eventTime, - pointerCount, pointerCoords); + pointerCoords); #if DEBUG_BATCHING LOGD("Appended motion sample onto batch for most recent synchronously " "dispatched motion event for this device in the outbound queues."); #endif - nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t currentTime = now(); dispatchEventToCurrentInputTargetsLocked(currentTime, syncedMotionEntry, true /*resumeWithAppendedMotionSample*/); @@ -1079,24 +1105,10 @@ NoBatchingOrStreaming:; } // Just enqueue a new motion event. - MotionEntry* newEntry = mAllocator.obtainMotionEntry(); - newEntry->eventTime = eventTime; - newEntry->deviceId = deviceId; - newEntry->nature = nature; - newEntry->policyFlags = policyFlags; - newEntry->action = action; - newEntry->metaState = metaState; - newEntry->edgeFlags = edgeFlags; - newEntry->xPrecision = xPrecision; - newEntry->yPrecision = yPrecision; - newEntry->downTime = downTime; - newEntry->pointerCount = pointerCount; - newEntry->firstSample.eventTime = eventTime; - newEntry->lastSample = & newEntry->firstSample; - for (uint32_t i = 0; i < pointerCount; i++) { - newEntry->pointerIds[i] = pointerIds[i]; - newEntry->firstSample.pointerCoords[i] = pointerCoords[i]; - } + MotionEntry* newEntry = mAllocator.obtainMotionEntry(eventTime, + deviceId, nature, policyFlags, action, metaState, edgeFlags, + xPrecision, yPrecision, downTime, + pointerCount, pointerIds, pointerCoords); wasEmpty = mInboundQueue.isEmpty(); mInboundQueue.enqueueAtTail(newEntry); @@ -1107,6 +1119,133 @@ NoBatchingOrStreaming:; } } +int32_t InputDispatcher::injectInputEvent(const InputEvent* event, + int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) { +#if DEBUG_INBOUND_EVENT_DETAILS + LOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, " + "sync=%d, timeoutMillis=%d", + event->getType(), injectorPid, injectorUid, sync, timeoutMillis); +#endif + + nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis); + + EventEntry* injectedEntry; + bool wasEmpty; + { // acquire lock + AutoMutex _l(mLock); + + injectedEntry = createEntryFromInputEventLocked(event); + injectedEntry->refCount += 1; + injectedEntry->injectorPid = injectorPid; + injectedEntry->injectorUid = injectorUid; + + wasEmpty = mInboundQueue.isEmpty(); + mInboundQueue.enqueueAtTail(injectedEntry); + + } // release lock + + if (wasEmpty) { + mPollLoop->wake(); + } + + int32_t injectionResult; + { // acquire lock + AutoMutex _l(mLock); + + for (;;) { + injectionResult = injectedEntry->injectionResult; + if (injectionResult != INPUT_EVENT_INJECTION_PENDING) { + break; + } + + nsecs_t remainingTimeout = endTime - now(); + if (remainingTimeout <= 0) { + injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; + sync = false; + break; + } + + mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout); + } + + if (sync) { + while (! isFullySynchronizedLocked()) { + nsecs_t remainingTimeout = endTime - now(); + if (remainingTimeout <= 0) { + injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; + break; + } + + mFullySynchronizedCondition.waitRelative(mLock, remainingTimeout); + } + } + + mAllocator.releaseEventEntry(injectedEntry); + } // release lock + + return injectionResult; +} + +void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) { + if (entry->isInjected()) { +#if DEBUG_INJECTION + LOGD("Setting input event injection result to %d. " + "injectorPid=%d, injectorUid=%d", + injectionResult, entry->injectorPid, entry->injectorUid); +#endif + + entry->injectionResult = injectionResult; + mInjectionResultAvailableCondition.broadcast(); + } +} + +bool InputDispatcher::isFullySynchronizedLocked() { + return mInboundQueue.isEmpty() && mActiveConnections.isEmpty(); +} + +InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked( + const InputEvent* event) { + switch (event->getType()) { + case INPUT_EVENT_TYPE_KEY: { + const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event); + uint32_t policyFlags = 0; // XXX consider adding a policy flag to track injected events + + KeyEntry* keyEntry = mAllocator.obtainKeyEntry(keyEvent->getEventTime(), + keyEvent->getDeviceId(), keyEvent->getNature(), policyFlags, + keyEvent->getAction(), keyEvent->getFlags(), + keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(), + keyEvent->getRepeatCount(), keyEvent->getDownTime()); + return keyEntry; + } + + case INPUT_EVENT_TYPE_MOTION: { + const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event); + uint32_t policyFlags = 0; // XXX consider adding a policy flag to track injected events + + const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); + const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); + size_t pointerCount = motionEvent->getPointerCount(); + + MotionEntry* motionEntry = mAllocator.obtainMotionEntry(*sampleEventTimes, + motionEvent->getDeviceId(), motionEvent->getNature(), policyFlags, + motionEvent->getAction(), motionEvent->getMetaState(), motionEvent->getEdgeFlags(), + motionEvent->getXPrecision(), motionEvent->getYPrecision(), + motionEvent->getDownTime(), uint32_t(pointerCount), + motionEvent->getPointerIds(), samplePointerCoords); + for (size_t i = motionEvent->getHistorySize(); i > 0; i--) { + sampleEventTimes += 1; + samplePointerCoords += pointerCount; + mAllocator.appendMotionSample(motionEntry, *sampleEventTimes, samplePointerCoords); + } + return motionEntry; + } + + default: + assert(false); + return NULL; + } +} + void InputDispatcher::resetKeyRepeatLocked() { if (mKeyRepeatState.lastKeyEntry) { mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry); @@ -1169,8 +1308,8 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh connection->status = Connection::STATUS_ZOMBIE; - nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); - abortDispatchCycleLocked(currentTime, connection.get(), true /*broken*/); + nsecs_t currentTime = now(); + abortDispatchCycleLocked(currentTime, connection, true /*broken*/); runCommandsLockedInterruptible(); } // release lock @@ -1202,11 +1341,11 @@ void InputDispatcher::deactivateConnectionLocked(Connection* connection) { } void InputDispatcher::onDispatchCycleStartedLocked( - nsecs_t currentTime, Connection* connection) { + nsecs_t currentTime, const sp<Connection>& connection) { } void InputDispatcher::onDispatchCycleFinishedLocked( - nsecs_t currentTime, Connection* connection, bool recoveredFromANR) { + nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR) { if (recoveredFromANR) { LOGI("channel '%s' ~ Recovered from ANR. %01.1fms since event, " "%01.1fms since dispatch, %01.1fms since ANR", @@ -1217,12 +1356,12 @@ void InputDispatcher::onDispatchCycleFinishedLocked( CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible); - commandEntry->inputChannel = connection->inputChannel; + commandEntry->connection = connection; } } void InputDispatcher::onDispatchCycleANRLocked( - nsecs_t currentTime, Connection* connection) { + nsecs_t currentTime, const sp<Connection>& connection) { LOGI("channel '%s' ~ Not responding! %01.1fms since event, %01.1fms since dispatch", connection->getInputChannelName(), connection->getEventLatencyMillis(currentTime), @@ -1230,47 +1369,64 @@ void InputDispatcher::onDispatchCycleANRLocked( CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doNotifyInputChannelANRLockedInterruptible); - commandEntry->inputChannel = connection->inputChannel; + commandEntry->connection = connection; } void InputDispatcher::onDispatchCycleBrokenLocked( - nsecs_t currentTime, Connection* connection) { + nsecs_t currentTime, const sp<Connection>& connection) { LOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!", connection->getInputChannelName()); CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible); - commandEntry->inputChannel = connection->inputChannel; + commandEntry->connection = connection; } void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible( CommandEntry* commandEntry) { - mLock.unlock(); + sp<Connection> connection = commandEntry->connection; - mPolicy->notifyInputChannelBroken(commandEntry->inputChannel); - commandEntry->inputChannel.clear(); + if (connection->status != Connection::STATUS_ZOMBIE) { + mLock.unlock(); - mLock.lock(); + mPolicy->notifyInputChannelBroken(connection->inputChannel); + + mLock.lock(); + } } void InputDispatcher::doNotifyInputChannelANRLockedInterruptible( CommandEntry* commandEntry) { - mLock.unlock(); + sp<Connection> connection = commandEntry->connection; - mPolicy->notifyInputChannelANR(commandEntry->inputChannel); - commandEntry->inputChannel.clear(); + if (connection->status != Connection::STATUS_ZOMBIE) { + mLock.unlock(); - mLock.lock(); + nsecs_t newTimeout; + bool resume = mPolicy->notifyInputChannelANR(connection->inputChannel, newTimeout); + + mLock.lock(); + + nsecs_t currentTime = now(); + if (resume) { + resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout); + } else { + abortDispatchCycleLocked(currentTime, connection, false /*(not) broken*/); + } + } } void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible( CommandEntry* commandEntry) { - mLock.unlock(); + sp<Connection> connection = commandEntry->connection; - mPolicy->notifyInputChannelRecoveredFromANR(commandEntry->inputChannel); - commandEntry->inputChannel.clear(); + if (connection->status != Connection::STATUS_ZOMBIE) { + mLock.unlock(); - mLock.lock(); + mPolicy->notifyInputChannelRecoveredFromANR(connection->inputChannel); + + mLock.lock(); + } } @@ -1279,29 +1435,69 @@ void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible( InputDispatcher::Allocator::Allocator() { } -InputDispatcher::ConfigurationChangedEntry* -InputDispatcher::Allocator::obtainConfigurationChangedEntry() { - ConfigurationChangedEntry* entry = mConfigurationChangeEntryPool.alloc(); +void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t type, + nsecs_t eventTime) { + entry->type = type; entry->refCount = 1; - entry->type = EventEntry::TYPE_CONFIGURATION_CHANGED; entry->dispatchInProgress = false; + entry->injectionResult = INPUT_EVENT_INJECTION_PENDING; + entry->injectorPid = -1; + entry->injectorUid = -1; +} + +InputDispatcher::ConfigurationChangedEntry* +InputDispatcher::Allocator::obtainConfigurationChangedEntry(nsecs_t eventTime) { + ConfigurationChangedEntry* entry = mConfigurationChangeEntryPool.alloc(); + initializeEventEntry(entry, EventEntry::TYPE_CONFIGURATION_CHANGED, eventTime); return entry; } -InputDispatcher::KeyEntry* InputDispatcher::Allocator::obtainKeyEntry() { +InputDispatcher::KeyEntry* InputDispatcher::Allocator::obtainKeyEntry(nsecs_t eventTime, + int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action, + int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, + int32_t repeatCount, nsecs_t downTime) { KeyEntry* entry = mKeyEntryPool.alloc(); - entry->refCount = 1; - entry->type = EventEntry::TYPE_KEY; - entry->dispatchInProgress = false; + initializeEventEntry(entry, EventEntry::TYPE_KEY, eventTime); + + entry->deviceId = deviceId; + entry->nature = nature; + entry->policyFlags = policyFlags; + entry->action = action; + entry->flags = flags; + entry->keyCode = keyCode; + entry->scanCode = scanCode; + entry->metaState = metaState; + entry->repeatCount = repeatCount; + entry->downTime = downTime; return entry; } -InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry() { +InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsecs_t eventTime, + int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action, + int32_t metaState, int32_t edgeFlags, float xPrecision, float yPrecision, + nsecs_t downTime, uint32_t pointerCount, + const int32_t* pointerIds, const PointerCoords* pointerCoords) { MotionEntry* entry = mMotionEntryPool.alloc(); - entry->refCount = 1; - entry->type = EventEntry::TYPE_MOTION; + initializeEventEntry(entry, EventEntry::TYPE_MOTION, eventTime); + + entry->eventTime = eventTime; + entry->deviceId = deviceId; + entry->nature = nature; + entry->policyFlags = policyFlags; + entry->action = action; + entry->metaState = metaState; + entry->edgeFlags = edgeFlags; + entry->xPrecision = xPrecision; + entry->yPrecision = yPrecision; + entry->downTime = downTime; + entry->pointerCount = pointerCount; + entry->firstSample.eventTime = eventTime; entry->firstSample.next = NULL; - entry->dispatchInProgress = false; + entry->lastSample = & entry->firstSample; + for (uint32_t i = 0; i < pointerCount; i++) { + entry->pointerIds[i] = pointerIds[i]; + entry->firstSample.pointerCoords[i] = pointerCoords[i]; + } return entry; } @@ -1379,10 +1575,11 @@ void InputDispatcher::Allocator::releaseCommandEntry(CommandEntry* entry) { } void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry, - nsecs_t eventTime, int32_t pointerCount, const PointerCoords* pointerCoords) { + nsecs_t eventTime, const PointerCoords* pointerCoords) { MotionSample* sample = mMotionSamplePool.alloc(); sample->eventTime = eventTime; - for (int32_t i = 0; i < pointerCount; i++) { + uint32_t pointerCount = motionEntry->pointerCount; + for (uint32_t i = 0; i < pointerCount; i++) { sample->pointerCoords[i] = pointerCoords[i]; } @@ -1407,6 +1604,10 @@ status_t InputDispatcher::Connection::initialize() { return inputPublisher.initialize(); } +void InputDispatcher::Connection::setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout) { + nextTimeoutTime = (timeout >= 0) ? currentTime + timeout : LONG_LONG_MAX; +} + const char* InputDispatcher::Connection::getStatusLabel() const { switch (status) { case STATUS_NORMAL: diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp index 7538dd0..32c58b4 100644 --- a/libs/ui/InputManager.cpp +++ b/libs/ui/InputManager.cpp @@ -80,6 +80,11 @@ status_t InputManager::unregisterInputChannel(const sp<InputChannel>& inputChann return mDispatcher->unregisterInputChannel(inputChannel); } +int32_t InputManager::injectInputEvent(const InputEvent* event, + int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) { + return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, sync, timeoutMillis); +} + void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) const { mReader->getCurrentInputConfiguration(outConfiguration); } diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp index 5a280ae..1824054 100644 --- a/libs/ui/InputReader.cpp +++ b/libs/ui/InputReader.cpp @@ -1444,7 +1444,7 @@ void InputReader::dispatchTouch(nsecs_t when, InputDevice* device, uint32_t poli case InputReaderPolicyInterface::ROTATION_90: { float xTemp = x; x = y; - y = mDisplayHeight - xTemp; + y = mDisplayWidth - xTemp; break; } case InputReaderPolicyInterface::ROTATION_180: { @@ -1454,7 +1454,7 @@ void InputReader::dispatchTouch(nsecs_t when, InputDevice* device, uint32_t poli } case InputReaderPolicyInterface::ROTATION_270: { float xTemp = x; - x = mDisplayWidth - y; + x = mDisplayHeight - y; y = xTemp; break; } @@ -1510,7 +1510,7 @@ void InputReader::onTrackballStateChanged(nsecs_t when, uint32_t fields = device->trackball.accumulator.fields; bool downChanged = fields & InputDevice::TrackballState::Accumulator::FIELD_BTN_MOUSE; - bool deltaChanged = (fields & DELTA_FIELDS) == DELTA_FIELDS; + bool deltaChanged = fields & DELTA_FIELDS; bool down; if (downChanged) { @@ -1546,10 +1546,10 @@ void InputReader::onTrackballStateChanged(nsecs_t when, int32_t pointerId = 0; PointerCoords pointerCoords; - pointerCoords.x = device->trackball.accumulator.relX - * device->trackball.precalculated.xScale; - pointerCoords.y = device->trackball.accumulator.relY - * device->trackball.precalculated.yScale; + pointerCoords.x = fields & InputDevice::TrackballState::Accumulator::FIELD_REL_X + ? device->trackball.accumulator.relX * device->trackball.precalculated.xScale : 0; + pointerCoords.y = fields & InputDevice::TrackballState::Accumulator::FIELD_REL_Y + ? device->trackball.accumulator.relY * device->trackball.precalculated.yScale : 0; pointerCoords.pressure = 1.0f; // XXX Consider making this 1.0f if down, 0 otherwise. pointerCoords.size = 0; diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java index 72c4166..8d9bb29 100644 --- a/services/java/com/android/server/InputManager.java +++ b/services/java/com/android/server/InputManager.java @@ -81,6 +81,10 @@ public class InputManager { private static native boolean nativeHasKeys(int[] keyCodes, boolean[] keyExists); private static native void nativeRegisterInputChannel(InputChannel inputChannel); private static native void nativeUnregisterInputChannel(InputChannel inputChannel); + private static native int nativeInjectKeyEvent(KeyEvent event, int nature, + int injectorPid, int injectorUid, boolean sync, int timeoutMillis); + private static native int nativeInjectMotionEvent(MotionEvent event, int nature, + int injectorPid, int injectorUid, boolean sync, int timeoutMillis); // Device class as defined by EventHub. private static final int CLASS_KEYBOARD = 0x00000001; @@ -90,6 +94,12 @@ public class InputManager { private static final int CLASS_TOUCHSCREEN_MT = 0x00000010; private static final int CLASS_DPAD = 0x00000020; + // Input event injection constants defined in InputDispatcher.h. + static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0; + static final int INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1; + static final int INPUT_EVENT_INJECTION_FAILED = 2; + static final int INPUT_EVENT_INJECTION_TIMED_OUT = 3; + public InputManager(Context context, WindowManagerService windowManagerService, WindowManagerPolicy windowManagerPolicy, @@ -215,37 +225,63 @@ public class InputManager { nativeUnregisterInputChannel(inputChannel); } - // TBD where this really belongs, duplicate copy in WindowManagerService - static final int INJECT_FAILED = 0; - static final int INJECT_SUCCEEDED = 1; - static final int INJECT_NO_PERMISSION = -1; - /** * Injects a key event into the event system on behalf of an application. + * This method may block even if sync is false because it must wait for previous events + * to be dispatched before it can determine whether input event injection will be + * permitted based on the current input focus. * @param event The event to inject. * @param nature The nature of the event. + * @param injectorPid The pid of the injecting application. + * @param injectorUid The uid of the injecting application. * @param sync If true, waits for the event to be completed before returning. - * @param pid The pid of the injecting application. - * @param uid The uid of the injecting application. - * @return INJECT_SUCCEEDED, INJECT_FAILED or INJECT_NO_PERMISSION + * @param timeoutMillis The injection timeout in milliseconds. + * @return One of the INPUT_EVENT_INJECTION_XXX constants. */ - public int injectKeyEvent(KeyEvent event, int nature, boolean sync, int pid, int uid) { - // TODO - return INJECT_FAILED; + public int injectKeyEvent(KeyEvent event, int nature, int injectorPid, int injectorUid, + boolean sync, int timeoutMillis) { + if (event == null) { + throw new IllegalArgumentException("event must not be null"); + } + if (injectorPid < 0 || injectorUid < 0) { + throw new IllegalArgumentException("injectorPid and injectorUid must not be negative."); + } + if (timeoutMillis <= 0) { + throw new IllegalArgumentException("timeoutMillis must be positive"); + } + + return nativeInjectKeyEvent(event, nature, injectorPid, injectorUid, + sync, timeoutMillis); } /** * Injects a motion event into the event system on behalf of an application. + * This method may block even if sync is false because it must wait for previous events + * to be dispatched before it can determine whether input event injection will be + * permitted based on the current input focus. * @param event The event to inject. * @param nature The nature of the event. * @param sync If true, waits for the event to be completed before returning. - * @param pid The pid of the injecting application. - * @param uid The uid of the injecting application. - * @return INJECT_SUCCEEDED, INJECT_FAILED or INJECT_NO_PERMISSION + * @param injectorPid The pid of the injecting application. + * @param injectorUid The uid of the injecting application. + * @param sync If true, waits for the event to be completed before returning. + * @param timeoutMillis The injection timeout in milliseconds. + * @return One of the INPUT_EVENT_INJECTION_XXX constants. */ - public int injectMotionEvent(MotionEvent event, int nature, boolean sync, int pid, int uid) { - // TODO - return INJECT_FAILED; + public int injectMotionEvent(MotionEvent event, int nature, int injectorPid, int injectorUid, + boolean sync, int timeoutMillis) { + if (event == null) { + throw new IllegalArgumentException("event must not be null"); + } + if (injectorPid < 0 || injectorUid < 0) { + throw new IllegalArgumentException("injectorPid and injectorUid must not be negative."); + } + if (timeoutMillis <= 0) { + throw new IllegalArgumentException("timeoutMillis must be positive"); + } + + return nativeInjectMotionEvent(event, nature, injectorPid, injectorUid, + sync, timeoutMillis); } public void dump(PrintWriter pw) { @@ -271,8 +307,6 @@ public class InputManager { private static final boolean DEBUG_VIRTUAL_KEYS = false; private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml"; - private final InputTargetList mReusableInputTargetList = new InputTargetList(); - @SuppressWarnings("unused") public boolean isScreenOn() { return mPowerManagerService.isScreenOn(); @@ -309,6 +343,21 @@ public class InputManager { } @SuppressWarnings("unused") + public void notifyInputChannelBroken(InputChannel inputChannel) { + mWindowManagerService.notifyInputChannelBroken(inputChannel); + } + + @SuppressWarnings("unused") + public long notifyInputChannelANR(InputChannel inputChannel) { + return mWindowManagerService.notifyInputChannelANR(inputChannel); + } + + @SuppressWarnings("unused") + public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) { + mWindowManagerService.notifyInputChannelRecoveredFromANR(inputChannel); + } + + @SuppressWarnings("unused") public int hackInterceptKey(int deviceId, int type, int scanCode, int keyCode, int policyFlags, int value, long whenNanos, boolean isScreenOn) { RawInputEvent event = new RawInputEvent(); @@ -437,24 +486,23 @@ public class InputManager { return names.toArray(new String[names.size()]); } + // TODO All code related to target identification should be moved down into native. @SuppressWarnings("unused") - public InputTarget[] getKeyEventTargets(KeyEvent event, int nature, int policyFlags) { - mReusableInputTargetList.clear(); - - mWindowManagerService.getKeyEventTargets(mReusableInputTargetList, - event, nature, policyFlags); - - return mReusableInputTargetList.toNullTerminatedArray(); + public int getKeyEventTargets(InputTargetList inputTargets, + KeyEvent event, int nature, int policyFlags, + int injectorPid, int injectorUid) { + inputTargets.clear(); + return mWindowManagerService.getKeyEventTargetsTd( + inputTargets, event, nature, policyFlags, injectorPid, injectorUid); } @SuppressWarnings("unused") - public InputTarget[] getMotionEventTargets(MotionEvent event, int nature, int policyFlags) { - mReusableInputTargetList.clear(); - - mWindowManagerService.getMotionEventTargets(mReusableInputTargetList, - event, nature, policyFlags); - - return mReusableInputTargetList.toNullTerminatedArray(); + public int getMotionEventTargets(InputTargetList inputTargets, + MotionEvent event, int nature, int policyFlags, + int injectorPid, int injectorUid) { + inputTargets.clear(); + return mWindowManagerService.getMotionEventTargetsTd( + inputTargets, event, nature, policyFlags, injectorPid, injectorUid); } } } diff --git a/services/java/com/android/server/InputTargetList.java b/services/java/com/android/server/InputTargetList.java index 1575612..83acc8f 100644 --- a/services/java/com/android/server/InputTargetList.java +++ b/services/java/com/android/server/InputTargetList.java @@ -29,7 +29,7 @@ import android.view.InputTarget; * * @hide */ -public class InputTargetList { +public final class InputTargetList { private InputTarget[] mArray; private int mCount; @@ -55,7 +55,7 @@ public class InputTargetList { count -= 1; mArray[count].recycle(); } - // mArray[0] could be set to null here but we do it in toNullTerminatedArray() + mArray[0] = null; } /** @@ -91,7 +91,7 @@ public class InputTargetList { mArray[mCount] = inputTarget; mCount += 1; - // mArray[mCount] could be set to null here but we do it in toNullTerminatedArray() + mArray[mCount] = null; } /** @@ -99,7 +99,6 @@ public class InputTargetList { * @return The input target array. */ public InputTarget[] toNullTerminatedArray() { - mArray[mCount] = null; return mArray; } }
\ No newline at end of file diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 503dbb9..cf4a60e 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -202,6 +202,10 @@ public class WindowManagerService extends IWindowManager.Stub /** Adjustment to time to perform a dim, to make it more dramatic. */ static final int DIM_DURATION_MULTIPLIER = 6; + + // Maximum number of milliseconds to wait for input event injection. + // FIXME is this value reasonable? + private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000; static final int INJECT_FAILED = 0; static final int INJECT_SUCCEEDED = 1; @@ -446,8 +450,6 @@ public class WindowManagerService extends IWindowManager.Stub final ArrayList<AppWindowToken> mToTopApps = new ArrayList<AppWindowToken>(); final ArrayList<AppWindowToken> mToBottomApps = new ArrayList<AppWindowToken>(); - //flag to detect fat touch events - boolean mFatTouch = false; Display mDisplay; H mH = new H(); @@ -5070,106 +5072,336 @@ public class WindowManagerService extends IWindowManager.Stub mPolicy.adjustConfigurationLw(config); return true; } + + /* Notifies the window manager about a broken input channel. + * + * Called by the InputManager. + */ + public void notifyInputChannelBroken(InputChannel inputChannel) { + synchronized (mWindowMap) { + WindowState windowState = getWindowStateForInputChannelLocked(inputChannel); + if (windowState == null) { + return; // irrelevant + } + + Slog.i(TAG, "WINDOW DIED " + windowState); + removeWindowLocked(windowState.mSession, windowState); + } + } + + /* Notifies the window manager about a broken input channel. + * + * Called by the InputManager. + */ + public long notifyInputChannelANR(InputChannel inputChannel) { + IApplicationToken appToken; + synchronized (mWindowMap) { + WindowState windowState = getWindowStateForInputChannelLocked(inputChannel); + if (windowState == null) { + return -2; // irrelevant, abort dispatching (-2) + } + + Slog.i(TAG, "Input event dispatching timed out sending to " + + windowState.mAttrs.getTitle()); + appToken = windowState.getAppToken(); + } + + try { + // Notify the activity manager about the timeout and let it decide whether + // to abort dispatching or keep waiting. + boolean abort = appToken.keyDispatchingTimedOut(); + if (abort) { + return -2; // abort dispatching + } + + // Return new timeout. + // We use -1 for infinite timeout to avoid clash with -2 magic number. + long newTimeout = appToken.getKeyDispatchingTimeout() * 1000000; + return newTimeout < 0 ? -1 : newTimeout; + } catch (RemoteException ex) { + return -2; // abort dispatching + } + } + + /* Notifies the window manager about a broken input channel. + * + * Called by the InputManager. + */ + public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) { + // Nothing to do just now. + // Just wait for the user to dismiss the ANR dialog. + + // TODO We could try to automatically dismiss the ANR dialog on recovery + // although that might be disorienting. + } + + private WindowState getWindowStateForInputChannelLocked(InputChannel inputChannel) { + int windowCount = mWindows.size(); + for (int i = 0; i < windowCount; i++) { + WindowState windowState = (WindowState) mWindows.get(i); + if (windowState.mInputChannel == inputChannel) { + return windowState; + } + } + + return null; + } // ------------------------------------------------------------- // Input Events and Focus Management // ------------------------------------------------------------- - public void getKeyEventTargets(InputTargetList inputTargets, - KeyEvent event, int nature, int policyFlags) { + private boolean checkInjectionPermissionTd(WindowState focus, + int injectorPid, int injectorUid) { + if (injectorUid > 0 && (focus == null || injectorUid != focus.mSession.mUid)) { + if (mContext.checkPermission( + android.Manifest.permission.INJECT_EVENTS, injectorPid, injectorUid) + != PackageManager.PERMISSION_GRANTED) { + Slog.w(TAG, "Permission denied: injecting key event from pid " + + injectorPid + " uid " + injectorUid + " to window " + focus + + " owned by uid " + focus.mSession.mUid); + return false; + } + } + return true; + } + + /* Gets the input targets for a key event. + * + * Called by the InputManager on the InputDispatcher thread. + */ + public int getKeyEventTargetsTd(InputTargetList inputTargets, + KeyEvent event, int nature, int policyFlags, int injectorPid, int injectorUid) { if (DEBUG_INPUT) Slog.v(TAG, "Dispatch key: " + event); // TODO what do we do with mDisplayFrozen? // TODO what do we do with focus.mToken.paused? WindowState focus = getFocusedWindow(); + + if (! checkInjectionPermissionTd(focus, injectorPid, injectorUid)) { + return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED; + } + + if (mPolicy.interceptKeyTi(focus, event.getKeyCode(), event.getMetaState(), + event.getAction() == KeyEvent.ACTION_DOWN, + event.getRepeatCount(), event.getFlags())) { + // Policy consumed the event. + return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED; + } + + if (focus == null) { + return InputManager.INPUT_EVENT_INJECTION_FAILED; + } + wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT); - addInputTarget(inputTargets, focus, InputTarget.FLAG_SYNC); + addInputTargetTd(inputTargets, focus, InputTarget.FLAG_SYNC); + return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED; } - // Target of Motion events - WindowState mTouchFocus; - - // Windows above the target who would like to receive an "outside" - // touch event for any down events outside of them. - // (This is a linked list by way of WindowState.mNextOutsideTouch.) - WindowState mOutsideTouchTargets; - - private void clearTouchFocus() { - mTouchFocus = null; - mOutsideTouchTargets = null; + /* Gets the input targets for a motion event. + * + * Called by the InputManager on the InputDispatcher thread. + */ + public int getMotionEventTargetsTd(InputTargetList inputTargets, + MotionEvent event, int nature, int policyFlags, int injectorPid, int injectorUid) { + switch (nature) { + case InputQueue.INPUT_EVENT_NATURE_TRACKBALL: + return getMotionEventTargetsForTrackballTd(inputTargets, event, policyFlags, + injectorPid, injectorUid); + + case InputQueue.INPUT_EVENT_NATURE_TOUCH: + return getMotionEventTargetsForTouchTd(inputTargets, event, policyFlags, + injectorPid, injectorUid); + + default: + return InputManager.INPUT_EVENT_INJECTION_FAILED; + } } - public void getMotionEventTargets(InputTargetList inputTargets, - MotionEvent event, int nature, int policyFlags) { - if (nature == InputQueue.INPUT_EVENT_NATURE_TRACKBALL) { - // More or less the same as for keys... - WindowState focus = getFocusedWindow(); - wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT); - - addInputTarget(inputTargets, focus, InputTarget.FLAG_SYNC); - return; + /* Gets the input targets for a trackball event. + * + * Called by the InputManager on the InputDispatcher thread. + */ + private int getMotionEventTargetsForTrackballTd(InputTargetList inputTargets, + MotionEvent event, int policyFlags, int injectorPid, int injectorUid) { + WindowState focus = getFocusedWindow(); + + if (! checkInjectionPermissionTd(focus, injectorPid, injectorUid)) { + return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED; } - int action = event.getAction(); - - // TODO detect cheek presses somewhere... either here or in native code + if (focus == null) { + return InputManager.INPUT_EVENT_INJECTION_FAILED; + } - final boolean screenWasOff = (policyFlags & WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0; + wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT); - WindowState target = mTouchFocus; + addInputTargetTd(inputTargets, focus, InputTarget.FLAG_SYNC); + return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED; + } + + /* Set to true when a fat touch has been detected during the processing of a touch event. + * + * Only used by getMotionEventTargetsForTouchTd. + * Set to true whenever a fat touch is detected and reset to false on ACTION_UP. + */ + private boolean mFatTouch; + + /* Set to true when we think the touch event. + * + * Only used by getMotionEventTargetsForTouchTd. + * Set to true on ACTION_DOWN and set to false on ACTION_UP. + */ + private boolean mTouchDown; + + /* Current target of Motion events. + * + * Only used by getMotionEventTargetsForTouchTd. + * Initialized on ACTION_DOWN and cleared on ACTION_UP. + */ + private WindowState mTouchFocus; + + /* Windows above the target that would like to receive an "outside" touch event + * for any down events outside of them. + * + * Only used by getMotionEventTargetsForTouchTd. + * Initialized on ACTION_DOWN and cleared immediately afterwards. + */ + private ArrayList<WindowState> mOutsideTouchTargets = new ArrayList<WindowState>(); + + /* Wallpaper windows that are currently receiving touch events. + * + * Only used by getMotionEventTargetsForTouchTd. + * Initialized on ACTION_DOWN and cleared on ACTION_UP. + */ + private ArrayList<WindowState> mWallpaperTouchTargets = new ArrayList<WindowState>(); + + /* Gets the input targets for a touch event. + * + * Called by the InputManager on the InputDispatcher thread. + */ + private int getMotionEventTargetsForTouchTd(InputTargetList inputTargets, + MotionEvent event, int policyFlags, int injectorPid, int injectorUid) { + final int action = event.getAction(); - if (action == MotionEvent.ACTION_UP) { - // let go of our target - mPowerManager.logPointerUpEvent(); - clearTouchFocus(); - } else if (action == MotionEvent.ACTION_DOWN) { - // acquire a new target - mPowerManager.logPointerDownEvent(); + if (action == MotionEvent.ACTION_DOWN) { + updateTouchFocusBeforeDownTd(event, policyFlags); + } else { + updateTouchFocusBeforeNonDownTd(event, policyFlags); + } + + boolean skipDelivery = false; + int touchTargetFlags = 0; - synchronized (mWindowMap) { - if (mTouchFocus != null) { - // this is weird, we got a pen down, but we thought it was - // already down! - // XXX: We should probably send an ACTION_UP to the current - // target. - Slog.w(TAG, "Pointer down received while already down in: " - + mTouchFocus); - clearTouchFocus(); - } - - // ACTION_DOWN is special, because we need to lock next events to - // the window we'll land onto. - final int x = (int) event.getX(); - final int y = (int) event.getY(); - - final ArrayList windows = mWindows; - final int N = windows.size(); - WindowState topErrWindow = null; - final Rect tmpRect = mTempRect; - for (int i=N-1; i>=0; i--) { - WindowState child = (WindowState)windows.get(i); - //Slog.i(TAG, "Checking dispatch to: " + child); - final int flags = child.mAttrs.flags; - if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) { - if (topErrWindow == null) { - topErrWindow = child; + int injectionResult = InputManager.INPUT_EVENT_INJECTION_SUCCEEDED; + WindowState focusedTouchTarget = mTouchFocus; + if (focusedTouchTarget == null) { + // In this case we are either dropping the event, or have received + // a move or up without a down. It is common to receive move + // events in such a way, since this means the user is moving the + // pointer without actually pressing down. All other cases should + // be atypical, so let's log them. + if (action != MotionEvent.ACTION_MOVE) { + Slog.w(TAG, "No window to dispatch pointer action " + action); + injectionResult = InputManager.INPUT_EVENT_INJECTION_FAILED; + } + } else { + // We have a valid focused touch target. + if (! checkInjectionPermissionTd(focusedTouchTarget, injectorPid, injectorUid)) { + return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED; + } + + wakeupIfNeeded(focusedTouchTarget, eventType(event)); + + if ((focusedTouchTarget.mAttrs.flags & + WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) { + // Target wants to ignore fat touch events + boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(event); + + if (cheekPress) { + if ((action == MotionEvent.ACTION_DOWN)) { + mFatTouch = true; + skipDelivery = true; + } else { + if (! mFatTouch) { + // cancel the earlier event + touchTargetFlags |= InputTarget.FLAG_CANCEL; + mFatTouch = true; + } else { + skipDelivery = true; } } - if (!child.isVisibleLw()) { - //Slog.i(TAG, "Not visible!"); - continue; - } - if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) { - //Slog.i(TAG, "Not touchable!"); - if ((flags & WindowManager.LayoutParams - .FLAG_WATCH_OUTSIDE_TOUCH) != 0) { - child.mNextOutsideTouch = mOutsideTouchTargets; - mOutsideTouchTargets = child; - } - continue; + } + } + } + + if (! skipDelivery) { + int outsideTargetCount = mOutsideTouchTargets.size(); + for (int i = 0; i < outsideTargetCount; i++) { + WindowState outsideTouchTarget = mOutsideTouchTargets.get(i); + addInputTargetTd(inputTargets, outsideTouchTarget, + InputTarget.FLAG_OUTSIDE | touchTargetFlags); + } + + int wallpaperTargetCount = mWallpaperTouchTargets.size(); + for (int i = 0; i < wallpaperTargetCount; i++) { + WindowState wallpaperTouchTarget = mWallpaperTouchTargets.get(i); + addInputTargetTd(inputTargets, wallpaperTouchTarget, + touchTargetFlags); + } + + if (focusedTouchTarget != null) { + addInputTargetTd(inputTargets, focusedTouchTarget, + InputTarget.FLAG_SYNC | touchTargetFlags); + } + } + + if (action == MotionEvent.ACTION_UP) { + updateTouchFocusAfterUpTd(event, policyFlags); + } + + return injectionResult; + } + + private void updateTouchFocusBeforeDownTd(MotionEvent event, int policyFlags) { + if (mTouchDown) { + // This is weird, we got a down, but we thought it was already down! + // XXX: We should probably send an ACTION_UP to the current target. + Slog.w(TAG, "Pointer down received while already down in: " + mTouchFocus); + updateTouchFocusAfterUpTd(event, policyFlags); + } + + mTouchDown = true; + mPowerManager.logPointerDownEvent(); + + final boolean screenWasOff = (policyFlags & WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0; + synchronized (mWindowMap) { + final int x = (int) event.getX(); + final int y = (int) event.getY(); + + final ArrayList windows = mWindows; + final int N = windows.size(); + WindowState topErrWindow = null; + final Rect tmpRect = mTempRect; + for (int i= N - 1; i >= 0; i--) { + WindowState child = (WindowState) windows.get(i); + //Slog.i(TAG, "Checking dispatch to: " + child); + + final int flags = child.mAttrs.flags; + if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) { + if (topErrWindow == null) { + topErrWindow = child; } + } + + if (!child.isVisibleLw()) { + //Slog.i(TAG, "Not visible!"); + continue; + } + + if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 0) { tmpRect.set(child.mFrame); if (child.mTouchableInsets == ViewTreeObserver .InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) { @@ -5195,7 +5427,7 @@ public class WindowManagerService extends IWindowManager.Stub |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); if (tmpRect.contains(x, y) || touchFlags == 0) { //Slog.i(TAG, "Using this target!"); - if (!screenWasOff || (flags & + if (! screenWasOff || (flags & WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING) != 0) { mTouchFocus = child; } else { @@ -5204,143 +5436,76 @@ public class WindowManagerService extends IWindowManager.Stub } break; } - - if ((flags & WindowManager.LayoutParams - .FLAG_WATCH_OUTSIDE_TOUCH) != 0) { - child.mNextOutsideTouch = mOutsideTouchTargets; - mOutsideTouchTargets = child; - //Slog.i(TAG, "Adding to outside target list: " + child); - } } - // if there's an error window but it's not accepting - // focus (typically because it is not yet visible) just - // wait for it -- any other focused window may in fact - // be in ANR state. - if (topErrWindow != null && mTouchFocus != topErrWindow) { - mTouchFocus = null; + if ((flags & WindowManager.LayoutParams + .FLAG_WATCH_OUTSIDE_TOUCH) != 0) { + //Slog.i(TAG, "Adding to outside target list: " + child); + mOutsideTouchTargets.add(child); } } - - target = mTouchFocus; - } - - if (target != null) { - wakeupIfNeeded(target, eventType(event)); - } - - int targetFlags = 0; - if (target == null) { - // In this case we are either dropping the event, or have received - // a move or up without a down. It is common to receive move - // events in such a way, since this means the user is moving the - // pointer without actually pressing down. All other cases should - // be atypical, so let's log them. - if (action != MotionEvent.ACTION_MOVE) { - Slog.w(TAG, "No window to dispatch pointer action " + action); - } - } else { - if ((target.mAttrs.flags & - WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) { - //target wants to ignore fat touch events - boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(event); - //explicit flag to return without processing event further - boolean returnFlag = false; - if((action == MotionEvent.ACTION_DOWN)) { - mFatTouch = false; - if(cheekPress) { - mFatTouch = true; - returnFlag = true; - } - } else { - if(action == MotionEvent.ACTION_UP) { - if(mFatTouch) { - //earlier even was invalid doesnt matter if current up is cheekpress or not - mFatTouch = false; - returnFlag = true; - } else if(cheekPress) { - //cancel the earlier event - targetFlags |= InputTarget.FLAG_CANCEL; - action = MotionEvent.ACTION_CANCEL; - } - } else if(action == MotionEvent.ACTION_MOVE) { - if(mFatTouch) { - //two cases here - //an invalid down followed by 0 or moves(valid or invalid) - //a valid down, invalid move, more moves. want to ignore till up - returnFlag = true; - } else if(cheekPress) { - //valid down followed by invalid moves - //an invalid move have to cancel earlier action - targetFlags |= InputTarget.FLAG_CANCEL; - action = MotionEvent.ACTION_CANCEL; - if (DEBUG_INPUT) Slog.v(TAG, "Sending cancel for invalid ACTION_MOVE"); - //note that the subsequent invalid moves will not get here - mFatTouch = true; - } - } - } //else if action - if(returnFlag) { - return; - } - } //end if target - } - - synchronized (mWindowMap) { - if (target != null && ! target.isVisibleLw()) { - target = null; + + // If there's an error window but it's not accepting focus (typically because + // it is not yet visible) just wait for it -- any other focused window may in fact + // be in ANR state. + if (topErrWindow != null && mTouchFocus != topErrWindow) { + mTouchFocus = null; } - if (action == MotionEvent.ACTION_DOWN) { - while (mOutsideTouchTargets != null) { - addInputTarget(inputTargets, mOutsideTouchTargets, - InputTarget.FLAG_OUTSIDE | targetFlags); - mOutsideTouchTargets = mOutsideTouchTargets.mNextOutsideTouch; - } + // Drop the touch focus if the window is not visible. + if (mTouchFocus != null && ! mTouchFocus.isVisibleLw()) { + mTouchFocus = null; } - // If we sent an initial down to the wallpaper, then continue - // sending events until the final up. - // Alternately if we are on top of the wallpaper, then the wallpaper also - // gets to see this movement. - if (mSendingPointersToWallpaper || - (target != null && action == MotionEvent.ACTION_DOWN - && mWallpaperTarget == target - && target.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD)) { + // Determine wallpaper targets. + if (mTouchFocus != null + && mTouchFocus == mWallpaperTarget + && mTouchFocus.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) { int curTokenIndex = mWallpaperTokens.size(); while (curTokenIndex > 0) { curTokenIndex--; WindowToken token = mWallpaperTokens.get(curTokenIndex); + int curWallpaperIndex = token.windows.size(); while (curWallpaperIndex > 0) { curWallpaperIndex--; WindowState wallpaper = token.windows.get(curWallpaperIndex); if ((wallpaper.mAttrs.flags & - WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) { - continue; - } - - switch (action) { - case MotionEvent.ACTION_DOWN: - mSendingPointersToWallpaper = true; - break; - case MotionEvent.ACTION_UP: - mSendingPointersToWallpaper = false; - break; + WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 0) { + mWallpaperTouchTargets.add(wallpaper); } - - addInputTarget(inputTargets, wallpaper, targetFlags); } } } - - if (target != null) { - addInputTarget(inputTargets, target, InputTarget.FLAG_SYNC | targetFlags); + } + } + + private void updateTouchFocusBeforeNonDownTd(MotionEvent event, int policyFlags) { + synchronized (mWindowMap) { + // Drop the touch focus if the window is not visible. + if (mTouchFocus != null && ! mTouchFocus.isVisibleLw()) { + mTouchFocus = null; + mWallpaperTouchTargets.clear(); } } } - private void addInputTarget(InputTargetList inputTargets, WindowState window, int flags) { + private void updateTouchFocusAfterUpTd(MotionEvent event, int policyFlags) { + mFatTouch = false; + mTouchDown = false; + mTouchFocus = null; + mOutsideTouchTargets.clear(); + mWallpaperTouchTargets.clear(); + + mPowerManager.logPointerUpEvent(); + } + + /* Adds a window to a list of input targets. + * Do NOT call this method while holding any locks because the call to + * appToken.getKeyDispatchingTimeout() can potentially call into the ActivityManager + * and create a deadlock hazard. + */ + private void addInputTargetTd(InputTargetList inputTargets, WindowState window, int flags) { if (window.mInputChannel == null) { return; } @@ -5877,8 +6042,8 @@ public class WindowManagerService extends IWindowManager.Stub final int result; if (ENABLE_NATIVE_INPUT_DISPATCH) { - result = mInputManager.injectKeyEvent(newEvent, - InputQueue.INPUT_EVENT_NATURE_KEY, sync, pid, uid); + result = mInputManager.injectKeyEvent(newEvent, InputQueue.INPUT_EVENT_NATURE_KEY, + pid, uid, sync, INJECTION_TIMEOUT_MILLIS); } else { result = dispatchKey(newEvent, pid, uid); if (sync) { @@ -5887,14 +6052,7 @@ public class WindowManagerService extends IWindowManager.Stub } Binder.restoreCallingIdentity(ident); - switch (result) { - case INJECT_NO_PERMISSION: - throw new SecurityException( - "Injecting to another application requires INJECT_EVENTS permission"); - case INJECT_SUCCEEDED: - return true; - } - return false; + return reportInjectionResult(result); } /** @@ -5913,8 +6071,8 @@ public class WindowManagerService extends IWindowManager.Stub final int result; if (ENABLE_NATIVE_INPUT_DISPATCH) { - result = mInputManager.injectMotionEvent(ev, - InputQueue.INPUT_EVENT_NATURE_TOUCH, sync, pid, uid); + result = mInputManager.injectMotionEvent(ev, InputQueue.INPUT_EVENT_NATURE_TOUCH, + pid, uid, sync, INJECTION_TIMEOUT_MILLIS); } else { result = dispatchPointer(null, ev, pid, uid); if (sync) { @@ -5923,14 +6081,7 @@ public class WindowManagerService extends IWindowManager.Stub } Binder.restoreCallingIdentity(ident); - switch (result) { - case INJECT_NO_PERMISSION: - throw new SecurityException( - "Injecting to another application requires INJECT_EVENTS permission"); - case INJECT_SUCCEEDED: - return true; - } - return false; + return reportInjectionResult(result); } /** @@ -5949,8 +6100,8 @@ public class WindowManagerService extends IWindowManager.Stub final int result; if (ENABLE_NATIVE_INPUT_DISPATCH) { - result = mInputManager.injectMotionEvent(ev, - InputQueue.INPUT_EVENT_NATURE_TRACKBALL, sync, pid, uid); + result = mInputManager.injectMotionEvent(ev, InputQueue.INPUT_EVENT_NATURE_TRACKBALL, + pid, uid, sync, INJECTION_TIMEOUT_MILLIS); } else { result = dispatchTrackball(null, ev, pid, uid); if (sync) { @@ -5959,14 +6110,37 @@ public class WindowManagerService extends IWindowManager.Stub } Binder.restoreCallingIdentity(ident); - switch (result) { - case INJECT_NO_PERMISSION: - throw new SecurityException( - "Injecting to another application requires INJECT_EVENTS permission"); - case INJECT_SUCCEEDED: - return true; + return reportInjectionResult(result); + } + + private boolean reportInjectionResult(int result) { + if (ENABLE_NATIVE_INPUT_DISPATCH) { + switch (result) { + case InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED: + Slog.w(TAG, "Input event injection permission denied."); + throw new SecurityException( + "Injecting to another application requires INJECT_EVENTS permission"); + case InputManager.INPUT_EVENT_INJECTION_SUCCEEDED: + Slog.v(TAG, "Input event injection succeeded."); + return true; + case InputManager.INPUT_EVENT_INJECTION_TIMED_OUT: + Slog.w(TAG, "Input event injection timed out."); + return false; + case InputManager.INPUT_EVENT_INJECTION_FAILED: + default: + Slog.w(TAG, "Input event injection failed."); + return false; + } + } else { + switch (result) { + case INJECT_NO_PERMISSION: + throw new SecurityException( + "Injecting to another application requires INJECT_EVENTS permission"); + case INJECT_SUCCEEDED: + return true; + } + return false; } - return false; } private WindowState getFocusedWindow() { diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index ab3922f..1a6119a 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -50,6 +50,9 @@ static struct { jmethodID isScreenBright; jmethodID notifyConfigurationChanged; jmethodID notifyLidSwitchChanged; + jmethodID notifyInputChannelBroken; + jmethodID notifyInputChannelANR; + jmethodID notifyInputChannelRecoveredFromANR; jmethodID virtualKeyFeedback; jmethodID hackInterceptKey; jmethodID goToSleep; @@ -73,6 +76,13 @@ static struct { jfieldID height; } gVirtualKeyDefinitionClassInfo; +static struct { + jclass clazz; + + jmethodID ctor; + jfieldID mArray; +} gInputTargetListClassInfo; + // ---------------------------------------------------------------------------- class NativeInputManager : public virtual RefBase, @@ -89,6 +99,10 @@ public: void setDisplaySize(int32_t displayId, int32_t width, int32_t height); void setDisplayOrientation(int32_t displayId, int32_t orientation); + status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel, + jweak inputChannelObjWeak); + status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel); + /* --- InputReaderPolicyInterface implementation --- */ virtual bool getDisplayInfo(int32_t displayId, @@ -112,18 +126,20 @@ public: virtual void notifyConfigurationChanged(nsecs_t when); virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel); - virtual void notifyInputChannelANR(const sp<InputChannel>& inputChannel); + virtual bool notifyInputChannelANR(const sp<InputChannel>& inputChannel, + nsecs_t& outNewTimeout); virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel); virtual nsecs_t getKeyRepeatTimeout(); - virtual void getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, - Vector<InputTarget>& outTargets); - virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, - Vector<InputTarget>& outTargets); + virtual int32_t getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, + int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets); + virtual int32_t getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, + int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets); private: sp<InputManager> mInputManager; jobject mCallbacksObj; + jobject mReusableInputTargetListObj; // Cached filtering policies. int32_t mFilterTouchEvents; @@ -138,12 +154,20 @@ private: bool isScreenOn(); bool isScreenBright(); + // Weak references to all currently registered input channels by receive fd. + Mutex mInputChannelRegistryLock; + KeyedVector<int, jweak> mInputChannelObjWeakByReceiveFd; + + jobject getInputChannelObjLocal(JNIEnv* env, const sp<InputChannel>& inputChannel); + static inline JNIEnv* jniEnv() { return AndroidRuntime::getJNIEnv(); } static bool isAppSwitchKey(int32_t keyCode); - static bool checkExceptionFromCallback(JNIEnv* env, const char* methodName); + static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName); + static void populateInputTargets(JNIEnv* env, jobject inputTargetListObj, + Vector<InputTarget>& outTargets); }; // ---------------------------------------------------------------------------- @@ -155,6 +179,11 @@ NativeInputManager::NativeInputManager(jobject callbacksObj) : mCallbacksObj = env->NewGlobalRef(callbacksObj); + jobject inputTargetListObj = env->NewObject(gInputTargetListClassInfo.clazz, + gInputTargetListClassInfo.ctor); + mReusableInputTargetListObj = env->NewGlobalRef(inputTargetListObj); + env->DeleteLocalRef(inputTargetListObj); + sp<EventHub> eventHub = new EventHub(); mInputManager = new InputManager(eventHub, this, this); } @@ -163,13 +192,14 @@ NativeInputManager::~NativeInputManager() { JNIEnv* env = jniEnv(); env->DeleteGlobalRef(mCallbacksObj); + env->DeleteGlobalRef(mReusableInputTargetListObj); } bool NativeInputManager::isAppSwitchKey(int32_t keyCode) { return keyCode == KEYCODE_HOME || keyCode == KEYCODE_ENDCALL; } -bool NativeInputManager::checkExceptionFromCallback(JNIEnv* env, const char* methodName) { +bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { if (env->ExceptionCheck()) { LOGE("An exception was thrown by callback '%s'.", methodName); LOGE_EX(env); @@ -196,6 +226,86 @@ void NativeInputManager::setDisplayOrientation(int32_t displayId, int32_t orient } } +status_t NativeInputManager::registerInputChannel(JNIEnv* env, + const sp<InputChannel>& inputChannel, jobject inputChannelObj) { + jweak inputChannelObjWeak = env->NewWeakGlobalRef(inputChannelObj); + if (! inputChannelObjWeak) { + LOGE("Could not create weak reference for input channel."); + LOGE_EX(env); + return NO_MEMORY; + } + + status_t status; + { + AutoMutex _l(mInputChannelRegistryLock); + + ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey( + inputChannel->getReceivePipeFd()); + if (index >= 0) { + LOGE("Input channel object '%s' has already been registered", + inputChannel->getName().string()); + status = INVALID_OPERATION; + goto DeleteWeakRef; + } + + mInputChannelObjWeakByReceiveFd.add(inputChannel->getReceivePipeFd(), + inputChannelObjWeak); + } + + status = mInputManager->registerInputChannel(inputChannel); + if (! status) { + return OK; + } + + { + AutoMutex _l(mInputChannelRegistryLock); + mInputChannelObjWeakByReceiveFd.removeItem(inputChannel->getReceivePipeFd()); + } + +DeleteWeakRef: + env->DeleteWeakGlobalRef(inputChannelObjWeak); + return status; +} + +status_t NativeInputManager::unregisterInputChannel(JNIEnv* env, + const sp<InputChannel>& inputChannel) { + jweak inputChannelObjWeak; + { + AutoMutex _l(mInputChannelRegistryLock); + + ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey( + inputChannel->getReceivePipeFd()); + if (index < 0) { + LOGE("Input channel object '%s' is not currently registered", + inputChannel->getName().string()); + return INVALID_OPERATION; + } + + inputChannelObjWeak = mInputChannelObjWeakByReceiveFd.valueAt(index); + mInputChannelObjWeakByReceiveFd.removeItemsAt(index); + } + + env->DeleteWeakGlobalRef(inputChannelObjWeak); + + return mInputManager->unregisterInputChannel(inputChannel); +} + +jobject NativeInputManager::getInputChannelObjLocal(JNIEnv* env, + const sp<InputChannel>& inputChannel) { + { + AutoMutex _l(mInputChannelRegistryLock); + + ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey( + inputChannel->getReceivePipeFd()); + if (index < 0) { + return NULL; + } + + jweak inputChannelObjWeak = mInputChannelObjWeakByReceiveFd.valueAt(index); + return env->NewLocalRef(inputChannelObjWeak); + } +} + bool NativeInputManager::getDisplayInfo(int32_t displayId, int32_t* width, int32_t* height, int32_t* orientation) { bool result = false; @@ -216,7 +326,7 @@ bool NativeInputManager::isScreenOn() { JNIEnv* env = jniEnv(); jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenOn); - if (checkExceptionFromCallback(env, "isScreenOn")) { + if (checkAndClearExceptionFromCallback(env, "isScreenOn")) { return true; } return result; @@ -226,7 +336,7 @@ bool NativeInputManager::isScreenBright() { JNIEnv* env = jniEnv(); jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenBright); - if (checkExceptionFromCallback(env, "isScreenBright")) { + if (checkAndClearExceptionFromCallback(env, "isScreenBright")) { return true; } return result; @@ -245,7 +355,7 @@ void NativeInputManager::virtualKeyFeedback(nsecs_t when, int32_t deviceId, env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.virtualKeyFeedback, when, deviceId, action, flags, keyCode, scanCode, metaState, downTime); - checkExceptionFromCallback(env, "virtualKeyFeedback"); + checkAndClearExceptionFromCallback(env, "virtualKeyFeedback"); } int32_t NativeInputManager::interceptKey(nsecs_t when, @@ -267,7 +377,7 @@ int32_t NativeInputManager::interceptKey(nsecs_t when, jint wmActions = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.hackInterceptKey, deviceId, EV_KEY, scanCode, keyCode, policyFlags, down ? 1 : 0, when, isScreenOn); - if (checkExceptionFromCallback(env, "hackInterceptKey")) { + if (checkAndClearExceptionFromCallback(env, "hackInterceptKey")) { wmActions = 0; } @@ -284,12 +394,12 @@ int32_t NativeInputManager::interceptKey(nsecs_t when, if (wmActions & WM_ACTION_GO_TO_SLEEP) { env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.goToSleep, when); - checkExceptionFromCallback(env, "goToSleep"); + checkAndClearExceptionFromCallback(env, "goToSleep"); } if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) { env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.pokeUserActivityForKey, when); - checkExceptionFromCallback(env, "pokeUserActivityForKey"); + checkAndClearExceptionFromCallback(env, "pokeUserActivityForKey"); } if (wmActions & WM_ACTION_PASS_TO_USER) { @@ -297,7 +407,7 @@ int32_t NativeInputManager::interceptKey(nsecs_t when, if (down && isAppSwitchKey(keyCode)) { env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyAppSwitchComing); - checkExceptionFromCallback(env, "notifyAppSwitchComing"); + checkAndClearExceptionFromCallback(env, "notifyAppSwitchComing"); actions |= InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING; } @@ -358,7 +468,7 @@ int32_t NativeInputManager::interceptSwitch(nsecs_t when, int32_t switchCode, case SW_LID: env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyLidSwitchChanged, when, switchValue == 0); - checkExceptionFromCallback(env, "notifyLidSwitchChanged"); + checkAndClearExceptionFromCallback(env, "notifyLidSwitchChanged"); break; } @@ -371,7 +481,7 @@ bool NativeInputManager::filterTouchEvents() { jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.filterTouchEvents); - if (checkExceptionFromCallback(env, "filterTouchEvents")) { + if (checkAndClearExceptionFromCallback(env, "filterTouchEvents")) { result = false; } @@ -386,7 +496,7 @@ bool NativeInputManager::filterJumpyTouchEvents() { jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.filterJumpyTouchEvents); - if (checkExceptionFromCallback(env, "filterJumpyTouchEvents")) { + if (checkAndClearExceptionFromCallback(env, "filterJumpyTouchEvents")) { result = false; } @@ -400,10 +510,10 @@ void NativeInputManager::getVirtualKeyDefinitions(const String8& deviceName, JNIEnv* env = jniEnv(); jstring deviceNameStr = env->NewStringUTF(deviceName.string()); - if (! checkExceptionFromCallback(env, "getVirtualKeyDefinitions")) { + if (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions")) { jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj, gCallbacksClassInfo.getVirtualKeyDefinitions, deviceNameStr)); - if (! checkExceptionFromCallback(env, "getVirtualKeyDefinitions") && result) { + if (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions") && result) { jsize length = env->GetArrayLength(result); for (jsize i = 0; i < length; i++) { jobject item = env->GetObjectArrayElement(result, i); @@ -433,7 +543,7 @@ void NativeInputManager::getExcludedDeviceNames(Vector<String8>& outExcludedDevi jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj, gCallbacksClassInfo.getExcludedDeviceNames)); - if (! checkExceptionFromCallback(env, "getExcludedDeviceNames") && result) { + if (! checkAndClearExceptionFromCallback(env, "getExcludedDeviceNames") && result) { jsize length = env->GetArrayLength(result); for (jsize i = 0; i < length; i++) { jstring item = jstring(env->GetObjectArrayElement(result, i)); @@ -460,7 +570,7 @@ void NativeInputManager::notifyConfigurationChanged(nsecs_t when) { env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyConfigurationChanged, when, config.touchScreen, config.keyboard, config.navigation); - checkExceptionFromCallback(env, "notifyConfigurationChanged"); + checkAndClearExceptionFromCallback(env, "notifyConfigurationChanged"); } void NativeInputManager::notifyInputChannelBroken(const sp<InputChannel>& inputChannel) { @@ -468,16 +578,47 @@ void NativeInputManager::notifyInputChannelBroken(const sp<InputChannel>& inputC LOGD("notifyInputChannelBroken - inputChannel='%s'", inputChannel->getName().string()); #endif - // TODO + JNIEnv* env = jniEnv(); + + jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel); + if (inputChannelObjLocal) { + env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyInputChannelBroken, + inputChannelObjLocal); + checkAndClearExceptionFromCallback(env, "notifyInputChannelBroken"); + + env->DeleteLocalRef(inputChannelObjLocal); + } } -void NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel) { +bool NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel, + nsecs_t& outNewTimeout) { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("notifyInputChannelANR - inputChannel='%s'", inputChannel->getName().string()); #endif - // TODO + JNIEnv* env = jniEnv(); + + jlong newTimeout; + jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel); + if (inputChannelObjLocal) { + newTimeout = env->CallLongMethod(mCallbacksObj, + gCallbacksClassInfo.notifyInputChannelANR, inputChannelObjLocal); + if (checkAndClearExceptionFromCallback(env, "notifyInputChannelANR")) { + newTimeout = -2; + } + + env->DeleteLocalRef(inputChannelObjLocal); + } else { + newTimeout = -2; + } + + if (newTimeout == -2) { + return false; // abort + } + + outNewTimeout = newTimeout; + return true; // resume } void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) { @@ -486,7 +627,16 @@ void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp<InputChanne inputChannel->getName().string()); #endif - // TODO + JNIEnv* env = jniEnv(); + + jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel); + if (inputChannelObjLocal) { + env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyInputChannelRecoveredFromANR, + inputChannelObjLocal); + checkAndClearExceptionFromCallback(env, "notifyInputChannelRecoveredFromANR"); + + env->DeleteLocalRef(inputChannelObjLocal); + } } nsecs_t NativeInputManager::getKeyRepeatTimeout() { @@ -499,73 +649,83 @@ nsecs_t NativeInputManager::getKeyRepeatTimeout() { } } -void NativeInputManager::getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, - Vector<InputTarget>& outTargets) { +int32_t NativeInputManager::getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, + int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) { #if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("getKeyEventTargets - policyFlags=%d", policyFlags); + LOGD("getKeyEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d", + policyFlags, injectorPid, injectorUid); #endif JNIEnv* env = jniEnv(); + jint injectionResult; jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent); if (! keyEventObj) { LOGE("Could not obtain DVM KeyEvent object to get key event targets."); + injectionResult = INPUT_EVENT_INJECTION_FAILED; } else { - jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj, - gCallbacksClassInfo.getKeyEventTargets, - keyEventObj, jint(keyEvent->getNature()), jint(policyFlags))); - if (! checkExceptionFromCallback(env, "getKeyEventTargets") && result) { - jsize length = env->GetArrayLength(result); - for (jsize i = 0; i < length; i++) { - jobject item = env->GetObjectArrayElement(result, i); - if (! item) { - break; // found null element indicating end of used portion of the array - } - - outTargets.add(); - android_view_InputTarget_toNative(env, item, & outTargets.editTop()); - - env->DeleteLocalRef(item); - } - env->DeleteLocalRef(result); + jint injectionResult = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.getKeyEventTargets, mReusableInputTargetListObj, + keyEventObj, jint(keyEvent->getNature()), jint(policyFlags), + jint(injectorPid), jint(injectorUid)); + if (checkAndClearExceptionFromCallback(env, "getKeyEventTargets")) { + injectionResult = INPUT_EVENT_INJECTION_FAILED; + } else { + populateInputTargets(env, mReusableInputTargetListObj, outTargets); } env->DeleteLocalRef(keyEventObj); } + return injectionResult; } -void NativeInputManager::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, - Vector<InputTarget>& outTargets) { +int32_t NativeInputManager::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, + int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) { #if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("getMotionEventTargets - policyFlags=%d", policyFlags); + LOGD("getMotionEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d", + policyFlags, injectorPid, injectorUid); #endif JNIEnv* env = jniEnv(); + jint injectionResult; jobject motionEventObj = android_view_MotionEvent_fromNative(env, motionEvent); if (! motionEventObj) { LOGE("Could not obtain DVM MotionEvent object to get key event targets."); + injectionResult = INPUT_EVENT_INJECTION_FAILED; } else { - jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj, - gCallbacksClassInfo.getMotionEventTargets, - motionEventObj, jint(motionEvent->getNature()), jint(policyFlags))); - if (! checkExceptionFromCallback(env, "getMotionEventTargets") && result) { - jsize length = env->GetArrayLength(result); - for (jsize i = 0; i < length; i++) { - jobject item = env->GetObjectArrayElement(result, i); - if (! item) { - break; // found null element indicating end of used portion of the array - } - - outTargets.add(); - android_view_InputTarget_toNative(env, item, & outTargets.editTop()); - - env->DeleteLocalRef(item); - } - env->DeleteLocalRef(result); + jint injectionResult = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.getMotionEventTargets, mReusableInputTargetListObj, + motionEventObj, jint(motionEvent->getNature()), jint(policyFlags), + jint(injectorPid), jint(injectorUid)); + if (checkAndClearExceptionFromCallback(env, "getMotionEventTargets")) { + injectionResult = INPUT_EVENT_INJECTION_FAILED; + } else { + populateInputTargets(env, mReusableInputTargetListObj, outTargets); } android_view_MotionEvent_recycle(env, motionEventObj); env->DeleteLocalRef(motionEventObj); } + return injectionResult; +} + +void NativeInputManager::populateInputTargets(JNIEnv* env, jobject inputTargetListObj, + Vector<InputTarget>& outTargets) { + jobjectArray inputTargetArray = jobjectArray(env->GetObjectField( + inputTargetListObj, gInputTargetListClassInfo.mArray)); + + jsize length = env->GetArrayLength(inputTargetArray); + for (jsize i = 0; i < length; i++) { + jobject item = env->GetObjectArrayElement(inputTargetArray, i); + if (! item) { + break; // found null element indicating end of used portion of the array + } + + outTargets.add(); + android_view_InputTarget_toNative(env, item, & outTargets.editTop()); + + env->DeleteLocalRef(item); + } + env->DeleteLocalRef(inputTargetArray); } @@ -686,7 +846,7 @@ static void android_server_InputManager_handleInputChannelDisposed(JNIEnv* env, "the input manager!", inputChannel->getName().string()); if (gNativeInputManager != NULL) { - gNativeInputManager->getInputManager()->unregisterInputChannel(inputChannel); + gNativeInputManager->unregisterInputChannel(env, inputChannel); } } @@ -703,7 +863,9 @@ static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env, return; } - status_t status = gNativeInputManager->getInputManager()->registerInputChannel(inputChannel); + + status_t status = gNativeInputManager->registerInputChannel( + env, inputChannel, inputChannelObj); if (status) { jniThrowRuntimeException(env, "Failed to register input channel. " "Check logs for details."); @@ -729,13 +891,41 @@ static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL); - status_t status = gNativeInputManager->getInputManager()->unregisterInputChannel(inputChannel); + status_t status = gNativeInputManager->unregisterInputChannel(env, inputChannel); if (status) { jniThrowRuntimeException(env, "Failed to unregister input channel. " "Check logs for details."); } } +static jint android_server_InputManager_nativeInjectKeyEvent(JNIEnv* env, jclass clazz, + jobject keyEventObj, jint nature, jint injectorPid, jint injectorUid, + jboolean sync, jint timeoutMillis) { + if (checkInputManagerUnitialized(env)) { + return INPUT_EVENT_INJECTION_FAILED; + } + + KeyEvent keyEvent; + android_view_KeyEvent_toNative(env, keyEventObj, nature, & keyEvent); + + return gNativeInputManager->getInputManager()->injectInputEvent(& keyEvent, + injectorPid, injectorUid, sync, timeoutMillis); +} + +static jint android_server_InputManager_nativeInjectMotionEvent(JNIEnv* env, jclass clazz, + jobject motionEventObj, jint nature, jint injectorPid, jint injectorUid, + jboolean sync, jint timeoutMillis) { + if (checkInputManagerUnitialized(env)) { + return INPUT_EVENT_INJECTION_FAILED; + } + + MotionEvent motionEvent; + android_view_MotionEvent_toNative(env, motionEventObj, nature, & motionEvent); + + return gNativeInputManager->getInputManager()->injectInputEvent(& motionEvent, + injectorPid, injectorUid, sync, timeoutMillis); +} + // ---------------------------------------------------------------------------- static JNINativeMethod gInputManagerMethods[] = { @@ -759,7 +949,11 @@ static JNINativeMethod gInputManagerMethods[] = { { "nativeRegisterInputChannel", "(Landroid/view/InputChannel;)V", (void*) android_server_InputManager_nativeRegisterInputChannel }, { "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V", - (void*) android_server_InputManager_nativeUnregisterInputChannel } + (void*) android_server_InputManager_nativeUnregisterInputChannel }, + { "nativeInjectKeyEvent", "(Landroid/view/KeyEvent;IIIZI)I", + (void*) android_server_InputManager_nativeInjectKeyEvent }, + { "nativeInjectMotionEvent", "(Landroid/view/MotionEvent;IIIZI)I", + (void*) android_server_InputManager_nativeInjectMotionEvent } }; #define FIND_CLASS(var, className) \ @@ -796,6 +990,15 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gCallbacksClassInfo.notifyLidSwitchChanged, gCallbacksClassInfo.clazz, "notifyLidSwitchChanged", "(JZ)V"); + GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelBroken, gCallbacksClassInfo.clazz, + "notifyInputChannelBroken", "(Landroid/view/InputChannel;)V"); + + GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelANR, gCallbacksClassInfo.clazz, + "notifyInputChannelANR", "(Landroid/view/InputChannel;)J"); + + GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelRecoveredFromANR, gCallbacksClassInfo.clazz, + "notifyInputChannelRecoveredFromANR", "(Landroid/view/InputChannel;)V"); + GET_METHOD_ID(gCallbacksClassInfo.virtualKeyFeedback, gCallbacksClassInfo.clazz, "virtualKeyFeedback", "(JIIIIIIJ)V"); @@ -825,10 +1028,12 @@ int register_android_server_InputManager(JNIEnv* env) { "getExcludedDeviceNames", "()[Ljava/lang/String;"); GET_METHOD_ID(gCallbacksClassInfo.getKeyEventTargets, gCallbacksClassInfo.clazz, - "getKeyEventTargets", "(Landroid/view/KeyEvent;II)[Landroid/view/InputTarget;"); + "getKeyEventTargets", + "(Lcom/android/server/InputTargetList;Landroid/view/KeyEvent;IIII)I"); GET_METHOD_ID(gCallbacksClassInfo.getMotionEventTargets, gCallbacksClassInfo.clazz, - "getMotionEventTargets", "(Landroid/view/MotionEvent;II)[Landroid/view/InputTarget;"); + "getMotionEventTargets", + "(Lcom/android/server/InputTargetList;Landroid/view/MotionEvent;IIII)I"); // VirtualKeyDefinition @@ -850,6 +1055,16 @@ int register_android_server_InputManager(JNIEnv* env) { GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.height, gVirtualKeyDefinitionClassInfo.clazz, "height", "I"); + // InputTargetList + + FIND_CLASS(gInputTargetListClassInfo.clazz, "com/android/server/InputTargetList"); + + GET_METHOD_ID(gInputTargetListClassInfo.ctor, gInputTargetListClassInfo.clazz, + "<init>", "()V"); + + GET_FIELD_ID(gInputTargetListClassInfo.mArray, gInputTargetListClassInfo.clazz, + "mArray", "[Landroid/view/InputTarget;"); + return 0; } |