Make input dispatcher only ANR for foreground windows.

Redesigned the input dispatcher's ANR timeout mechanism so it is much
closer to Froyo's policy.  ANR is only ever signalled if the dispatcher
is waiting on a window to finish processing its previous event(s) and
there is new pending input.

In the old code, we tracked the dispatch timeout separately for each
input channel.  This was somewhat complicated and also resulted in the
situation where applications could ANR long after the user had pushed
them into the background.

Change-Id: I666ecada0952d4b95f1d67b9f733842b745c7f4b
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 @@
  */
 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 @@
     // 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 @@
     };
 
     sp<InputChannel> inputChannel;
+    String8 name;
     int32_t layoutParamsFlags;
     int32_t layoutParamsType;
     nsecs_t dispatchingTimeout;
@@ -206,9 +200,11 @@
     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 @@
 
     /* 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 @@
      */
     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 @@
     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 @@
         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 @@
         int32_t targetFlags;
         float xOffset;
         float yOffset;
-        nsecs_t timeout;
 
         // True if dispatch has started.
         bool inProgress;
@@ -540,12 +518,8 @@
         //   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 @@
             dequeue(first);
             return first;
         }
+
+        uint32_t count() const;
     };
 
     /* Allocates queue entries and performs reference counting as needed. */
@@ -647,7 +623,7 @@
                 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 @@
             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 @@
         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 @@
         // 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 @@
             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 @@
     // 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 @@
     // 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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
         *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 @@
         }
 
         entry->dispatchInProgress = true;
-        startFindingTargetsLocked();
+        startFindingTargetsLocked(); // resets mCurrentInputTargetsValid
     }
 
     // Identify targets.
@@ -618,7 +582,7 @@
         logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
 
         entry->dispatchInProgress = true;
-        startFindingTargetsLocked();
+        startFindingTargetsLocked(); // resets mCurrentInputTargetsValid
     }
 
     bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
@@ -728,7 +692,7 @@
     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 @@
     } 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 @@
     }
 
     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 @@
     }
 }
 
-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 @@
         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 @@
         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 @@
         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 @@
             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 @@
             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 @@
             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 @@
         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 @@
             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 @@
     }
 
 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 @@
     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 @@
         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 @@
     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 @@
         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 @@
 
                 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 @@
     // 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 @@
     // 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 @@
     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 @@
         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 @@
                 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 @@
     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 @@
             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::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 @@
             // 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 @@
 
             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::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 @@
     { // 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 @@
             mTouchedWallpaperWindows.clear();
         }
 
-        bool hadFocusedWindow = mFocusedWindow != NULL;
-
         mFocusedWindow = NULL;
         mWallpaperWindows.clear();
 
@@ -2283,9 +2204,36 @@
 
         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::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 @@
                 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 @@
     { // 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 @@
     { // 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 @@
     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::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 @@
     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::doNotifyInputChannelANRLockedInterruptible(
+void InputDispatcher::doNotifyANRLockedInterruptible(
         CommandEntry* commandEntry) {
-    sp<Connection> connection = commandEntry->connection;
+    mLock.unlock();
 
-    if (connection->status != Connection::STATUS_ZOMBIE) {
-        mLock.unlock();
+    nsecs_t newTimeout = mPolicy->notifyANR(
+            commandEntry->inputApplicationHandle, commandEntry->inputChannel);
 
-        nsecs_t newTimeout = mPolicy->notifyInputChannelANR(connection->inputChannel);
+    mLock.lock();
 
-        mLock.lock();
-
-        nsecs_t currentTime = now();
-        resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout);
-    }
-}
-
-void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible(
-        CommandEntry* commandEntry) {
-    sp<Connection> connection = commandEntry->connection;
-
-    if (connection->status != Connection::STATUS_ZOMBIE) {
-        mLock.unlock();
-
-        mPolicy->notifyInputChannelRecoveredFromANR(connection->inputChannel);
-
-        mLock.lock();
-    }
+    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel);
 }
 
 void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
@@ -2695,22 +2590,9 @@
     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 @@
 }
 
 
+// --- 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 @@
     entry->injectionIsAsync = false;
     entry->injectorPid = -1;
     entry->injectorUid = -1;
-    entry->pendingSyncDispatches = 0;
+    entry->pendingForegroundDispatches = 0;
 }
 
 InputDispatcher::ConfigurationChangedEntry*
@@ -2797,14 +2691,13 @@
 
 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::EventEntry::recycle() {
     injectionResult = INPUT_EVENT_INJECTION_PENDING;
     dispatchInProgress = false;
-    pendingSyncDispatches = 0;
+    pendingForegroundDispatches = 0;
 }
 
 
@@ -3106,9 +2999,7 @@
 
 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 @@
     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 @@
     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.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 @@
     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 @@
         nativeSetFocusedApplication(application);
     }
     
-    public void preemptInputDispatch() {
-        nativePreemptInputDispatch();
-    }
-    
     public void setInputDispatchMode(boolean enabled, boolean frozen) {
         nativeSetInputDispatchMode(enabled, frozen);
     }
@@ -395,20 +385,10 @@
         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 @@
     // 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 @@
     // Window is visible.
     public boolean visible;
     
+    // Window can receive keys.
+    public boolean canReceiveKeys;
+    
     // Window has focus.
     public boolean hasFocus;
     
@@ -64,6 +70,9 @@
     // 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 27d875e..9835098 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -5096,61 +5096,39 @@
             }
         }
         
-        /* 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) {
                 }
@@ -5203,13 +5181,16 @@
                 // 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;
                 
@@ -5302,23 +5283,6 @@
 
             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.
@@ -5330,14 +5294,6 @@
             }
         }
         
-        /* 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 @@
     jmethodID notifyConfigurationChanged;
     jmethodID notifyLidSwitchChanged;
     jmethodID notifyInputChannelBroken;
-    jmethodID notifyInputChannelANR;
-    jmethodID notifyInputChannelRecoveredFromANR;
     jmethodID notifyANR;
     jmethodID virtualKeyDownFeedback;
     jmethodID interceptKeyBeforeQueueing;
@@ -85,6 +83,7 @@
     jclass clazz;
 
     jfieldID inputChannel;
+    jfieldID name;
     jfieldID layoutParamsFlags;
     jfieldID layoutParamsType;
     jfieldID dispatchingTimeoutNanos;
@@ -101,9 +100,11 @@
     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 @@
     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 @@
     /* --- 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 @@
     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);
-        }
-
-        env->DeleteLocalRef(tokenObjLocal);
+    jobject tokenObjLocal;
+    if (inputApplicationHandle.get()) {
+        ApplicationToken* token = static_cast<ApplicationToken*>(inputApplicationHandle.get());
+        jweak tokenObjWeak = token->getTokenObj();
+        tokenObjLocal = env->NewLocalRef(tokenObjWeak);
     } else {
-        newTimeout = 0; // abort dispatch
+        tokenObjLocal = NULL;
     }
 
+    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 @@
     }
 }
 
-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 @@
         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 @@
                     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 @@
             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 @@
     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 @@
     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 @@
             (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 @@
     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 @@
     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 @@
     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 @@
     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");