diff options
author | Jeff Brown <jeffbrown@google.com> | 2010-09-15 21:42:28 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2010-09-15 21:42:28 -0700 |
commit | fa8186c71662ae9c869adbf379ccc2d917638881 (patch) | |
tree | 09ec6f08e79949096595440475f61ca2c6078dd8 | |
parent | fcec46b3d4fa08a36e904d7474a73032aa583ccd (diff) | |
parent | 7642c82c291051919aad87c797e2dc1d77ed55e1 (diff) | |
download | frameworks_base-fa8186c71662ae9c869adbf379ccc2d917638881.zip frameworks_base-fa8186c71662ae9c869adbf379ccc2d917638881.tar.gz frameworks_base-fa8186c71662ae9c869adbf379ccc2d917638881.tar.bz2 |
am 7642c82c: am f4d788c9: Merge "Make input dispatcher only ANR for foreground windows." into gingerbread
Merge commit '7642c82c291051919aad87c797e2dc1d77ed55e1'
* commit '7642c82c291051919aad87c797e2dc1d77ed55e1':
Make input dispatcher only ANR for foreground windows.
-rw-r--r-- | include/ui/InputDispatcher.h | 121 | ||||
-rw-r--r-- | libs/ui/InputDispatcher.cpp | 662 | ||||
-rw-r--r-- | services/java/com/android/server/InputManager.java | 24 | ||||
-rw-r--r-- | services/java/com/android/server/InputWindow.java | 9 | ||||
-rw-r--r-- | services/java/com/android/server/WindowManagerService.java | 88 | ||||
-rw-r--r-- | services/jni/com_android_server_InputManager.cpp | 139 |
6 files changed, 385 insertions, 658 deletions
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h index d7e6254..e466ddd 100644 --- a/include/ui/InputDispatcher.h +++ b/include/ui/InputDispatcher.h @@ -81,9 +81,8 @@ enum { */ struct InputTarget { enum { - /* This flag indicates that subsequent event delivery should be held until the - * current event is delivered to this target or a timeout occurs. */ - FLAG_SYNC = 0x01, + /* This flag indicates that the event is being delivered to a foreground application. */ + FLAG_FOREGROUND = 0x01, /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside * of the area of this target and so should instead be delivered as an @@ -109,12 +108,6 @@ struct InputTarget { // Flags for the input target. int32_t flags; - // The timeout for event delivery to this target in nanoseconds, or -1 to wait indefinitely. - nsecs_t timeout; - - // The time already spent waiting for this target in nanoseconds, or 0 if none. - nsecs_t timeSpentWaitingForApplication; - // The x and y offset to add to a MotionEvent as it is delivered. // (ignored for KeyEvents) float xOffset, yOffset; @@ -190,6 +183,7 @@ struct InputWindow { }; sp<InputChannel> inputChannel; + String8 name; int32_t layoutParamsFlags; int32_t layoutParamsType; nsecs_t dispatchingTimeout; @@ -206,9 +200,11 @@ struct InputWindow { int32_t touchableAreaRight; int32_t touchableAreaBottom; bool visible; + bool canReceiveKeys; bool hasFocus; bool hasWallpaper; bool paused; + int32_t layer; int32_t ownerPid; int32_t ownerUid; @@ -257,18 +253,12 @@ public: /* Notifies the system that an application is not responding. * Returns a new timeout to continue waiting, or 0 to abort dispatch. */ - virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle) = 0; + virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle, + const sp<InputChannel>& inputChannel) = 0; /* 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. - * Returns a new timeout to continue waiting, or 0 to abort dispatch. */ - virtual nsecs_t notifyInputChannelANR(const sp<InputChannel>& inputChannel) = 0; - - /* Notifies the system that an input channel recovered from ANR. */ - virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) = 0; - /* Gets the key repeat initial timeout or -1 if automatic key repeating is disabled. */ virtual nsecs_t getKeyRepeatTimeout() = 0; @@ -361,16 +351,6 @@ public: */ virtual void setInputDispatchMode(bool enabled, bool frozen) = 0; - /* Preempts input dispatch in progress by making pending synchronous - * dispatches asynchronous instead. This method is generally called during a focus - * transition from one application to the next so as to enable the new application - * to start receiving input as soon as possible without having to wait for the - * old application to finish up. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual void preemptInputDispatch() = 0; - /* Registers or unregister input channels that may be used as targets for input events. * If monitor is true, the channel will receive a copy of all input events. * @@ -424,7 +404,6 @@ public: virtual void setInputWindows(const Vector<InputWindow>& inputWindows); virtual void setFocusedApplication(const InputApplication* inputApplication); virtual void setInputDispatchMode(bool enabled, bool frozen); - virtual void preemptInputDispatch(); virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor); virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel); @@ -454,7 +433,7 @@ private: int32_t injectorUid; // -1 if not injected bool dispatchInProgress; // initially false, set to true while dispatching - int32_t pendingSyncDispatches; // the number of synchronous dispatches in progress + int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress inline bool isInjected() { return injectorPid >= 0; } @@ -522,7 +501,6 @@ private: int32_t targetFlags; float xOffset; float yOffset; - nsecs_t timeout; // True if dispatch has started. bool inProgress; @@ -540,12 +518,8 @@ private: // will be set to NULL. MotionSample* tailMotionSample; - inline bool isSyncTarget() const { - return targetFlags & InputTarget::FLAG_SYNC; - } - - inline void preemptSyncTarget() { - targetFlags &= ~ InputTarget::FLAG_SYNC; + inline bool hasForegroundTarget() const { + return targetFlags & InputTarget::FLAG_FOREGROUND; } }; @@ -628,6 +602,8 @@ private: dequeue(first); return first; } + + uint32_t count() const; }; /* Allocates queue entries and performs reference counting as needed. */ @@ -647,7 +623,7 @@ private: nsecs_t downTime, uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords); DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry, - int32_t targetFlags, float xOffset, float yOffset, nsecs_t timeout); + int32_t targetFlags, float xOffset, float yOffset); CommandEntry* obtainCommandEntry(Command command); void releaseEventEntry(EventEntry* entry); @@ -761,8 +737,6 @@ private: STATUS_NORMAL, // An unrecoverable communication error has occurred. STATUS_BROKEN, - // The client is not responding. - STATUS_NOT_RESPONDING, // The input channel has been unregistered. STATUS_ZOMBIE }; @@ -772,11 +746,9 @@ private: InputPublisher inputPublisher; InputState inputState; Queue<DispatchEntry> outboundQueue; - nsecs_t nextTimeoutTime; // next timeout time (LONG_LONG_MAX if none) nsecs_t lastEventTime; // the time when the event was originally captured nsecs_t lastDispatchTime; // the time when the last event was dispatched - nsecs_t lastANRTime; // the time when the last ANR was recorded explicit Connection(const sp<InputChannel>& inputChannel); @@ -788,18 +760,6 @@ private: // Returns NULL if not found. DispatchEntry* findQueuedDispatchEntryForEvent(const EventEntry* eventEntry) const; - // Determine whether this connection has a pending synchronous dispatch target. - // Since there can only ever be at most one such target at a time, if there is one, - // it must be at the tail because nothing else can be enqueued after it. - inline bool hasPendingSyncTarget() const { - return ! outboundQueue.isEmpty() && outboundQueue.tailSentinel.prev->isSyncTarget(); - } - - // Assuming there is a pending sync target, make it async. - inline void preemptSyncTarget() { - outboundQueue.tailSentinel.prev->preemptSyncTarget(); - } - // Gets the time since the current event was originally obtained from the input driver. inline double getEventLatencyMillis(nsecs_t currentTime) const { return (currentTime - lastEventTime) / 1000000.0; @@ -810,15 +770,7 @@ private: return (currentTime - lastDispatchTime) / 1000000.0; } - // Gets the time since the current event ANR was declared, if applicable. - inline double getANRLatencyMillis(nsecs_t currentTime) const { - return (currentTime - lastANRTime) / 1000000.0; - } - status_t initialize(); - - void setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout); - void resetTimeout(nsecs_t currentTime); }; sp<InputDispatcherPolicyInterface> mPolicy; @@ -851,7 +803,7 @@ private: // All registered connections mapped by receive pipe file descriptor. KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd; - ssize_t getConnectionIndex(const sp<InputChannel>& inputChannel); + ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel); // 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 @@ -859,12 +811,6 @@ private: // 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; - // Input channels that will receive a copy of all input events. Vector<sp<InputChannel> > mMonitoringChannels; @@ -877,7 +823,7 @@ private: void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult); Condition mInjectionSyncFinishedCondition; - void decrementPendingSyncDispatchesLocked(EventEntry* entry); + void decrementPendingForegroundDispatchesLocked(EventEntry* entry); // Throttling state. struct ThrottleState { @@ -951,8 +897,6 @@ private: void logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry); // The input targets that were most recently identified for dispatch. - // If there is a synchronous event dispatch in progress, the current input targets will - // remain unchanged until the dispatch has completed or been aborted. bool mCurrentInputTargetsValid; // false while targets are being recomputed Vector<InputTarget> mCurrentInputTargets; int32_t mCurrentInputWindowType; @@ -975,8 +919,9 @@ private: int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry, const InputApplication* application, const InputWindow* window, nsecs_t* nextWakeupTime); - void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout); - nsecs_t getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(nsecs_t currentTime); + void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout, + const sp<InputChannel>& inputChannel); + nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime); void resetANRTimeoutsLocked(); int32_t findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry, @@ -984,14 +929,16 @@ private: int32_t findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry, nsecs_t* nextWakeupTime, InputWindow** outWindow); - void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags, - nsecs_t timeSpentWaitingForApplication); + void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags); void addMonitoringTargetsLocked(); void pokeUserActivityLocked(nsecs_t eventTime, int32_t windowType, int32_t eventType); bool checkInjectionPermission(const InputWindow* window, int32_t injectorPid, int32_t injectorUid); bool isWindowObscuredLocked(const InputWindow* window); + bool isWindowFinishedWithPreviousInputLocked(const InputWindow* window); void releaseTouchedWindowLocked(); + String8 getApplicationWindowLabelLocked(const InputApplication* application, + const InputWindow* window); // Manage the dispatch cycle for a single connection. // These methods are deliberately not Interruptible because doing all of the work @@ -1000,21 +947,14 @@ private: void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget, bool resumeWithAppendedMotionSample); - void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, - nsecs_t timeSpentWaitingForApplication); + void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection); void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection); void startNextDispatchCycleLocked(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); - void drainOutboundQueueLocked(Connection* connection, DispatchEntry* firstDispatchEntryToDrain); + void drainOutboundQueueLocked(Connection* connection); static int handleReceiveCallback(int receiveFd, int events, void* data); - // Preempting input dispatch. - bool preemptInputDispatchInnerLocked(); - // Dump state. void dumpDispatchStateLocked(String8& dump); void logDispatchStateLocked(); @@ -1027,20 +967,23 @@ private: void onDispatchCycleStartedLocked( nsecs_t currentTime, const sp<Connection>& connection); void onDispatchCycleFinishedLocked( - nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR); - void onDispatchCycleANRLocked( nsecs_t currentTime, const sp<Connection>& connection); void onDispatchCycleBrokenLocked( nsecs_t currentTime, const sp<Connection>& connection); + void onANRLocked( + nsecs_t currentTime, const InputApplication* application, const InputWindow* window, + nsecs_t eventTime, nsecs_t waitStartTime); // Outbound policy interactions. void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry); void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry); - void doNotifyInputChannelANRLockedInterruptible(CommandEntry* commandEntry); - void doNotifyInputChannelRecoveredFromANRLockedInterruptible(CommandEntry* commandEntry); + void doNotifyANRLockedInterruptible(CommandEntry* commandEntry); void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry); void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry); - void doTargetsNotReadyTimeoutLockedInterruptible(CommandEntry* commandEntry); + + // Statistics gathering. + void updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry, + int32_t injectionResult, nsecs_t timeSpentWaitingForApplication); }; /* Enqueues and dispatches input events, endlessly. */ diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index 48dea57..3b3c483 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -196,42 +196,6 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, *nextWakeupTime = mAppSwitchDueTime; } - // Detect and process timeouts for all connections and determine if there are any - // synchronous event dispatches pending. This step is entirely non-interruptible. - bool havePendingSyncTarget = false; - size_t activeConnectionCount = mActiveConnections.size(); - for (size_t i = 0; i < activeConnectionCount; i++) { - Connection* connection = mActiveConnections.itemAt(i); - - if (connection->hasPendingSyncTarget()) { - if (isAppSwitchDue) { - connection->preemptSyncTarget(); - } else { - havePendingSyncTarget = true; - } - } - - nsecs_t connectionTimeoutTime = connection->nextTimeoutTime; - if (connectionTimeoutTime <= currentTime) { - mTimedOutConnections.add(connection); - } else if (connectionTimeoutTime < *nextWakeupTime) { - *nextWakeupTime = connectionTimeoutTime; - } - } - - size_t timedOutConnectionCount = mTimedOutConnections.size(); - for (size_t i = 0; i < timedOutConnectionCount; i++) { - Connection* connection = mTimedOutConnections.itemAt(i); - timeoutDispatchCycleLocked(currentTime, connection); - *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately - } - mTimedOutConnections.clear(); - - // If we have a pending synchronous target, skip dispatch. - if (havePendingSyncTarget) { - return; - } - // Ready to start a new event. // If we don't already have a pending event, go grab one. if (! mPendingEvent) { @@ -557,7 +521,7 @@ bool InputDispatcher::dispatchKeyLocked( } entry->dispatchInProgress = true; - startFindingTargetsLocked(); + startFindingTargetsLocked(); // resets mCurrentInputTargetsValid } // Identify targets. @@ -618,7 +582,7 @@ bool InputDispatcher::dispatchMotionLocked( logOutboundMotionDetailsLocked("dispatchMotion - ", entry); entry->dispatchInProgress = true; - startFindingTargetsLocked(); + startFindingTargetsLocked(); // resets mCurrentInputTargetsValid } bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER; @@ -728,7 +692,7 @@ void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTi for (size_t i = 0; i < mCurrentInputTargets.size(); i++) { const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i); - ssize_t connectionIndex = getConnectionIndex(inputTarget.inputChannel); + ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel); if (connectionIndex >= 0) { sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget, @@ -770,9 +734,8 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, } else { if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { #if DEBUG_FOCUS - LOGD("Waiting for application to become ready for input: name=%s, window=%s", - application ? application->name.string() : "<unknown>", - window ? window->inputChannel->getName().string() : "<unknown>"); + LOGD("Waiting for application to become ready for input: %s", + getApplicationWindowLabelLocked(application, window).string()); #endif nsecs_t timeout = window ? window->dispatchingTimeout : application ? application->dispatchingTimeout : DEFAULT_INPUT_DISPATCHING_TIMEOUT; @@ -789,21 +752,7 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, } if (currentTime >= mInputTargetWaitTimeoutTime) { - LOGI("Application is not ready for input: name=%s, window=%s," - "%01.1fms since event, %01.1fms since wait started", - application ? application->name.string() : "<unknown>", - window ? window->inputChannel->getName().string() : "<unknown>", - (currentTime - entry->eventTime) / 1000000.0, - (currentTime - mInputTargetWaitStartTime) / 1000000.0); - - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doTargetsNotReadyTimeoutLockedInterruptible); - if (application) { - commandEntry->inputApplicationHandle = application->handle; - } - if (window) { - commandEntry->inputChannel = window->inputChannel; - } + onANRLocked(currentTime, application, window, entry->eventTime, mInputTargetWaitStartTime); // Force poll loop to wake up immediately on next iteration once we get the // ANR response back from the policy. @@ -818,17 +767,25 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, } } -void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout) { +void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout, + const sp<InputChannel>& inputChannel) { if (newTimeout > 0) { // Extend the timeout. mInputTargetWaitTimeoutTime = now() + newTimeout; } else { // Give up. mInputTargetWaitTimeoutExpired = true; + + // Input state will not be realistic. Mark it out of sync. + ssize_t connectionIndex = getConnectionIndexLocked(inputChannel); + if (connectionIndex >= 0) { + sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); + connection->inputState.setOutOfSync(); + } } } -nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationWhileFindingTargetsLocked( +nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked( nsecs_t currentTime) { if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { return currentTime - mInputTargetWaitStartTime; @@ -841,13 +798,6 @@ void InputDispatcher::resetANRTimeoutsLocked() { LOGD("Resetting ANR timeouts."); #endif - // Reset timeouts for all active connections. - nsecs_t currentTime = now(); - for (size_t i = 0; i < mActiveConnections.size(); i++) { - Connection* connection = mActiveConnections[i]; - connection->resetTimeout(currentTime); - } - // Reset input target wait timeout. mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; } @@ -865,8 +815,8 @@ int32_t InputDispatcher::findFocusedWindowLocked(nsecs_t currentTime, const Even if (mFocusedApplication) { #if DEBUG_FOCUS LOGD("Waiting because there is no focused window but there is a " - "focused application that may eventually add a window: '%s'.", - mFocusedApplication->name.string()); + "focused application that may eventually add a window: %s.", + getApplicationWindowLabelLocked(mFocusedApplication, NULL).string()); #endif injectionResult = handleTargetsNotReadyLocked(currentTime, entry, mFocusedApplication, NULL, nextWakeupTime); @@ -894,19 +844,31 @@ int32_t InputDispatcher::findFocusedWindowLocked(nsecs_t currentTime, const Even goto Unresponsive; } + // If the currently focused window is still working on previous events then keep waiting. + if (! isWindowFinishedWithPreviousInputLocked(mFocusedWindow)) { +#if DEBUG_FOCUS + LOGD("Waiting because focused window still processing previous input."); +#endif + injectionResult = handleTargetsNotReadyLocked(currentTime, entry, + mFocusedApplication, mFocusedWindow, nextWakeupTime); + goto Unresponsive; + } + // Success! Output targets. injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; *outWindow = mFocusedWindow; - addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_SYNC, - getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(currentTime)); + addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND); // Done. Failed: Unresponsive: + nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime); + updateDispatchStatisticsLocked(currentTime, entry, + injectionResult, timeSpentWaitingForApplication); #if DEBUG_FOCUS - LOGD("findFocusedWindow finished: injectionResult=%d", - injectionResult); - logDispatchStateLocked(); + LOGD("findFocusedWindow finished: injectionResult=%d, " + "timeSpendWaitingForApplication=%0.1fms", + injectionResult, timeSpentWaitingForApplication / 1000000.0); #endif return injectionResult; } @@ -1018,8 +980,8 @@ int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const Moti if (mFocusedApplication) { #if DEBUG_FOCUS LOGD("Waiting because there is no touched window but there is a " - "focused application that may eventually add a new window: '%s'.", - mFocusedApplication->name.string()); + "focused application that may eventually add a new window: %s.", + getApplicationWindowLabelLocked(mFocusedApplication, NULL).string()); #endif injectionResult = handleTargetsNotReadyLocked(currentTime, entry, mFocusedApplication, NULL, nextWakeupTime); @@ -1051,6 +1013,17 @@ int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const Moti goto Unresponsive; } + // If the touched window is still working on previous events then keep waiting. + if (! isWindowFinishedWithPreviousInputLocked(newTouchedWindow)) { +#if DEBUG_FOCUS + LOGD("Waiting because touched window still processing previous input."); +#endif + injectionResult = handleTargetsNotReadyLocked(currentTime, entry, + NULL, newTouchedWindow, nextWakeupTime); + injectionPermission = INJECTION_PERMISSION_GRANTED; + goto Unresponsive; + } + // Success! Update the touch dispatch state for real. releaseTouchedWindowLocked(); @@ -1098,6 +1071,17 @@ int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const Moti injectionPermission = INJECTION_PERMISSION_GRANTED; goto Unresponsive; } + + // If the touched window is still working on previous events then keep waiting. + if (! isWindowFinishedWithPreviousInputLocked(mTouchedWindow)) { +#if DEBUG_FOCUS + LOGD("Waiting because touched window still processing previous input."); +#endif + injectionResult = handleTargetsNotReadyLocked(currentTime, entry, + NULL, mTouchedWindow, nextWakeupTime); + injectionPermission = INJECTION_PERMISSION_GRANTED; + goto Unresponsive; + } } // Success! Output targets. @@ -1108,7 +1092,7 @@ int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const Moti size_t numWallpaperWindows = mTouchedWallpaperWindows.size(); for (size_t i = 0; i < numWallpaperWindows; i++) { addWindowTargetLocked(mTouchedWallpaperWindows[i], - InputTarget::FLAG_WINDOW_IS_OBSCURED, 0); + InputTarget::FLAG_WINDOW_IS_OBSCURED); } size_t numOutsideTargets = mTempTouchedOutsideTargets.size(); @@ -1118,16 +1102,15 @@ int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const Moti if (outsideTarget.obscured) { outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; } - addWindowTargetLocked(outsideTarget.window, outsideTargetFlags, 0); + addWindowTargetLocked(outsideTarget.window, outsideTargetFlags); } mTempTouchedOutsideTargets.clear(); - int32_t targetFlags = InputTarget::FLAG_SYNC; + int32_t targetFlags = InputTarget::FLAG_FOREGROUND; if (mTouchedWindowIsObscured) { targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; } - addWindowTargetLocked(mTouchedWindow, targetFlags, - getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(currentTime)); + addWindowTargetLocked(mTouchedWindow, targetFlags); *outWindow = mTouchedWindow; } @@ -1166,10 +1149,13 @@ Failed: } Unresponsive: + nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime); + updateDispatchStatisticsLocked(currentTime, entry, + injectionResult, timeSpentWaitingForApplication); #if DEBUG_FOCUS - LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d", - injectionResult, injectionPermission); - logDispatchStateLocked(); + LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d," + "timeSpendWaitingForApplication=%0.1fms", + injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0); #endif return injectionResult; } @@ -1180,15 +1166,12 @@ void InputDispatcher::releaseTouchedWindowLocked() { mTouchedWallpaperWindows.clear(); } -void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags, - nsecs_t timeSpentWaitingForApplication) { +void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags) { mCurrentInputTargets.push(); InputTarget& target = mCurrentInputTargets.editTop(); target.inputChannel = window->inputChannel; target.flags = targetFlags; - target.timeout = window->dispatchingTimeout; - target.timeSpentWaitingForApplication = timeSpentWaitingForApplication; target.xOffset = - window->frameLeft; target.yOffset = - window->frameTop; } @@ -1200,8 +1183,6 @@ void InputDispatcher::addMonitoringTargetsLocked() { InputTarget& target = mCurrentInputTargets.editTop(); target.inputChannel = mMonitoringChannels[i]; target.flags = 0; - target.timeout = -1; - target.timeSpentWaitingForApplication = 0; target.xOffset = 0; target.yOffset = 0; } @@ -1241,6 +1222,34 @@ bool InputDispatcher::isWindowObscuredLocked(const InputWindow* window) { return false; } +bool InputDispatcher::isWindowFinishedWithPreviousInputLocked(const InputWindow* window) { + ssize_t connectionIndex = getConnectionIndexLocked(window->inputChannel); + if (connectionIndex >= 0) { + sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); + return connection->outboundQueue.isEmpty(); + } else { + return true; + } +} + +String8 InputDispatcher::getApplicationWindowLabelLocked(const InputApplication* application, + const InputWindow* window) { + if (application) { + if (window) { + String8 label(application->name); + label.append(" - "); + label.append(window->name); + return label; + } else { + return application->name; + } + } else if (window) { + return window->name; + } else { + return String8("<unknown application or window>"); + } +} + void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime, int32_t windowType, int32_t eventType) { CommandEntry* commandEntry = postCommandLocked( @@ -1254,25 +1263,18 @@ 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, " + LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, " "xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s", - connection->getInputChannelName(), inputTarget->flags, inputTarget->timeout, + connection->getInputChannelName(), inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset, toString(resumeWithAppendedMotionSample)); #endif // Skip this event if the connection status is not normal. - // We don't want to enqueue additional outbound events if the connection is broken or - // not responding. + // We don't want to enqueue additional outbound events if the connection is broken. if (connection->status != Connection::STATUS_NORMAL) { LOGW("channel '%s' ~ Dropping event because the channel status is %s", connection->getInputChannelName(), connection->getStatusLabel()); - - // If the connection is not responding but the user is poking the application anyways, - // retrigger the original timeout. - if (connection->status == Connection::STATUS_NOT_RESPONDING) { - timeoutDispatchCycleLocked(currentTime, connection); - } return; } @@ -1379,7 +1381,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, DispatchEntry* cancelationDispatchEntry = mAllocator.obtainDispatchEntry(cancelationEventEntry, - 0, inputTarget->xOffset, inputTarget->yOffset, inputTarget->timeout); + 0, inputTarget->xOffset, inputTarget->yOffset); // increments ref connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry); mAllocator.releaseEventEntry(cancelationEventEntry); @@ -1390,10 +1392,9 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref - inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset, - inputTarget->timeout); - if (dispatchEntry->isSyncTarget()) { - eventEntry->pendingSyncDispatches += 1; + inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset); + if (dispatchEntry->hasForegroundTarget()) { + eventEntry->pendingForegroundDispatches += 1; } // Handle the case where we could not stream a new motion sample because the consumer has @@ -1416,13 +1417,12 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, // If the outbound queue was previously empty, start the dispatch cycle going. if (wasEmpty) { activateConnectionLocked(connection.get()); - startDispatchCycleLocked(currentTime, connection, - inputTarget->timeSpentWaitingForApplication); + startDispatchCycleLocked(currentTime, connection); } } void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, - const sp<Connection>& connection, nsecs_t timeSpentWaitingForApplication) { + const sp<Connection>& connection) { #if DEBUG_DISPATCH_CYCLE LOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName()); @@ -1588,9 +1588,6 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, connection->lastEventTime = dispatchEntry->eventEntry->eventTime; connection->lastDispatchTime = currentTime; - nsecs_t timeout = dispatchEntry->timeout - timeSpentWaitingForApplication; - connection->setNextTimeoutTime(currentTime, timeout); - // Notify other system components. onDispatchCycleStartedLocked(currentTime, connection); } @@ -1610,21 +1607,8 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, return; } - // Clear the pending timeout. - connection->nextTimeoutTime = LONG_LONG_MAX; - - if (connection->status == Connection::STATUS_NOT_RESPONDING) { - // Recovering from an ANR. - connection->status = Connection::STATUS_NORMAL; - - // Notify other system components. - onDispatchCycleFinishedLocked(currentTime, connection, true /*recoveredFromANR*/); - } else { - // Normal finish. Not much to do here. - - // Notify other system components. - onDispatchCycleFinishedLocked(currentTime, connection, false /*recoveredFromANR*/); - } + // Notify other system components. + onDispatchCycleFinishedLocked(currentTime, connection); // Reset the publisher since the event has been consumed. // We do this now so that the publisher can release some of its internal resources @@ -1653,20 +1637,20 @@ void InputDispatcher::startNextDispatchCycleLocked(nsecs_t currentTime, dispatchEntry->inProgress = false; dispatchEntry->headMotionSample = dispatchEntry->tailMotionSample; dispatchEntry->tailMotionSample = NULL; - startDispatchCycleLocked(currentTime, connection, 0); + startDispatchCycleLocked(currentTime, connection); return; } // Finished. connection->outboundQueue.dequeueAtHead(); - if (dispatchEntry->isSyncTarget()) { - decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry); + if (dispatchEntry->hasForegroundTarget()) { + decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry); } mAllocator.releaseDispatchEntry(dispatchEntry); } else { // If the head is not in progress, then we must have already dequeued the in - // progress event, which means we actually aborted it (due to ANR). + // progress event, which means we actually aborted it. // So just start the next event for this connection. - startDispatchCycleLocked(currentTime, connection, 0); + startDispatchCycleLocked(currentTime, connection); return; } } @@ -1675,66 +1659,6 @@ void InputDispatcher::startNextDispatchCycleLocked(nsecs_t currentTime, deactivateConnectionLocked(connection.get()); } -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) { - // Enter the not responding state. - connection->status = Connection::STATUS_NOT_RESPONDING; - connection->lastANRTime = currentTime; - } else if (connection->status != Connection::STATUS_NOT_RESPONDING) { - // Connection is broken or dead. - return; - } - - // Notify other system components. - // This enqueues a command which will eventually call resumeAfterTimeoutDispatchCycleLocked. - onDispatchCycleANRLocked(currentTime, connection); -} - -void InputDispatcher::resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime, - const sp<Connection>& connection, nsecs_t newTimeout) { -#if DEBUG_DISPATCH_CYCLE - LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked - newTimeout=%lld", - connection->getInputChannelName(), newTimeout); -#endif - - if (connection->status != Connection::STATUS_NOT_RESPONDING) { - return; - } - - if (newTimeout > 0) { - // The system has decided to give the application some more time. - // Keep waiting synchronously and resume normal dispatch. - connection->status = Connection::STATUS_NORMAL; - connection->setNextTimeoutTime(currentTime, newTimeout); - } else { - // The system is about to throw up an ANR dialog and has requested that we abort dispatch. - // Reset the timeout. - connection->nextTimeoutTime = LONG_LONG_MAX; - - // Input state will no longer be realistic. - connection->inputState.setOutOfSync(); - - if (! connection->outboundQueue.isEmpty()) { - // Make the current pending dispatch asynchronous (if it isn't already) so that - // subsequent events can be delivered to the ANR dialog or to another application. - DispatchEntry* currentDispatchEntry = connection->outboundQueue.headSentinel.next; - currentDispatchEntry->preemptSyncTarget(); - - // Drain all but the first entry in the outbound queue. We keep the first entry - // since that is the one that dispatch is stuck on. We throw away the others - // so that we don't spam the application with stale messages if it eventually - // wakes up and recovers from the ANR. - drainOutboundQueueLocked(connection.get(), currentDispatchEntry->next); - } - } -} - void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, bool broken) { #if DEBUG_DISPATCH_CYCLE @@ -1742,20 +1666,16 @@ void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, connection->getInputChannelName(), toString(broken)); #endif - // Clear the pending timeout. - connection->nextTimeoutTime = LONG_LONG_MAX; - // Input state will no longer be realistic. connection->inputState.setOutOfSync(); // Clear the outbound queue. - drainOutboundQueueLocked(connection.get(), connection->outboundQueue.headSentinel.next); + drainOutboundQueueLocked(connection.get()); // Handle the case where the connection appears to be unrecoverably broken. // Ignore already broken or zombie connections. if (broken) { - if (connection->status == Connection::STATUS_NORMAL - || connection->status == Connection::STATUS_NOT_RESPONDING) { + if (connection->status == Connection::STATUS_NORMAL) { connection->status = Connection::STATUS_BROKEN; // Notify other system components. @@ -1764,24 +1684,16 @@ void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, } } -void InputDispatcher::drainOutboundQueueLocked(Connection* connection, - DispatchEntry* firstDispatchEntryToDrain) { - for (DispatchEntry* dispatchEntry = firstDispatchEntryToDrain; - dispatchEntry != & connection->outboundQueue.tailSentinel;) { - DispatchEntry* next = dispatchEntry->next; - connection->outboundQueue.dequeue(dispatchEntry); - - if (dispatchEntry->isSyncTarget()) { - decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry); +void InputDispatcher::drainOutboundQueueLocked(Connection* connection) { + while (! connection->outboundQueue.isEmpty()) { + DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead(); + if (dispatchEntry->hasForegroundTarget()) { + decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry); } mAllocator.releaseDispatchEntry(dispatchEntry); - - dispatchEntry = next; } - if (connection->outboundQueue.isEmpty()) { - deactivateConnectionLocked(connection); - } + deactivateConnectionLocked(connection); } int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) { @@ -1941,56 +1853,63 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t // STREAMING CASE // // There is no pending motion event (of any kind) for this device in the inbound queue. - // Search the outbound queues for a synchronously dispatched motion event for this - // device. If found, then we append the new sample to that event and then try to - // push it out to all current targets. It is possible that some targets will already - // have consumed the motion event. This case is automatically handled by the - // logic in prepareDispatchCycleLocked by tracking where resumption takes place. - // - // The reason we look for a synchronously dispatched motion event is because we - // want to be sure that no other motion events have been dispatched since the move. - // It's also convenient because it means that the input targets are still valid. - // This code could be improved to support streaming of asynchronously dispatched - // motion events (which might be significantly more efficient) but it may become - // a little more complicated as a result. - // - // Note: This code crucially depends on the invariant that an outbound queue always - // contains at most one synchronous event and it is always last (but it might - // not be first!). + // Search the outbound queue for the current foreground targets to find a dispatched + // motion event that is still in progress. If found, then, appen the new sample to + // that event and push it out to all current targets. The logic in + // prepareDispatchCycleLocked takes care of the case where some targets may + // already have consumed the motion event by starting a new dispatch cycle if needed. if (mCurrentInputTargetsValid) { - for (size_t i = 0; i < mActiveConnections.size(); i++) { - Connection* connection = mActiveConnections.itemAt(i); - if (! connection->outboundQueue.isEmpty()) { - DispatchEntry* dispatchEntry = connection->outboundQueue.tailSentinel.prev; - if (dispatchEntry->isSyncTarget()) { - if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) { - goto NoBatchingOrStreaming; - } - - MotionEntry* syncedMotionEntry = static_cast<MotionEntry*>( - dispatchEntry->eventEntry); - if (syncedMotionEntry->action != AMOTION_EVENT_ACTION_MOVE - || syncedMotionEntry->deviceId != deviceId - || syncedMotionEntry->pointerCount != pointerCount - || syncedMotionEntry->isInjected()) { - goto NoBatchingOrStreaming; - } - - // Found synced move entry. Append sample and resume dispatch. - mAllocator.appendMotionSample(syncedMotionEntry, eventTime, - 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 = now(); - dispatchEventToCurrentInputTargetsLocked(currentTime, syncedMotionEntry, - true /*resumeWithAppendedMotionSample*/); - - runCommandsLockedInterruptible(); - return; // done! - } + for (size_t i = 0; i < mCurrentInputTargets.size(); i++) { + const InputTarget& inputTarget = mCurrentInputTargets[i]; + if ((inputTarget.flags & InputTarget::FLAG_FOREGROUND) == 0) { + // Skip non-foreground targets. We only want to stream if there is at + // least one foreground target whose dispatch is still in progress. + continue; + } + + ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel); + if (connectionIndex < 0) { + // Connection must no longer be valid. + continue; + } + + sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); + if (connection->outboundQueue.isEmpty()) { + // This foreground target has an empty outbound queue. + continue; } + + DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next; + if (! dispatchEntry->inProgress + || dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) { + // No motion event is being dispatched. + continue; + } + + MotionEntry* motionEntry = static_cast<MotionEntry*>( + dispatchEntry->eventEntry); + if (motionEntry->action != AMOTION_EVENT_ACTION_MOVE + || motionEntry->deviceId != deviceId + || motionEntry->pointerCount != pointerCount + || motionEntry->isInjected()) { + // The motion event is not compatible with this move. + continue; + } + + // Hurray! This foreground target is currently dispatching a move event + // that we can stream onto. Append the motion sample and resume dispatch. + mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords); +#if DEBUG_BATCHING + LOGD("Appended motion sample onto batch for most recently dispatched " + "motion event for this device in the outbound queues. " + "Attempting to stream the motion sample."); +#endif + nsecs_t currentTime = now(); + dispatchEventToCurrentInputTargetsLocked(currentTime, motionEntry, + true /*resumeWithAppendedMotionSample*/); + + runCommandsLockedInterruptible(); + return; // done! } } @@ -2074,15 +1993,15 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED && syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) { - while (injectedEntry->pendingSyncDispatches != 0) { + while (injectedEntry->pendingForegroundDispatches != 0) { #if DEBUG_INJECTION - LOGD("injectInputEvent - Waiting for %d pending synchronous dispatches.", - injectedEntry->pendingSyncDispatches); + LOGD("injectInputEvent - Waiting for %d pending foreground dispatches.", + injectedEntry->pendingForegroundDispatches); #endif nsecs_t remainingTimeout = endTime - now(); if (remainingTimeout <= 0) { #if DEBUG_INJECTION - LOGD("injectInputEvent - Timed out waiting for pending synchronous " + LOGD("injectInputEvent - Timed out waiting for pending foreground " "dispatches to finish."); #endif injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; @@ -2137,10 +2056,10 @@ void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t inject } } -void InputDispatcher::decrementPendingSyncDispatchesLocked(EventEntry* entry) { - entry->pendingSyncDispatches -= 1; +void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* entry) { + entry->pendingForegroundDispatches -= 1; - if (entry->isInjected() && entry->pendingSyncDispatches == 0) { + if (entry->isInjected() && entry->pendingForegroundDispatches == 0) { mInjectionSyncFinishedCondition.broadcast(); } } @@ -2238,6 +2157,10 @@ void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) { { // acquire lock AutoMutex _l(mLock); + sp<InputChannel> oldFocusedWindowChannel = mFocusedWindow + ? mFocusedWindow->inputChannel : NULL; + int32_t oldFocusedWindowLayer = mFocusedWindow ? mFocusedWindow->layer : -1; + sp<InputChannel> touchedWindowChannel; if (mTouchedWindow) { touchedWindowChannel = mTouchedWindow->inputChannel; @@ -2251,8 +2174,6 @@ void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) { mTouchedWallpaperWindows.clear(); } - bool hadFocusedWindow = mFocusedWindow != NULL; - mFocusedWindow = NULL; mWallpaperWindows.clear(); @@ -2283,9 +2204,36 @@ void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) { mTempTouchedWallpaperChannels.clear(); - if ((hadFocusedWindow && ! mFocusedWindow) - || (mFocusedWindow && ! mFocusedWindow->visible)) { - preemptInputDispatchInnerLocked(); + bool preempt = false; + if (mFocusedWindow + && mFocusedWindow->inputChannel != oldFocusedWindowChannel + && mFocusedWindow->canReceiveKeys) { + // If the new input focus is an error window or appears above the current + // input focus, drop the current touched window so that we can start + // delivering events to the new input focus as soon as possible. + if (mFocusedWindow->layoutParamsFlags & InputWindow::FLAG_SYSTEM_ERROR) { +#if DEBUG_FOCUS + LOGD("Preempting: New SYSTEM_ERROR window; resetting state"); +#endif + preempt = true; + } else if (oldFocusedWindowChannel.get() != NULL + && mFocusedWindow->layer > oldFocusedWindowLayer) { +#if DEBUG_FOCUS + LOGD("Preempting: Transferring focus to new window at higher layer: " + "old win layer=%d, new win layer=%d", + oldFocusedWindowLayer, mFocusedWindow->layer); +#endif + preempt = true; + } + } + if (mTouchedWindow && ! mTouchedWindow->visible) { +#if DEBUG_FOCUS + LOGD("Preempting: Touched window became invisible."); +#endif + preempt = true; + } + if (preempt) { + releaseTouchedWindowLocked(); } #if DEBUG_FOCUS @@ -2359,39 +2307,6 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { } } -void InputDispatcher::preemptInputDispatch() { -#if DEBUG_FOCUS - LOGD("preemptInputDispatch"); -#endif - - bool preemptedOne; - { // acquire lock - AutoMutex _l(mLock); - preemptedOne = preemptInputDispatchInnerLocked(); - } // release lock - - if (preemptedOne) { - // Wake up the poll loop so it can get a head start dispatching the next event. - mLooper->wake(); - } -} - -bool InputDispatcher::preemptInputDispatchInnerLocked() { - bool preemptedOne = false; - for (size_t i = 0; i < mActiveConnections.size(); i++) { - Connection* connection = mActiveConnections[i]; - if (connection->hasPendingSyncTarget()) { -#if DEBUG_DISPATCH_CYCLE - LOGD("channel '%s' ~ Preempted pending synchronous dispatch", - connection->getInputChannelName()); -#endif - connection->preemptSyncTarget(); - preemptedOne = true; - } - } - return preemptedOne; -} - void InputDispatcher::logDispatchStateLocked() { String8 dump; dumpDispatchStateLocked(dump); @@ -2447,12 +2362,14 @@ void InputDispatcher::dumpDispatchStateLocked(String8& dump) { i, channel->getName().string()); } + dump.appendFormat(" inboundQueue: length=%u", mInboundQueue.count()); + for (size_t i = 0; i < mActiveConnections.size(); i++) { const Connection* connection = mActiveConnections[i]; - dump.appendFormat(" activeConnection[%d]: '%s', status=%s, hasPendingSyncTarget=%s, " + dump.appendFormat(" activeConnection[%d]: '%s', status=%s, outboundQueueLength=%u" "inputState.isNeutral=%s, inputState.isOutOfSync=%s\n", i, connection->getInputChannelName(), connection->getStatusLabel(), - toString(connection->hasPendingSyncTarget()), + connection->outboundQueue.count(), toString(connection->inputState.isNeutral()), toString(connection->inputState.isOutOfSync())); } @@ -2474,7 +2391,7 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan { // acquire lock AutoMutex _l(mLock); - if (getConnectionIndex(inputChannel) >= 0) { + if (getConnectionIndexLocked(inputChannel) >= 0) { LOGW("Attempted to register already registered input channel '%s'", inputChannel->getName().string()); return BAD_VALUE; @@ -2510,7 +2427,7 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh { // acquire lock AutoMutex _l(mLock); - ssize_t connectionIndex = getConnectionIndex(inputChannel); + ssize_t connectionIndex = getConnectionIndexLocked(inputChannel); if (connectionIndex < 0) { LOGW("Attempted to unregister already unregistered input channel '%s'", inputChannel->getName().string()); @@ -2543,7 +2460,7 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh return OK; } -ssize_t InputDispatcher::getConnectionIndex(const sp<InputChannel>& inputChannel) { +ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) { ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd()); if (connectionIndex >= 0) { sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); @@ -2578,31 +2495,7 @@ void InputDispatcher::onDispatchCycleStartedLocked( } void InputDispatcher::onDispatchCycleFinishedLocked( - 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", - connection->getInputChannelName(), - connection->getEventLatencyMillis(currentTime), - connection->getDispatchLatencyMillis(currentTime), - connection->getANRLatencyMillis(currentTime)); - - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible); - commandEntry->connection = connection; - } -} - -void InputDispatcher::onDispatchCycleANRLocked( 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), - connection->getDispatchLatencyMillis(currentTime)); - - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doNotifyInputChannelANRLockedInterruptible); - commandEntry->connection = connection; } void InputDispatcher::onDispatchCycleBrokenLocked( @@ -2615,6 +2508,25 @@ void InputDispatcher::onDispatchCycleBrokenLocked( commandEntry->connection = connection; } +void InputDispatcher::onANRLocked( + nsecs_t currentTime, const InputApplication* application, const InputWindow* window, + nsecs_t eventTime, nsecs_t waitStartTime) { + LOGI("Application is not responding: %s. " + "%01.1fms since event, %01.1fms since wait started", + getApplicationWindowLabelLocked(application, window).string(), + (currentTime - eventTime) / 1000000.0, + (currentTime - waitStartTime) / 1000000.0); + + CommandEntry* commandEntry = postCommandLocked( + & InputDispatcher::doNotifyANRLockedInterruptible); + if (application) { + commandEntry->inputApplicationHandle = application->handle; + } + if (window) { + commandEntry->inputChannel = window->inputChannel; + } +} + void InputDispatcher::doNotifyConfigurationChangedInterruptible( CommandEntry* commandEntry) { mLock.unlock(); @@ -2637,33 +2549,16 @@ void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible( } } -void InputDispatcher::doNotifyInputChannelANRLockedInterruptible( +void InputDispatcher::doNotifyANRLockedInterruptible( CommandEntry* commandEntry) { - sp<Connection> connection = commandEntry->connection; - - if (connection->status != Connection::STATUS_ZOMBIE) { - mLock.unlock(); - - nsecs_t newTimeout = mPolicy->notifyInputChannelANR(connection->inputChannel); - - mLock.lock(); - - nsecs_t currentTime = now(); - resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout); - } -} + mLock.unlock(); -void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible( - CommandEntry* commandEntry) { - sp<Connection> connection = commandEntry->connection; + nsecs_t newTimeout = mPolicy->notifyANR( + commandEntry->inputApplicationHandle, commandEntry->inputChannel); - if (connection->status != Connection::STATUS_ZOMBIE) { - mLock.unlock(); - - mPolicy->notifyInputChannelRecoveredFromANR(connection->inputChannel); + mLock.lock(); - mLock.lock(); - } + resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel); } void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( @@ -2695,22 +2590,9 @@ void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* comman mLock.lock(); } -void InputDispatcher::doTargetsNotReadyTimeoutLockedInterruptible( - CommandEntry* commandEntry) { - mLock.unlock(); - - nsecs_t newTimeout; - if (commandEntry->inputChannel.get()) { - newTimeout = mPolicy->notifyInputChannelANR(commandEntry->inputChannel); - } else if (commandEntry->inputApplicationHandle.get()) { - newTimeout = mPolicy->notifyANR(commandEntry->inputApplicationHandle); - } else { - newTimeout = 0; - } - - mLock.lock(); - - resumeAfterTargetsNotReadyTimeoutLocked(newTimeout); +void InputDispatcher::updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry, + int32_t injectionResult, nsecs_t timeSpentWaitingForApplication) { + // TODO Write some statistics about how long we spend waiting. } void InputDispatcher::dump(String8& dump) { @@ -2718,6 +2600,18 @@ void InputDispatcher::dump(String8& dump) { } +// --- InputDispatcher::Queue --- + +template <typename T> +uint32_t InputDispatcher::Queue<T>::count() const { + uint32_t result = 0; + for (const T* entry = headSentinel.next; entry != & tailSentinel; entry = entry->next) { + result += 1; + } + return result; +} + + // --- InputDispatcher::Allocator --- InputDispatcher::Allocator::Allocator() { @@ -2733,7 +2627,7 @@ void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t entry->injectionIsAsync = false; entry->injectorPid = -1; entry->injectorUid = -1; - entry->pendingSyncDispatches = 0; + entry->pendingForegroundDispatches = 0; } InputDispatcher::ConfigurationChangedEntry* @@ -2797,14 +2691,13 @@ InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsec InputDispatcher::DispatchEntry* InputDispatcher::Allocator::obtainDispatchEntry( EventEntry* eventEntry, - int32_t targetFlags, float xOffset, float yOffset, nsecs_t timeout) { + int32_t targetFlags, float xOffset, float yOffset) { DispatchEntry* entry = mDispatchEntryPool.alloc(); entry->eventEntry = eventEntry; eventEntry->refCount += 1; entry->targetFlags = targetFlags; entry->xOffset = xOffset; entry->yOffset = yOffset; - entry->timeout = timeout; entry->inProgress = false; entry->headMotionSample = NULL; entry->tailMotionSample = NULL; @@ -2896,7 +2789,7 @@ void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry, void InputDispatcher::EventEntry::recycle() { injectionResult = INPUT_EVENT_INJECTION_PENDING; dispatchInProgress = false; - pendingSyncDispatches = 0; + pendingForegroundDispatches = 0; } @@ -3106,9 +2999,7 @@ void InputDispatcher::InputState::clear() { InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) : status(STATUS_NORMAL), inputChannel(inputChannel), inputPublisher(inputChannel), - nextTimeoutTime(LONG_LONG_MAX), - lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX), - lastANRTime(LONG_LONG_MAX) { + lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX) { } InputDispatcher::Connection::~Connection() { @@ -3118,18 +3009,6 @@ 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; -} - -void InputDispatcher::Connection::resetTimeout(nsecs_t currentTime) { - if (outboundQueue.isEmpty()) { - nextTimeoutTime = LONG_LONG_MAX; - } else { - setNextTimeoutTime(currentTime, outboundQueue.headSentinel.next->timeout); - } -} - const char* InputDispatcher::Connection::getStatusLabel() const { switch (status) { case STATUS_NORMAL: @@ -3138,9 +3017,6 @@ const char* InputDispatcher::Connection::getStatusLabel() const { case STATUS_BROKEN: return "BROKEN"; - case STATUS_NOT_RESPONDING: - return "NOT_RESPONDING"; - case STATUS_ZOMBIE: return "ZOMBIE"; diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java index 90d036a..024aec5 100644 --- a/services/java/com/android/server/InputManager.java +++ b/services/java/com/android/server/InputManager.java @@ -24,18 +24,13 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.os.Environment; -import android.os.LocalPowerManager; -import android.os.PowerManager; import android.os.SystemProperties; import android.util.Slog; import android.util.Xml; import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; -import android.view.KeyEvent; -import android.view.MotionEvent; import android.view.Surface; -import android.view.WindowManagerPolicy; import java.io.BufferedReader; import java.io.File; @@ -83,7 +78,6 @@ public class InputManager { private static native void nativeSetInputWindows(InputWindow[] windows); private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen); private static native void nativeSetFocusedApplication(InputApplication application); - private static native void nativePreemptInputDispatch(); private static native InputDevice nativeGetInputDevice(int deviceId); private static native int[] nativeGetInputDeviceIds(); private static native String nativeDump(); @@ -331,10 +325,6 @@ public class InputManager { nativeSetFocusedApplication(application); } - public void preemptInputDispatch() { - nativePreemptInputDispatch(); - } - public void setInputDispatchMode(boolean enabled, boolean frozen) { nativeSetInputDispatchMode(enabled, frozen); } @@ -395,20 +385,10 @@ public class InputManager { public void notifyInputChannelBroken(InputChannel inputChannel) { mWindowManagerService.mInputMonitor.notifyInputChannelBroken(inputChannel); } - - @SuppressWarnings("unused") - public long notifyInputChannelANR(InputChannel inputChannel) { - return mWindowManagerService.mInputMonitor.notifyInputChannelANR(inputChannel); - } - - @SuppressWarnings("unused") - public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) { - mWindowManagerService.mInputMonitor.notifyInputChannelRecoveredFromANR(inputChannel); - } @SuppressWarnings("unused") - public long notifyANR(Object token) { - return mWindowManagerService.mInputMonitor.notifyANR(token); + public long notifyANR(Object token, InputChannel inputChannel) { + return mWindowManagerService.mInputMonitor.notifyANR(token, inputChannel); } @SuppressWarnings("unused") diff --git a/services/java/com/android/server/InputWindow.java b/services/java/com/android/server/InputWindow.java index dbc59ef..befc770 100644 --- a/services/java/com/android/server/InputWindow.java +++ b/services/java/com/android/server/InputWindow.java @@ -27,6 +27,9 @@ public final class InputWindow { // The input channel associated with the window. public InputChannel inputChannel; + // The window name. + public String name; + // Window layout params attributes. (WindowManager.LayoutParams) public int layoutParamsFlags; public int layoutParamsType; @@ -55,6 +58,9 @@ public final class InputWindow { // Window is visible. public boolean visible; + // Window can receive keys. + public boolean canReceiveKeys; + // Window has focus. public boolean hasFocus; @@ -64,6 +70,9 @@ public final class InputWindow { // Input event dispatching is paused. public boolean paused; + // Window layer. + public int layer; + // Id of process and user that owns the window. public int ownerPid; public int ownerUid; diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index dacdd7d..fb8e888 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -5088,61 +5088,39 @@ public class WindowManagerService extends IWindowManager.Stub } } - /* Notifies the window manager about an input channel that is not responding. + /* Notifies the window manager about an application that is not responding. * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch. * * Called by the InputManager. */ - public long notifyInputChannelANR(InputChannel inputChannel) { - AppWindowToken token; - synchronized (mWindowMap) { - WindowState windowState = getWindowStateForInputChannelLocked(inputChannel); - if (windowState == null) { - return 0; // window is unknown, abort dispatching + public long notifyANR(Object token, InputChannel inputChannel) { + AppWindowToken appWindowToken = null; + if (inputChannel != null) { + synchronized (mWindowMap) { + WindowState windowState = getWindowStateForInputChannelLocked(inputChannel); + if (windowState != null) { + Slog.i(TAG, "Input event dispatching timed out sending to " + + windowState.mAttrs.getTitle()); + appWindowToken = windowState.mAppToken; + } } - - Slog.i(TAG, "Input event dispatching timed out sending to " - + windowState.mAttrs.getTitle()); - token = windowState.mAppToken; } - return notifyANRInternal(token); - } - - /* Notifies the window manager about an input channel spontaneously recovering from ANR - * by successfully delivering the event that originally timed out. - * - * Called by the InputManager. - */ - public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) { - // Nothing to do just now. - // Just wait for the user to dismiss the ANR dialog. - } - - /* Notifies the window manager about an application that is not responding - * in general rather than with respect to a particular input channel. - * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch. - * - * Called by the InputManager. - */ - public long notifyANR(Object token) { - AppWindowToken appWindowToken = (AppWindowToken) token; + if (appWindowToken == null && token != null) { + appWindowToken = (AppWindowToken) token; + Slog.i(TAG, "Input event dispatching timed out sending to application " + + appWindowToken.stringName); + } - Slog.i(TAG, "Input event dispatching timed out sending to application " - + appWindowToken.stringName); - return notifyANRInternal(appWindowToken); - } - - private long notifyANRInternal(AppWindowToken token) { - if (token != null && token.appToken != null) { + if (appWindowToken != null && appWindowToken.appToken != null) { try { // Notify the activity manager about the timeout and let it decide whether // to abort dispatching or keep waiting. - boolean abort = token.appToken.keyDispatchingTimedOut(); + boolean abort = appWindowToken.appToken.keyDispatchingTimedOut(); if (! abort) { // The activity manager declined to abort dispatching. // Wait a bit longer and timeout again later. - return token.inputDispatchingTimeoutNanos; + return appWindowToken.inputDispatchingTimeoutNanos; } } catch (RemoteException ex) { } @@ -5195,13 +5173,16 @@ public class WindowManagerService extends IWindowManager.Stub // Add a window to our list of input windows. final InputWindow inputWindow = mTempInputWindows.add(); inputWindow.inputChannel = child.mInputChannel; + inputWindow.name = child.toString(); inputWindow.layoutParamsFlags = flags; inputWindow.layoutParamsType = type; inputWindow.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos(); inputWindow.visible = isVisible; + inputWindow.canReceiveKeys = child.canReceiveKeys(); inputWindow.hasFocus = hasFocus; inputWindow.hasWallpaper = hasWallpaper; inputWindow.paused = child.mAppToken != null ? child.mAppToken.paused : false; + inputWindow.layer = child.mLayer; inputWindow.ownerPid = child.mSession.mPid; inputWindow.ownerUid = child.mSession.mUid; @@ -5294,23 +5275,6 @@ public class WindowManagerService extends IWindowManager.Stub if (newWindow != mInputFocus) { if (newWindow != null && newWindow.canReceiveKeys()) { - // If the new input focus is an error window or appears above the current - // input focus, preempt any pending synchronous dispatch so that we can - // start delivering events to the new input focus as soon as possible. - if ((newWindow.mAttrs.flags & FLAG_SYSTEM_ERROR) != 0) { - if (DEBUG_INPUT) { - Slog.v(TAG, "New SYSTEM_ERROR window; resetting state"); - } - preemptInputDispatchLw(); - } else if (mInputFocus != null && newWindow.mLayer > mInputFocus.mLayer) { - if (DEBUG_INPUT) { - Slog.v(TAG, "Transferring focus to new window at higher layer: " - + "old win layer=" + mInputFocus.mLayer - + ", new win layer=" + newWindow.mLayer); - } - preemptInputDispatchLw(); - } - // Displaying a window implicitly causes dispatching to be unpaused. // This is to protect against bugs if someone pauses dispatching but // forgets to resume. @@ -5322,14 +5286,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - /* Tells the dispatcher to stop waiting for its current synchronous event targets. - * Essentially, just makes those dispatches asynchronous so a new dispatch cycle - * can begin. - */ - private void preemptInputDispatchLw() { - mInputManager.preemptInputDispatch(); - } - public void setFocusedAppLw(AppWindowToken newApp) { // Focused app has changed. if (newApp == null) { diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 6dd619c..cbdfba9 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -49,8 +49,6 @@ static struct { jmethodID notifyConfigurationChanged; jmethodID notifyLidSwitchChanged; jmethodID notifyInputChannelBroken; - jmethodID notifyInputChannelANR; - jmethodID notifyInputChannelRecoveredFromANR; jmethodID notifyANR; jmethodID virtualKeyDownFeedback; jmethodID interceptKeyBeforeQueueing; @@ -85,6 +83,7 @@ static struct { jclass clazz; jfieldID inputChannel; + jfieldID name; jfieldID layoutParamsFlags; jfieldID layoutParamsType; jfieldID dispatchingTimeoutNanos; @@ -101,9 +100,11 @@ static struct { jfieldID touchableAreaRight; jfieldID touchableAreaBottom; jfieldID visible; + jfieldID canReceiveKeys; jfieldID hasFocus; jfieldID hasWallpaper; jfieldID paused; + jfieldID layer; jfieldID ownerPid; jfieldID ownerUid; } gInputWindowClassInfo; @@ -168,7 +169,6 @@ public: void setInputWindows(JNIEnv* env, jobjectArray windowObjArray); void setFocusedApplication(JNIEnv* env, jobject applicationObj); void setInputDispatchMode(bool enabled, bool frozen); - void preemptInputDispatch(); /* --- InputReaderPolicyInterface implementation --- */ @@ -191,10 +191,9 @@ public: /* --- InputDispatcherPolicyInterface implementation --- */ virtual void notifyConfigurationChanged(nsecs_t when); - virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle); + virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle, + const sp<InputChannel>& inputChannel); virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel); - virtual nsecs_t notifyInputChannelANR(const sp<InputChannel>& inputChannel); - virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel); virtual nsecs_t getKeyRepeatTimeout(); virtual nsecs_t getKeyRepeatDelay(); virtual int32_t getMaxEventsPerSecond(); @@ -702,32 +701,40 @@ void NativeInputManager::notifyConfigurationChanged(nsecs_t when) { checkAndClearExceptionFromCallback(env, "notifyConfigurationChanged"); } -nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle) { +nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle, + const sp<InputChannel>& inputChannel) { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("notifyANR"); #endif JNIEnv* env = jniEnv(); - ApplicationToken* token = static_cast<ApplicationToken*>(inputApplicationHandle.get()); - jweak tokenObjWeak = token->getTokenObj(); - - jlong newTimeout; - jobject tokenObjLocal = env->NewLocalRef(tokenObjWeak); - if (tokenObjLocal) { - newTimeout = env->CallLongMethod(mCallbacksObj, - gCallbacksClassInfo.notifyANR, tokenObjLocal); - if (checkAndClearExceptionFromCallback(env, "notifyANR")) { - newTimeout = 0; // abort dispatch - } else { - assert(newTimeout >= 0); - } + jobject tokenObjLocal; + if (inputApplicationHandle.get()) { + ApplicationToken* token = static_cast<ApplicationToken*>(inputApplicationHandle.get()); + jweak tokenObjWeak = token->getTokenObj(); + tokenObjLocal = env->NewLocalRef(tokenObjWeak); + } else { + tokenObjLocal = NULL; + } - env->DeleteLocalRef(tokenObjLocal); + jobject inputChannelObjLocal; + if (inputChannel.get()) { + inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel); } else { + inputChannelObjLocal = NULL; + } + + jlong newTimeout = env->CallLongMethod(mCallbacksObj, + gCallbacksClassInfo.notifyANR, tokenObjLocal, inputChannelObjLocal); + if (checkAndClearExceptionFromCallback(env, "notifyANR")) { newTimeout = 0; // abort dispatch + } else { + assert(newTimeout >= 0); } + env->DeleteLocalRef(tokenObjLocal); + env->DeleteLocalRef(inputChannelObjLocal); return newTimeout; } @@ -748,51 +755,6 @@ void NativeInputManager::notifyInputChannelBroken(const sp<InputChannel>& inputC } } -nsecs_t NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel) { -#if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("notifyInputChannelANR - inputChannel='%s'", - inputChannel->getName().string()); -#endif - - JNIEnv* env = jniEnv(); - - jlong newTimeout; - jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel); - if (inputChannelObjLocal) { - newTimeout = env->CallLongMethod(mCallbacksObj, - gCallbacksClassInfo.notifyInputChannelANR, inputChannelObjLocal); - if (checkAndClearExceptionFromCallback(env, "notifyInputChannelANR")) { - newTimeout = 0; // abort dispatch - } else { - assert(newTimeout >= 0); - } - - env->DeleteLocalRef(inputChannelObjLocal); - } else { - newTimeout = 0; // abort dispatch - } - - return newTimeout; -} - -void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) { -#if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("notifyInputChannelRecoveredFromANR - inputChannel='%s'", - inputChannel->getName().string()); -#endif - - 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() { if (! isScreenOn()) { // Disable key repeat when the screen is off. @@ -855,6 +817,8 @@ bool NativeInputManager::populateWindow(JNIEnv* env, jobject windowObj, sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); if (inputChannel != NULL) { + jstring name = jstring(env->GetObjectField(windowObj, + gInputWindowClassInfo.name)); jint layoutParamsFlags = env->GetIntField(windowObj, gInputWindowClassInfo.layoutParamsFlags); jint layoutParamsType = env->GetIntField(windowObj, @@ -887,18 +851,25 @@ bool NativeInputManager::populateWindow(JNIEnv* env, jobject windowObj, gInputWindowClassInfo.touchableAreaBottom); jboolean visible = env->GetBooleanField(windowObj, gInputWindowClassInfo.visible); + jboolean canReceiveKeys = env->GetBooleanField(windowObj, + gInputWindowClassInfo.canReceiveKeys); jboolean hasFocus = env->GetBooleanField(windowObj, gInputWindowClassInfo.hasFocus); jboolean hasWallpaper = env->GetBooleanField(windowObj, gInputWindowClassInfo.hasWallpaper); jboolean paused = env->GetBooleanField(windowObj, gInputWindowClassInfo.paused); + jint layer = env->GetIntField(windowObj, + gInputWindowClassInfo.layer); jint ownerPid = env->GetIntField(windowObj, gInputWindowClassInfo.ownerPid); jint ownerUid = env->GetIntField(windowObj, gInputWindowClassInfo.ownerUid); + const char* nameStr = env->GetStringUTFChars(name, NULL); + outWindow.inputChannel = inputChannel; + outWindow.name.setTo(nameStr); outWindow.layoutParamsFlags = layoutParamsFlags; outWindow.layoutParamsType = layoutParamsType; outWindow.dispatchingTimeout = dispatchingTimeoutNanos; @@ -915,11 +886,15 @@ bool NativeInputManager::populateWindow(JNIEnv* env, jobject windowObj, outWindow.touchableAreaRight = touchableAreaRight; outWindow.touchableAreaBottom = touchableAreaBottom; outWindow.visible = visible; + outWindow.canReceiveKeys = canReceiveKeys; outWindow.hasFocus = hasFocus; outWindow.hasWallpaper = hasWallpaper; outWindow.paused = paused; + outWindow.layer = layer; outWindow.ownerPid = ownerPid; outWindow.ownerUid = ownerUid; + + env->ReleaseStringUTFChars(name, nameStr); valid = true; } else { LOGW("Dropping input target because its input channel is not initialized."); @@ -973,10 +948,6 @@ void NativeInputManager::setInputDispatchMode(bool enabled, bool frozen) { mInputManager->getDispatcher()->setInputDispatchMode(enabled, frozen); } -void NativeInputManager::preemptInputDispatch() { - mInputManager->getDispatcher()->preemptInputDispatch(); -} - bool NativeInputManager::interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel, const KeyEvent* keyEvent, uint32_t policyFlags) { bool isScreenOn = this->isScreenOn(); @@ -1246,15 +1217,6 @@ static void android_server_InputManager_nativeSetInputDispatchMode(JNIEnv* env, gNativeInputManager->setInputDispatchMode(enabled, frozen); } -static void android_server_InputManager_nativePreemptInputDispatch(JNIEnv* env, - jclass clazz) { - if (checkInputManagerUnitialized(env)) { - return; - } - - gNativeInputManager->preemptInputDispatch(); -} - static jobject android_server_InputManager_nativeGetInputDevice(JNIEnv* env, jclass clazz, jint deviceId) { if (checkInputManagerUnitialized(env)) { @@ -1357,8 +1319,6 @@ static JNINativeMethod gInputManagerMethods[] = { (void*) android_server_InputManager_nativeSetFocusedApplication }, { "nativeSetInputDispatchMode", "(ZZ)V", (void*) android_server_InputManager_nativeSetInputDispatchMode }, - { "nativePreemptInputDispatch", "()V", - (void*) android_server_InputManager_nativePreemptInputDispatch }, { "nativeGetInputDevice", "(I)Landroid/view/InputDevice;", (void*) android_server_InputManager_nativeGetInputDevice }, { "nativeGetInputDeviceIds", "()[I", @@ -1398,14 +1358,8 @@ int register_android_server_InputManager(JNIEnv* env) { 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.notifyANR, gCallbacksClassInfo.clazz, - "notifyANR", "(Ljava/lang/Object;)J"); + "notifyANR", "(Ljava/lang/Object;Landroid/view/InputChannel;)J"); GET_METHOD_ID(gCallbacksClassInfo.virtualKeyDownFeedback, gCallbacksClassInfo.clazz, "virtualKeyDownFeedback", "()V"); @@ -1477,6 +1431,9 @@ int register_android_server_InputManager(JNIEnv* env) { GET_FIELD_ID(gInputWindowClassInfo.inputChannel, gInputWindowClassInfo.clazz, "inputChannel", "Landroid/view/InputChannel;"); + GET_FIELD_ID(gInputWindowClassInfo.name, gInputWindowClassInfo.clazz, + "name", "Ljava/lang/String;"); + GET_FIELD_ID(gInputWindowClassInfo.layoutParamsFlags, gInputWindowClassInfo.clazz, "layoutParamsFlags", "I"); @@ -1525,6 +1482,9 @@ int register_android_server_InputManager(JNIEnv* env) { GET_FIELD_ID(gInputWindowClassInfo.visible, gInputWindowClassInfo.clazz, "visible", "Z"); + GET_FIELD_ID(gInputWindowClassInfo.canReceiveKeys, gInputWindowClassInfo.clazz, + "canReceiveKeys", "Z"); + GET_FIELD_ID(gInputWindowClassInfo.hasFocus, gInputWindowClassInfo.clazz, "hasFocus", "Z"); @@ -1534,6 +1494,9 @@ int register_android_server_InputManager(JNIEnv* env) { GET_FIELD_ID(gInputWindowClassInfo.paused, gInputWindowClassInfo.clazz, "paused", "Z"); + GET_FIELD_ID(gInputWindowClassInfo.layer, gInputWindowClassInfo.clazz, + "layer", "I"); + GET_FIELD_ID(gInputWindowClassInfo.ownerPid, gInputWindowClassInfo.clazz, "ownerPid", "I"); |