OSDN Git Service

Input dispatcher ANR handling enhancements.
authorJeff Brown <jeffbrown@google.com>
Wed, 8 Sep 2010 18:49:43 +0000 (11:49 -0700)
committerJeff Brown <jeffbrown@google.com>
Sun, 12 Sep 2010 23:52:03 +0000 (16:52 -0700)
This change is essentially a rewrite of the main input dispatcher loop
with the target identification folded in.  Since the input dispatcher now
has all of the window state, it can make better decisions about
when to ANR.

Added a .5 second deadline for processing app switch keys.  This behavior
predates Gingerbread but had not previously been ported.

Fixed some timing inaccuracies in the ANR accounting that could cause
applications to ANR sooner than they should have.

Added a mechanism for tracking key and motion events that have been
dispatched to a window so that appropriate cancelation events can be
synthesized when recovering from ANR.  This change helps to keep
applications in sync so they don't end up with stuck buttons upon
recovery from ANRs.

Added more comments to describe the tricky parts of PollLoop.

Change-Id: I13dffca27acb436fc383980db536abc4d8b9e6f1

include/ui/InputDispatcher.h
include/ui/InputManager.h
include/ui/InputReader.h
include/ui/PowerManager.h [new file with mode: 0644]
include/utils/PollLoop.h
libs/ui/InputDispatcher.cpp
libs/ui/InputManager.cpp
libs/ui/InputReader.cpp
libs/utils/PollLoop.cpp

index f00f2db..a06208a 100644 (file)
@@ -30,6 +30,7 @@
 
 #include <stddef.h>
 #include <unistd.h>
+#include <limits.h>
 
 
 namespace android {
@@ -108,9 +109,12 @@ struct InputTarget {
     // Flags for the input target.
     int32_t flags;
 
-    // The timeout for event delivery to this target in nanoseconds.  Or -1 if none.
+    // 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;
@@ -118,6 +122,122 @@ struct InputTarget {
 
 
 /*
+ * An input window describes the bounds of a window that can receive input.
+ */
+struct InputWindow {
+    // Window flags from WindowManager.LayoutParams
+    enum {
+        FLAG_ALLOW_LOCK_WHILE_SCREEN_ON     = 0x00000001,
+        FLAG_DIM_BEHIND        = 0x00000002,
+        FLAG_BLUR_BEHIND        = 0x00000004,
+        FLAG_NOT_FOCUSABLE      = 0x00000008,
+        FLAG_NOT_TOUCHABLE      = 0x00000010,
+        FLAG_NOT_TOUCH_MODAL    = 0x00000020,
+        FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040,
+        FLAG_KEEP_SCREEN_ON     = 0x00000080,
+        FLAG_LAYOUT_IN_SCREEN   = 0x00000100,
+        FLAG_LAYOUT_NO_LIMITS   = 0x00000200,
+        FLAG_FULLSCREEN      = 0x00000400,
+        FLAG_FORCE_NOT_FULLSCREEN   = 0x00000800,
+        FLAG_DITHER             = 0x00001000,
+        FLAG_SECURE             = 0x00002000,
+        FLAG_SCALED             = 0x00004000,
+        FLAG_IGNORE_CHEEK_PRESSES    = 0x00008000,
+        FLAG_LAYOUT_INSET_DECOR = 0x00010000,
+        FLAG_ALT_FOCUSABLE_IM = 0x00020000,
+        FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000,
+        FLAG_SHOW_WHEN_LOCKED = 0x00080000,
+        FLAG_SHOW_WALLPAPER = 0x00100000,
+        FLAG_TURN_SCREEN_ON = 0x00200000,
+        FLAG_DISMISS_KEYGUARD = 0x00400000,
+        FLAG_IMMERSIVE = 0x00800000,
+        FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000,
+        FLAG_COMPATIBLE_WINDOW = 0x20000000,
+        FLAG_SYSTEM_ERROR = 0x40000000,
+    };
+
+    // Window types from WindowManager.LayoutParams
+    enum {
+        FIRST_APPLICATION_WINDOW = 1,
+        TYPE_BASE_APPLICATION   = 1,
+        TYPE_APPLICATION        = 2,
+        TYPE_APPLICATION_STARTING = 3,
+        LAST_APPLICATION_WINDOW = 99,
+        FIRST_SUB_WINDOW        = 1000,
+        TYPE_APPLICATION_PANEL  = FIRST_SUB_WINDOW,
+        TYPE_APPLICATION_MEDIA  = FIRST_SUB_WINDOW+1,
+        TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2,
+        TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3,
+        TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW+4,
+        LAST_SUB_WINDOW         = 1999,
+        FIRST_SYSTEM_WINDOW     = 2000,
+        TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW,
+        TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1,
+        TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2,
+        TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3,
+        TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4,
+        TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5,
+        TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6,
+        TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7,
+        TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8,
+        TYPE_KEYGUARD_DIALOG    = FIRST_SYSTEM_WINDOW+9,
+        TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10,
+        TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11,
+        TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12,
+        TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13,
+        TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14,
+        LAST_SYSTEM_WINDOW      = 2999,
+    };
+
+    sp<InputChannel> inputChannel;
+    int32_t layoutParamsFlags;
+    int32_t layoutParamsType;
+    nsecs_t dispatchingTimeout;
+    int32_t frameLeft;
+    int32_t frameTop;
+    int32_t frameRight;
+    int32_t frameBottom;
+    int32_t visibleFrameLeft;
+    int32_t visibleFrameTop;
+    int32_t visibleFrameRight;
+    int32_t visibleFrameBottom;
+    int32_t touchableAreaLeft;
+    int32_t touchableAreaTop;
+    int32_t touchableAreaRight;
+    int32_t touchableAreaBottom;
+    bool visible;
+    bool hasFocus;
+    bool hasWallpaper;
+    bool paused;
+    int32_t ownerPid;
+    int32_t ownerUid;
+
+    bool visibleFrameIntersects(const InputWindow* other) const;
+    bool touchableAreaContainsPoint(int32_t x, int32_t y) const;
+};
+
+
+/*
+ * A private handle type used by the input manager to track the window.
+ */
+class InputApplicationHandle : public RefBase {
+protected:
+    InputApplicationHandle() { }
+    virtual ~InputApplicationHandle() { }
+};
+
+
+/*
+ * An input application describes properties of an application that can receive input.
+ */
+struct InputApplication {
+    String8 name;
+    nsecs_t dispatchingTimeout;
+    sp<InputApplicationHandle> handle;
+};
+
+
+/*
  * Input dispatcher policy interface.
  *
  * The input reader policy is used by the input reader to interact with the Window Manager
@@ -135,14 +255,16 @@ public:
     /* Notifies the system that a configuration change has occurred. */
     virtual void notifyConfigurationChanged(nsecs_t when) = 0;
 
+    /* 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;
+
     /* 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 true and a new timeout value if the dispatcher should keep waiting.
-     * Otherwise returns false. */
-    virtual bool notifyInputChannelANR(const sp<InputChannel>& inputChannel,
-            nsecs_t& outNewTimeout) = 0;
+     * 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;
@@ -153,29 +275,27 @@ public:
     /* Gets the key repeat inter-key delay. */
     virtual nsecs_t getKeyRepeatDelay() = 0;
 
-    /* Waits for key event input targets to become available.
-     * If the event is being injected, injectorPid and injectorUid should specify the
-     * process id and used id of the injecting application, otherwise they should both
-     * be -1.
-     * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
-    virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
-            int32_t injectorPid, int32_t injectorUid,
-            Vector<InputTarget>& outTargets) = 0;
-
-    /* Waits for motion event targets to become available.
-     * If the event is being injected, injectorPid and injectorUid should specify the
-     * process id and used id of the injecting application, otherwise they should both
-     * be -1.
-     * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
-    virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
-            int32_t injectorPid, int32_t injectorUid,
-            Vector<InputTarget>& outTargets) = 0;
-
     /* Gets the maximum suggested event delivery rate per second.
      * This value is used to throttle motion event movement actions on a per-device
      * basis.  It is not intended to be a hard limit.
      */
     virtual int32_t getMaxEventsPerSecond() = 0;
+
+    /* Allows the policy a chance to intercept a key before dispatching. */
+    virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
+            const KeyEvent* keyEvent, uint32_t policyFlags) = 0;
+
+    /* Poke user activity for an event dispatched to a window. */
+    virtual void pokeUserActivity(nsecs_t eventTime, int32_t windowType, int32_t eventType) = 0;
+
+    /* Checks whether a given application pid/uid has permission to inject input events
+     * into other applications.
+     *
+     * This method is special in that its implementation promises to be non-reentrant and
+     * is safe to call while holding other locks.  (Most other methods make no such guarantees!)
+     */
+    virtual bool checkInjectEventsPermissionNonReentrant(
+            int32_t injectorPid, int32_t injectorUid) = 0;
 };
 
 
@@ -187,6 +307,11 @@ protected:
     virtual ~InputDispatcherInterface() { }
 
 public:
+    /* Dumps the state of the input dispatcher.
+     *
+     * This method may be called on any thread (usually by the input manager). */
+    virtual void dump(String8& dump) = 0;
+
     /* Runs a single iteration of the dispatch loop.
      * Nominally processes one queued event, a timeout, or a response from an input consumer.
      *
@@ -199,7 +324,6 @@ public:
      * These methods should only be called on the input reader thread.
      */
     virtual void notifyConfigurationChanged(nsecs_t eventTime) = 0;
-    virtual void notifyAppSwitchComing(nsecs_t eventTime) = 0;
     virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source,
             uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
             int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0;
@@ -219,6 +343,24 @@ public:
     virtual int32_t injectInputEvent(const InputEvent* event,
             int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0;
 
+    /* Sets the list of input windows.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual void setInputWindows(const Vector<InputWindow>& inputWindows) = 0;
+
+    /* Sets the focused application.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual void setFocusedApplication(const InputApplication* inputApplication) = 0;
+
+    /* Sets the input dispatching mode.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    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
@@ -230,10 +372,11 @@ public:
     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.
      *
      * These methods may be called on any thread (usually by the input manager).
      */
-    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0;
+    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) = 0;
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
 };
 
@@ -261,10 +404,11 @@ protected:
 public:
     explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
 
+    virtual void dump(String8& dump);
+
     virtual void dispatchOnce();
 
     virtual void notifyConfigurationChanged(nsecs_t eventTime);
-    virtual void notifyAppSwitchComing(nsecs_t eventTime);
     virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source,
             uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
             int32_t scanCode, int32_t metaState, nsecs_t downTime);
@@ -277,9 +421,12 @@ public:
     virtual int32_t injectInputEvent(const InputEvent* event,
             int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis);
 
+    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);
+    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor);
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
 
 private:
@@ -310,6 +457,8 @@ private:
         int32_t pendingSyncDispatches; // the number of synchronous dispatches in progress
 
         inline bool isInjected() { return injectorPid >= 0; }
+
+        void recycle();
     };
 
     struct ConfigurationChangedEntry : EventEntry {
@@ -326,6 +475,17 @@ private:
         int32_t metaState;
         int32_t repeatCount;
         nsecs_t downTime;
+
+        bool syntheticRepeat; // set to true for synthetic key repeats
+
+        enum InterceptKeyResult {
+            INTERCEPT_KEY_RESULT_UNKNOWN,
+            INTERCEPT_KEY_RESULT_SKIP,
+            INTERCEPT_KEY_RESULT_CONTINUE,
+        };
+        InterceptKeyResult interceptKeyResult; // set based on the interception result
+
+        void recycle();
     };
 
     struct MotionSample {
@@ -380,9 +540,13 @@ private:
         //   will be set to NULL.
         MotionSample* tailMotionSample;
 
-        inline bool isSyncTarget() {
+        inline bool isSyncTarget() const {
             return targetFlags & InputTarget::FLAG_SYNC;
         }
+
+        inline void preemptSyncTarget() {
+            targetFlags &= ~ InputTarget::FLAG_SYNC;
+        }
     };
 
     // A command entry captures state and behavior for an action to be performed in the
@@ -413,37 +577,43 @@ private:
 
         // parameters for the command (usage varies by command)
         sp<Connection> connection;
+        nsecs_t eventTime;
+        KeyEntry* keyEntry;
+        sp<InputChannel> inputChannel;
+        sp<InputApplicationHandle> inputApplicationHandle;
+        int32_t windowType;
+        int32_t userActivityEventType;
     };
 
     // Generic queue implementation.
     template <typename T>
     struct Queue {
-        T head;
-        T tail;
+        T headSentinel;
+        T tailSentinel;
 
         inline Queue() {
-            head.prev = NULL;
-            head.next = & tail;
-            tail.prev = & head;
-            tail.next = NULL;
+            headSentinel.prev = NULL;
+            headSentinel.next = & tailSentinel;
+            tailSentinel.prev = & headSentinel;
+            tailSentinel.next = NULL;
         }
 
-        inline bool isEmpty() {
-            return head.next == & tail;
+        inline bool isEmpty() const {
+            return headSentinel.next == & tailSentinel;
         }
 
         inline void enqueueAtTail(T* entry) {
-            T* last = tail.prev;
+            T* last = tailSentinel.prev;
             last->next = entry;
             entry->prev = last;
-            entry->next = & tail;
-            tail.prev = entry;
+            entry->next = & tailSentinel;
+            tailSentinel.prev = entry;
         }
 
         inline void enqueueAtHead(T* entry) {
-            T* first = head.next;
-            head.next = entry;
-            entry->prev = & head;
+            T* first = headSentinel.next;
+            headSentinel.next = entry;
+            entry->prev = & headSentinel;
             entry->next = first;
             first->prev = entry;
         }
@@ -454,7 +624,7 @@ private:
         }
 
         inline T* dequeueAtHead() {
-            T* first = head.next;
+            T* first = headSentinel.next;
             dequeue(first);
             return first;
         }
@@ -476,7 +646,8 @@ private:
                 float xPrecision, float yPrecision,
                 nsecs_t downTime, uint32_t pointerCount,
                 const int32_t* pointerIds, const PointerCoords* pointerCoords);
-        DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry);
+        DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry,
+                int32_t targetFlags, float xOffset, float yOffset, nsecs_t timeout);
         CommandEntry* obtainCommandEntry(Command command);
 
         void releaseEventEntry(EventEntry* entry);
@@ -500,6 +671,85 @@ private:
         void initializeEventEntry(EventEntry* entry, int32_t type, nsecs_t eventTime);
     };
 
+    /* Tracks dispatched key and motion event state so that cancelation events can be
+     * synthesized when events are dropped. */
+    class InputState {
+    public:
+        // Specifies whether a given event will violate input state consistency.
+        enum Consistency {
+            // The event is consistent with the current input state.
+            CONSISTENT,
+            // The event is inconsistent with the current input state but applications
+            // will tolerate it.  eg. Down followed by another down.
+            TOLERABLE,
+            // The event is inconsistent with the current input state and will probably
+            // cause applications to crash.  eg. Up without prior down, move with
+            // unexpected number of pointers.
+            BROKEN
+        };
+
+        InputState();
+        ~InputState();
+
+        // Returns true if there is no state to be canceled.
+        bool isNeutral() const;
+
+        // Returns true if the input state believes it is out of sync.
+        bool isOutOfSync() const;
+
+        // Sets the input state to be out of sync if it is not neutral.
+        void setOutOfSync();
+
+        // Resets the input state out of sync flag.
+        void resetOutOfSync();
+
+        // Records tracking information for an event that has just been published.
+        // Returns whether the event is consistent with the current input state.
+        Consistency trackEvent(const EventEntry* entry);
+
+        // Records tracking information for a key event that has just been published.
+        // Returns whether the event is consistent with the current input state.
+        Consistency trackKey(const KeyEntry* entry);
+
+        // Records tracking information for a motion event that has just been published.
+        // Returns whether the event is consistent with the current input state.
+        Consistency trackMotion(const MotionEntry* entry);
+
+        // Synthesizes cancelation events for the current state.
+        void synthesizeCancelationEvents(Allocator* allocator,
+                Vector<EventEntry*>& outEvents) const;
+
+        // Clears the current state.
+        void clear();
+
+    private:
+        bool mIsOutOfSync;
+
+        struct KeyMemento {
+            int32_t deviceId;
+            int32_t source;
+            int32_t keyCode;
+            int32_t scanCode;
+            nsecs_t downTime;
+        };
+
+        struct MotionMemento {
+            int32_t deviceId;
+            int32_t source;
+            float xPrecision;
+            float yPrecision;
+            nsecs_t downTime;
+            uint32_t pointerCount;
+            int32_t pointerIds[MAX_POINTERS];
+            PointerCoords pointerCoords[MAX_POINTERS];
+
+            void setPointers(const MotionEntry* entry);
+        };
+
+        Vector<KeyMemento> mKeyMementos;
+        Vector<MotionMemento> mMotionMementos;
+    };
+
     /* Manages the dispatch state associated with a single input channel. */
     class Connection : public RefBase {
     protected:
@@ -520,6 +770,7 @@ private:
         Status status;
         sp<InputChannel> inputChannel;
         InputPublisher inputPublisher;
+        InputState inputState;
         Queue<DispatchEntry> outboundQueue;
         nsecs_t nextTimeoutTime; // next timeout time (LONG_LONG_MAX if none)
 
@@ -540,28 +791,34 @@ private:
         // 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() {
-            return ! outboundQueue.isEmpty() && outboundQueue.tail.prev->isSyncTarget();
+        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) {
+        inline double getEventLatencyMillis(nsecs_t currentTime) const {
             return (currentTime - lastEventTime) / 1000000.0;
         }
 
         // Gets the time since the current event entered the outbound dispatch queue.
-        inline double getDispatchLatencyMillis(nsecs_t currentTime) {
+        inline double getDispatchLatencyMillis(nsecs_t currentTime) const {
             return (currentTime - lastDispatchTime) / 1000000.0;
         }
 
         // Gets the time since the current event ANR was declared, if applicable.
-        inline double getANRLatencyMillis(nsecs_t currentTime) {
+        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;
@@ -571,9 +828,26 @@ private:
     Allocator mAllocator;
     sp<PollLoop> mPollLoop;
 
+    EventEntry* mPendingEvent;
     Queue<EventEntry> mInboundQueue;
     Queue<CommandEntry> mCommandQueue;
 
+    Vector<EventEntry*> mTempCancelationEvents;
+
+    void dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, nsecs_t keyRepeatDelay,
+            nsecs_t* nextWakeupTime);
+
+    // Enqueues an inbound event.  Returns true if mPollLoop->wake() should be called.
+    bool enqueueInboundEventLocked(EventEntry* entry);
+
+    // App switch latency optimization.
+    nsecs_t mAppSwitchDueTime;
+
+    static bool isAppSwitchKey(int32_t keyCode);
+    bool isAppSwitchPendingLocked();
+    bool detectPendingAppSwitchLocked(KeyEntry* inboundKeyEntry);
+    void resetPendingAppSwitchLocked(bool handled);
+
     // All registered connections mapped by receive pipe file descriptor.
     KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd;
 
@@ -591,20 +865,15 @@ private:
     // the duration.
     Vector<Connection*> mTimedOutConnections;
 
-    // Preallocated key and motion event objects used only to ask the input dispatcher policy
-    // for the targets of an event that is to be dispatched.
-    KeyEvent mReusableKeyEvent;
-    MotionEvent mReusableMotionEvent;
+    // Input channels that will receive a copy of all input events.
+    Vector<sp<InputChannel> > mMonitoringChannels;
 
-    // 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.
-    Vector<InputTarget> mCurrentInputTargets;
-    bool mCurrentInputTargetsValid; // false while targets are being recomputed
+    // Preallocated key event object used for policy inquiries.
+    KeyEvent mReusableKeyEvent;
 
     // Event injection and synchronization.
     Condition mInjectionResultAvailableCondition;
-    EventEntry* createEntryFromInputEventLocked(const InputEvent* event);
+    EventEntry* createEntryFromInjectedInputEventLocked(const InputEvent* event);
     void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
 
     Condition mInjectionSyncFinishedCondition;
@@ -622,36 +891,108 @@ private:
     } mThrottleState;
 
     // Key repeat tracking.
-    // XXX Move this up to the input reader instead.
     struct KeyRepeatState {
         KeyEntry* lastKeyEntry; // or null if no repeat
         nsecs_t nextRepeatTime;
     } mKeyRepeatState;
 
     void resetKeyRepeatLocked();
+    KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime, nsecs_t keyRepeatTimeout);
 
     // Deferred command processing.
     bool runCommandsLockedInterruptible();
     CommandEntry* postCommandLocked(Command command);
 
-    // Process events that have just been dequeued from the head of the input queue.
-    void processConfigurationChangedLockedInterruptible(
+    // Inbound event processing.
+    void drainInboundQueueLocked();
+    void releasePendingEventLocked(bool wasDropped);
+    void releaseInboundEventLocked(EventEntry* entry, bool wasDropped);
+    bool isEventFromReliableSourceLocked(EventEntry* entry);
+
+    // Dispatch state.
+    bool mDispatchEnabled;
+    bool mDispatchFrozen;
+    Vector<InputWindow> mWindows;
+    Vector<InputWindow*> mWallpaperWindows;
+
+    // Focus tracking for keys, trackball, etc.
+    InputWindow* mFocusedWindow;
+
+    // Focus tracking for touch.
+    bool mTouchDown;
+    InputWindow* mTouchedWindow;                   // primary target for current down
+    bool mTouchedWindowIsObscured;                 // true if other windows may obscure the target
+    Vector<InputWindow*> mTouchedWallpaperWindows; // wallpaper targets
+    struct OutsideTarget {
+        InputWindow* window;
+        bool obscured;
+    };
+    Vector<OutsideTarget> mTempTouchedOutsideTargets; // temporary outside touch targets
+    Vector<sp<InputChannel> > mTempTouchedWallpaperChannels; // temporary wallpaper targets
+
+    // Focused application.
+    InputApplication* mFocusedApplication;
+    InputApplication mFocusedApplicationStorage; // preallocated storage for mFocusedApplication
+    void releaseFocusedApplicationLocked();
+
+    // Dispatch inbound events.
+    bool dispatchConfigurationChangedLocked(
             nsecs_t currentTime, ConfigurationChangedEntry* entry);
-    void processKeyLockedInterruptible(
-            nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout);
-    void processKeyRepeatLockedInterruptible(
-            nsecs_t currentTime, nsecs_t keyRepeatTimeout);
-    void processMotionLockedInterruptible(
-            nsecs_t currentTime, MotionEntry* entry);
-
-    // Identify input targets for an event and dispatch to them.
-    void identifyInputTargetsAndDispatchKeyLockedInterruptible(
-            nsecs_t currentTime, KeyEntry* entry);
-    void identifyInputTargetsAndDispatchMotionLockedInterruptible(
-            nsecs_t currentTime, MotionEntry* entry);
+    bool dispatchKeyLocked(
+            nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,
+            nsecs_t* nextWakeupTime);
+    bool dispatchMotionLocked(
+            nsecs_t currentTime, MotionEntry* entry,
+            nsecs_t* nextWakeupTime);
     void dispatchEventToCurrentInputTargetsLocked(
             nsecs_t currentTime, EventEntry* entry, bool resumeWithAppendedMotionSample);
 
+    void logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry);
+    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;
+    sp<InputChannel> mCurrentInputChannel;
+
+    enum InputTargetWaitCause {
+        INPUT_TARGET_WAIT_CAUSE_NONE,
+        INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY,
+        INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY,
+    };
+
+    InputTargetWaitCause mInputTargetWaitCause;
+    nsecs_t mInputTargetWaitStartTime;
+    nsecs_t mInputTargetWaitTimeoutTime;
+    bool mInputTargetWaitTimeoutExpired;
+
+    // Finding targets for input events.
+    void startFindingTargetsLocked();
+    void finishFindingTargetsLocked(const InputWindow* window);
+    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 resetANRTimeoutsLocked();
+
+    int32_t findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry,
+            nsecs_t* nextWakeupTime, InputWindow** outWindow);
+    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 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);
+    void releaseTouchedWindowLocked();
+
     // Manage the dispatch cycle for a single connection.
     // These methods are deliberately not Interruptible because doing all of the work
     // with the mutex held makes it easier to ensure that connection invariants are maintained.
@@ -659,15 +1000,25 @@ 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);
+    void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
+            nsecs_t timeSpentWaitingForApplication);
     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);
     static bool handleReceiveCallback(int receiveFd, int events, void* data);
 
+    // Preempting input dispatch.
+    bool preemptInputDispatchInnerLocked();
+
+    // Dump state.
+    void dumpDispatchStateLocked(String8& dump);
+    void logDispatchStateLocked();
+
     // Add or remove a connection to the mActiveConnections vector.
     void activateConnectionLocked(Connection* connection);
     void deactivateConnectionLocked(Connection* connection);
@@ -683,9 +1034,13 @@ private:
             nsecs_t currentTime, const sp<Connection>& connection);
 
     // Outbound policy interactions.
+    void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry);
     void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry);
     void doNotifyInputChannelANRLockedInterruptible(CommandEntry* commandEntry);
     void doNotifyInputChannelRecoveredFromANRLockedInterruptible(CommandEntry* commandEntry);
+    void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry);
+    void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry);
+    void doTargetsNotReadyTimeoutLockedInterruptible(CommandEntry* commandEntry);
 };
 
 /* Enqueues and dispatches input events, endlessly. */
@@ -702,4 +1057,4 @@ private:
 
 } // namespace android
 
-#endif // _UI_INPUT_DISPATCHER_PRIV_H
+#endif // _UI_INPUT_DISPATCHER_H
index 4012c69..568568b 100644 (file)
@@ -72,51 +72,11 @@ public:
     /* Stops the input manager threads and waits for them to exit. */
     virtual status_t stop() = 0;
 
-    /* Registers an input channel prior to using it as the target of an event. */
-    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0;
-
-    /* Unregisters an input channel. */
-    virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
-
-    /* Injects an input event and optionally waits for sync.
-     * The synchronization mode determines whether the method blocks while waiting for
-     * input injection to proceed.
-     * Returns one of the INPUT_EVENT_INJECTION_XXX constants.
-     */
-    virtual int32_t injectInputEvent(const InputEvent* event,
-            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 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.
-     */
-    virtual void preemptInputDispatch() = 0;
-
-    /* Gets input device configuration. */
-    virtual void getInputConfiguration(InputConfiguration* outConfiguration) = 0;
-
-    /* Gets information about the specified input device.
-     * Returns OK if the device information was obtained or NAME_NOT_FOUND if there
-     * was no such device.
-     */
-    virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) = 0;
-
-    /* Gets the list of all registered device ids. */
-    virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds) = 0;
-
-    /* Queries current input state. */
-    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
-            int32_t scanCode) = 0;
-    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
-            int32_t keyCode) = 0;
-    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
-            int32_t sw) = 0;
-
-    /* Determines whether physical keys exist for the given framework-domain key codes. */
-    virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
-            size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0;
+    /* Gets the input reader. */
+    virtual sp<InputReaderInterface> getReader() = 0;
+
+    /* Gets the input dispatcher. */
+    virtual sp<InputDispatcherInterface> getDispatcher() = 0;
 };
 
 class InputManager : public InputManagerInterface {
@@ -137,25 +97,8 @@ public:
     virtual status_t start();
     virtual status_t stop();
 
-    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
-    virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
-
-    virtual int32_t injectInputEvent(const InputEvent* event,
-            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis);
-
-    virtual void preemptInputDispatch();
-
-    virtual void getInputConfiguration(InputConfiguration* outConfiguration);
-    virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo);
-    virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds);
-    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
-            int32_t scanCode);
-    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
-            int32_t keyCode);
-    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
-            int32_t sw);
-    virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
-            size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags);
+    virtual sp<InputReaderInterface> getReader();
+    virtual sp<InputDispatcherInterface> getDispatcher();
 
 private:
     sp<InputReaderInterface> mReader;
index 7a089a4..903c3c4 100644 (file)
@@ -95,10 +95,6 @@ public:
 
         // The input dispatcher should dispatch the input to the application.
         ACTION_DISPATCH = 0x00000001,
-
-        // The input dispatcher should perform special filtering in preparation for
-        // a pending app switch.
-        ACTION_APP_SWITCH_COMING = 0x00000002,
     };
 
     /* Gets information about the display with the specified id.
@@ -168,6 +164,11 @@ protected:
     virtual ~InputReaderInterface() { }
 
 public:
+    /* Dumps the state of the input reader.
+     *
+     * This method may be called on any thread (usually by the input manager). */
+    virtual void dump(String8& dump) = 0;
+
     /* Runs a single iteration of the processing loop.
      * Nominally reads and processes one incoming message from the EventHub.
      *
@@ -240,6 +241,8 @@ public:
             const sp<InputDispatcherInterface>& dispatcher);
     virtual ~InputReader();
 
+    virtual void dump(String8& dump);
+
     virtual void loopOnce();
 
     virtual void getInputConfiguration(InputConfiguration* outConfiguration);
@@ -305,6 +308,9 @@ private:
             GetStateFunc getStateFunc);
     bool markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
             const int32_t* keyCodes, uint8_t* outFlags);
+
+    // dump state
+    void dumpDeviceInfo(String8& dump);
 };
 
 
@@ -759,9 +765,11 @@ protected:
     } mLocked;
 
     virtual void configureParameters();
+    virtual void logParameters();
     virtual void configureRawAxes();
     virtual void logRawAxes();
     virtual bool configureSurfaceLocked();
+    virtual void logMotionRangesLocked();
     virtual void configureVirtualKeysLocked();
     virtual void parseCalibration();
     virtual void resolveCalibration();
diff --git a/include/ui/PowerManager.h b/include/ui/PowerManager.h
new file mode 100644 (file)
index 0000000..5434b4f
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_POWER_MANAGER_H
+#define _UI_POWER_MANAGER_H
+
+
+namespace android {
+
+enum {
+    POWER_MANAGER_OTHER_EVENT = 0,
+    POWER_MANAGER_CHEEK_EVENT = 1,
+    POWER_MANAGER_TOUCH_EVENT = 2, // touch events are TOUCH for 300ms, and then either
+                                   // up events or LONG_TOUCH events.
+    POWER_MANAGER_LONG_TOUCH_EVENT = 3,
+    POWER_MANAGER_TOUCH_UP_EVENT = 4,
+    POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events.
+
+    POWER_MANAGER_LAST_EVENT = POWER_MANAGER_BUTTON_EVENT, // Last valid event code.
+};
+
+} // namespace android
+
+#endif // _UI_POWER_MANAGER_H
index bc616eb..c2dfe5d 100644 (file)
@@ -172,22 +172,36 @@ private:
         void* data;
     };
     
-    const bool mAllowNonCallbacks;
-    
+    const bool mAllowNonCallbacks; // immutable
+
+    int mWakeReadPipeFd;  // immutable
+    int mWakeWritePipeFd; // immutable
+
+    // The lock guards state used to track whether there is a poll() in progress and whether
+    // there are any other threads waiting in wakeAndLock().  The condition variables
+    // are used to transfer control among these threads such that all waiters are
+    // serviced before a new poll can begin.
+    // The wakeAndLock() method increments mWaiters, wakes the poll, blocks on mAwake
+    // until mPolling becomes false, then decrements mWaiters again.
+    // The poll() method blocks on mResume until mWaiters becomes 0, then sets
+    // mPolling to true, blocks until the poll completes, then resets mPolling to false
+    // and signals mResume if there are waiters.
     Mutex mLock;
-    bool mPolling;
-    uint32_t mWaiters;
-    Condition mAwake;
-    Condition mResume;
-
-    int mWakeReadPipeFd;
-    int mWakeWritePipeFd;
-
+    bool mPolling;      // guarded by mLock
+    uint32_t mWaiters;  // guarded by mLock
+    Condition mAwake;   // guarded by mLock
+    Condition mResume;  // guarded by mLock
+
+    // The next two vectors are only mutated when mPolling is false since they must
+    // not be changed while the poll() system call is in progress.  To mutate these
+    // vectors, the poll() must first be awoken then the lock acquired.
     Vector<struct pollfd> mRequestedFds;
     Vector<RequestedCallback> mRequestedCallbacks;
 
-    Vector<PendingCallback> mPendingCallbacks; // used privately by pollOnce
-    Vector<PendingCallback> mPendingFds;       // used privately by pollOnce
+    // This state is only used privately by pollOnce and does not require a lock since
+    // it runs on a single thread.
+    Vector<PendingCallback> mPendingCallbacks;
+    Vector<PendingCallback> mPendingFds;
     size_t mPendingFdsPos;
     
     void openWakePipe();
index df232d4..b8a26b0 100644 (file)
 // Log debug messages about input event throttling.
 #define DEBUG_THROTTLING 0
 
+// Log debug messages about input focus tracking.
+#define DEBUG_FOCUS 0
+
+// Log debug messages about the app switch latency optimization.
+#define DEBUG_APP_SWITCH 0
+
 #include <cutils/log.h>
 #include <ui/InputDispatcher.h>
+#include <ui/PowerManager.h>
 
 #include <stddef.h>
 #include <unistd.h>
 
 namespace android {
 
-// TODO, this needs to be somewhere else, perhaps in the policy
-static inline bool isMovementKey(int32_t keyCode) {
-    return keyCode == AKEYCODE_DPAD_UP
-            || keyCode == AKEYCODE_DPAD_DOWN
-            || keyCode == AKEYCODE_DPAD_LEFT
-            || keyCode == AKEYCODE_DPAD_RIGHT;
-}
+// Delay between reporting long touch events to the power manager.
+const nsecs_t EVENT_IGNORE_DURATION = 300 * 1000000LL; // 300 ms
+
+// Default input dispatching timeout if there is no focused application or paused window
+// from which to determine an appropriate dispatching timeout.
+const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec
+
+// Amount of time to allow for all pending events to be processed when an app switch
+// key is on the way.  This is used to preempt input dispatch and drop input events
+// when an application takes too long to respond and the user has pressed an app switch key.
+const nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec
+
 
 static inline nsecs_t now() {
     return systemTime(SYSTEM_TIME_MONOTONIC);
 }
 
+static inline const char* toString(bool value) {
+    return value ? "true" : "false";
+}
+
+
+// --- InputWindow ---
+
+bool InputWindow::visibleFrameIntersects(const InputWindow* other) const {
+    return visibleFrameRight > other->visibleFrameLeft
+        && visibleFrameLeft < other->visibleFrameRight
+        && visibleFrameBottom > other->visibleFrameTop
+        && visibleFrameTop < other->visibleFrameBottom;
+}
+
+bool InputWindow::touchableAreaContainsPoint(int32_t x, int32_t y) const {
+    return x >= touchableAreaLeft && x <= touchableAreaRight
+            && y >= touchableAreaTop && y <= touchableAreaBottom;
+}
+
+
 // --- InputDispatcher ---
 
 InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
-    mPolicy(policy) {
+    mPolicy(policy),
+    mPendingEvent(NULL), mAppSwitchDueTime(LONG_LONG_MAX),
+    mDispatchEnabled(true), mDispatchFrozen(false),
+    mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
+    mFocusedApplication(NULL),
+    mCurrentInputTargetsValid(false),
+    mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
     mPollLoop = new PollLoop(false);
 
-    mInboundQueue.head.refCount = -1;
-    mInboundQueue.head.type = EventEntry::TYPE_SENTINEL;
-    mInboundQueue.head.eventTime = LONG_LONG_MIN;
+    mInboundQueue.headSentinel.refCount = -1;
+    mInboundQueue.headSentinel.type = EventEntry::TYPE_SENTINEL;
+    mInboundQueue.headSentinel.eventTime = LONG_LONG_MIN;
 
-    mInboundQueue.tail.refCount = -1;
-    mInboundQueue.tail.type = EventEntry::TYPE_SENTINEL;
-    mInboundQueue.tail.eventTime = LONG_LONG_MAX;
+    mInboundQueue.tailSentinel.refCount = -1;
+    mInboundQueue.tailSentinel.type = EventEntry::TYPE_SENTINEL;
+    mInboundQueue.tailSentinel.eventTime = LONG_LONG_MAX;
 
     mKeyRepeatState.lastKeyEntry = NULL;
 
@@ -77,21 +115,19 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic
     mThrottleState.originalSampleCount = 0;
     LOGD("Throttling - Max events per second = %d", maxEventsPerSecond);
 #endif
-
-    mCurrentInputTargetsValid = false;
 }
 
 InputDispatcher::~InputDispatcher() {
-    resetKeyRepeatLocked();
+    { // acquire lock
+        AutoMutex _l(mLock);
 
-    while (mConnectionsByReceiveFd.size() != 0) {
-        unregisterInputChannel(mConnectionsByReceiveFd.valueAt(0)->inputChannel);
+        resetKeyRepeatLocked();
+        releasePendingEventLocked(true);
+        drainInboundQueueLocked();
     }
 
-    for (EventEntry* entry = mInboundQueue.head.next; entry != & mInboundQueue.tail; ) {
-        EventEntry* next = entry->next;
-        mAllocator.releaseEventEntry(next);
-        entry = next;
+    while (mConnectionsByReceiveFd.size() != 0) {
+        unregisterInputChannel(mConnectionsByReceiveFd.valueAt(0)->inputChannel);
     }
 }
 
@@ -99,167 +135,282 @@ void InputDispatcher::dispatchOnce() {
     nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();
     nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay();
 
-    bool skipPoll = false;
-    nsecs_t currentTime;
     nsecs_t nextWakeupTime = LONG_LONG_MAX;
     { // acquire lock
         AutoMutex _l(mLock);
-        currentTime = now();
+        dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime);
 
-        // Reset the key repeat timer whenever we disallow key events, even if the next event
-        // is not a key.  This is to ensure that we abort a key repeat if the device is just coming
-        // out of sleep.
-        // XXX we should handle resetting input state coming out of sleep more generally elsewhere
-        if (keyRepeatTimeout < 0) {
-            resetKeyRepeatLocked();
+        if (runCommandsLockedInterruptible()) {
+            nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
+        }
+    } // release lock
+
+    // Wait for callback or timeout or wake.  (make sure we round up, not down)
+    nsecs_t currentTime = now();
+    int32_t timeoutMillis;
+    if (nextWakeupTime > currentTime) {
+        uint64_t timeout = uint64_t(nextWakeupTime - currentTime);
+        timeout = (timeout + 999999LL) / 1000000LL;
+        timeoutMillis = timeout > INT_MAX ? -1 : int32_t(timeout);
+    } else {
+        timeoutMillis = 0;
+    }
+
+    mPollLoop->pollOnce(timeoutMillis);
+}
+
+void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
+        nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) {
+    nsecs_t currentTime = now();
+
+    // Reset the key repeat timer whenever we disallow key events, even if the next event
+    // is not a key.  This is to ensure that we abort a key repeat if the device is just coming
+    // out of sleep.
+    if (keyRepeatTimeout < 0) {
+        resetKeyRepeatLocked();
+    }
+
+    // If dispatching is disabled, drop all events in the queue.
+    if (! mDispatchEnabled) {
+        if (mPendingEvent || ! mInboundQueue.isEmpty()) {
+            LOGI("Dropping pending events because input dispatch is disabled.");
+            releasePendingEventLocked(true);
+            drainInboundQueueLocked();
         }
+        return;
+    }
+
+    // If dispatching is frozen, do not process timeouts or try to deliver any new events.
+    if (mDispatchFrozen) {
+#if DEBUG_FOCUS
+        LOGD("Dispatch frozen.  Waiting some more.");
+#endif
+        return;
+    }
 
-        // Detect and process timeouts for all connections and determine if there are any
-        // synchronous event dispatches pending.  This step is entirely non-interruptible.
-        bool hasPendingSyncTarget = false;
-        size_t activeConnectionCount = mActiveConnections.size();
-        for (size_t i = 0; i < activeConnectionCount; i++) {
-            Connection* connection = mActiveConnections.itemAt(i);
+    // Optimize latency of app switches.
+    // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has
+    // been pressed.  When it expires, we preempt dispatch and drop all other pending events.
+    bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
+    if (mAppSwitchDueTime < *nextWakeupTime) {
+        *nextWakeupTime = mAppSwitchDueTime;
+    }
 
-            if (connection->hasPendingSyncTarget()) {
-                hasPendingSyncTarget = true;
-            }
+    // 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);
 
-            nsecs_t connectionTimeoutTime  = connection->nextTimeoutTime;
-            if (connectionTimeoutTime <= currentTime) {
-                mTimedOutConnections.add(connection);
-            } else if (connectionTimeoutTime < nextWakeupTime) {
-                nextWakeupTime = connectionTimeoutTime;
+        if (connection->hasPendingSyncTarget()) {
+            if (isAppSwitchDue) {
+                connection->preemptSyncTarget();
+            } else {
+                havePendingSyncTarget = true;
             }
         }
 
-        size_t timedOutConnectionCount = mTimedOutConnections.size();
-        for (size_t i = 0; i < timedOutConnectionCount; i++) {
-            Connection* connection = mTimedOutConnections.itemAt(i);
-            timeoutDispatchCycleLocked(currentTime, connection);
-            skipPoll = true;
-        }
-        mTimedOutConnections.clear();
-
-        // If we don't have a pending sync target, then we can begin delivering a new event.
-        // (Otherwise we wait for dispatch to complete for that target.)
-        if (! hasPendingSyncTarget) {
-            if (mInboundQueue.isEmpty()) {
-                if (mKeyRepeatState.lastKeyEntry) {
-                    if (currentTime >= mKeyRepeatState.nextRepeatTime) {
-                        processKeyRepeatLockedInterruptible(currentTime, keyRepeatDelay);
-                        skipPoll = true;
-                    } else {
-                        if (mKeyRepeatState.nextRepeatTime < nextWakeupTime) {
-                            nextWakeupTime = mKeyRepeatState.nextRepeatTime;
-                        }
+        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) {
+        if (mInboundQueue.isEmpty()) {
+            if (isAppSwitchDue) {
+                // The inbound queue is empty so the app switch key we were waiting
+                // for will never arrive.  Stop waiting for it.
+                resetPendingAppSwitchLocked(false);
+                isAppSwitchDue = false;
+            }
+
+            // Synthesize a key repeat if appropriate.
+            if (mKeyRepeatState.lastKeyEntry) {
+                if (currentTime >= mKeyRepeatState.nextRepeatTime) {
+                    mPendingEvent = synthesizeKeyRepeatLocked(currentTime, keyRepeatDelay);
+                } else {
+                    if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
+                        *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
                     }
                 }
-            } else {
-                // Inbound queue has at least one entry.
-                EventEntry* entry = mInboundQueue.head.next;
-
-                // Consider throttling the entry if it is a move event and there are no
-                // other events behind it in the queue.  Due to movement batching, additional
-                // samples may be appended to this event by the time the throttling timeout
-                // expires.
-                // TODO Make this smarter and consider throttling per device independently.
-                if (entry->type == EventEntry::TYPE_MOTION) {
-                    MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
-                    int32_t deviceId = motionEntry->deviceId;
-                    uint32_t source = motionEntry->source;
-                    if (motionEntry->next == & mInboundQueue.tail
-                            && motionEntry->action == AMOTION_EVENT_ACTION_MOVE
-                            && deviceId == mThrottleState.lastDeviceId
-                            && source == mThrottleState.lastSource) {
-                        nsecs_t nextTime = mThrottleState.lastEventTime
-                                + mThrottleState.minTimeBetweenEvents;
-                        if (currentTime < nextTime) {
-                            // Throttle it!
+            }
+            if (! mPendingEvent) {
+                return;
+            }
+        } else {
+            // Inbound queue has at least one entry.
+            EventEntry* entry = mInboundQueue.headSentinel.next;
+
+            // Throttle the entry if it is a move event and there are no
+            // other events behind it in the queue.  Due to movement batching, additional
+            // samples may be appended to this event by the time the throttling timeout
+            // expires.
+            // TODO Make this smarter and consider throttling per device independently.
+            if (entry->type == EventEntry::TYPE_MOTION) {
+                MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
+                int32_t deviceId = motionEntry->deviceId;
+                uint32_t source = motionEntry->source;
+                if (! isAppSwitchDue
+                        && motionEntry->next == & mInboundQueue.tailSentinel // exactly one event
+                        && motionEntry->action == AMOTION_EVENT_ACTION_MOVE
+                        && deviceId == mThrottleState.lastDeviceId
+                        && source == mThrottleState.lastSource) {
+                    nsecs_t nextTime = mThrottleState.lastEventTime
+                            + mThrottleState.minTimeBetweenEvents;
+                    if (currentTime < nextTime) {
+                        // Throttle it!
 #if DEBUG_THROTTLING
-                            LOGD("Throttling - Delaying motion event for "
-                                    "device 0x%x, source 0x%08x by up to %0.3fms.",
-                                    deviceId, source, (nextTime - currentTime) * 0.000001);
+                        LOGD("Throttling - Delaying motion event for "
+                                "device 0x%x, source 0x%08x by up to %0.3fms.",
+                                deviceId, source, (nextTime - currentTime) * 0.000001);
 #endif
-                            if (nextTime < nextWakeupTime) {
-                                nextWakeupTime = nextTime;
-                            }
-                            if (mThrottleState.originalSampleCount == 0) {
-                                mThrottleState.originalSampleCount =
-                                        motionEntry->countSamples();
-                            }
-                            goto Throttle;
+                        if (nextTime < *nextWakeupTime) {
+                            *nextWakeupTime = nextTime;
                         }
+                        if (mThrottleState.originalSampleCount == 0) {
+                            mThrottleState.originalSampleCount =
+                                    motionEntry->countSamples();
+                        }
+                        return;
                     }
+                }
 
 #if DEBUG_THROTTLING
-                    if (mThrottleState.originalSampleCount != 0) {
-                        uint32_t count = motionEntry->countSamples();
-                        LOGD("Throttling - Motion event sample count grew by %d from %d to %d.",
-                                count - mThrottleState.originalSampleCount,
-                                mThrottleState.originalSampleCount, count);
-                        mThrottleState.originalSampleCount = 0;
-                    }
+                if (mThrottleState.originalSampleCount != 0) {
+                    uint32_t count = motionEntry->countSamples();
+                    LOGD("Throttling - Motion event sample count grew by %d from %d to %d.",
+                            count - mThrottleState.originalSampleCount,
+                            mThrottleState.originalSampleCount, count);
+                    mThrottleState.originalSampleCount = 0;
+                }
 #endif
 
-                    mThrottleState.lastEventTime = entry->eventTime < currentTime
-                            ? entry->eventTime : currentTime;
-                    mThrottleState.lastDeviceId = deviceId;
-                    mThrottleState.lastSource = source;
-                }
+                mThrottleState.lastEventTime = entry->eventTime < currentTime
+                        ? entry->eventTime : currentTime;
+                mThrottleState.lastDeviceId = deviceId;
+                mThrottleState.lastSource = source;
+            }
 
-                // Start processing the entry but leave it on the queue until later so that the
-                // input reader can keep appending samples onto a motion event between the
-                // time we started processing it and the time we finally enqueue dispatch
-                // entries for it.
-                switch (entry->type) {
-                case EventEntry::TYPE_CONFIGURATION_CHANGED: {
-                    ConfigurationChangedEntry* typedEntry =
-                            static_cast<ConfigurationChangedEntry*>(entry);
-                    processConfigurationChangedLockedInterruptible(currentTime, typedEntry);
-                    break;
-                }
+            mInboundQueue.dequeue(entry);
+            mPendingEvent = entry;
+        }
+    }
 
-                case EventEntry::TYPE_KEY: {
-                    KeyEntry* typedEntry = static_cast<KeyEntry*>(entry);
-                    processKeyLockedInterruptible(currentTime, typedEntry, keyRepeatTimeout);
-                    break;
-                }
+    // Now we have an event to dispatch.
+    assert(mPendingEvent != NULL);
+    bool wasDispatched = false;
+    bool wasDropped = false;
+    switch (mPendingEvent->type) {
+    case EventEntry::TYPE_CONFIGURATION_CHANGED: {
+        ConfigurationChangedEntry* typedEntry =
+                static_cast<ConfigurationChangedEntry*>(mPendingEvent);
+        wasDispatched = dispatchConfigurationChangedLocked(currentTime, typedEntry);
+        break;
+    }
 
-                case EventEntry::TYPE_MOTION: {
-                    MotionEntry* typedEntry = static_cast<MotionEntry*>(entry);
-                    processMotionLockedInterruptible(currentTime, typedEntry);
-                    break;
-                }
+    case EventEntry::TYPE_KEY: {
+        KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
+        if (isAppSwitchPendingLocked()) {
+            if (isAppSwitchKey(typedEntry->keyCode)) {
+                resetPendingAppSwitchLocked(true);
+            } else if (isAppSwitchDue) {
+                LOGI("Dropping key because of pending overdue app switch.");
+                wasDropped = true;
+                break;
+            }
+        }
+        wasDispatched = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout,
+                nextWakeupTime);
+        break;
+    }
 
-                default:
-                    assert(false);
-                    break;
-                }
+    case EventEntry::TYPE_MOTION: {
+        MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
+        if (isAppSwitchDue) {
+            LOGI("Dropping motion because of pending overdue app switch.");
+            wasDropped = true;
+            break;
+        }
+        wasDispatched = dispatchMotionLocked(currentTime, typedEntry, nextWakeupTime);
+        break;
+    }
 
-                // Dequeue and release the event entry that we just processed.
-                mInboundQueue.dequeue(entry);
-                mAllocator.releaseEventEntry(entry);
-                skipPoll = true;
+    default:
+        assert(false);
+        wasDropped = true;
+        break;
+    }
 
-            Throttle: ;
-            }
-        }
+    if (wasDispatched || wasDropped) {
+        releasePendingEventLocked(wasDropped);
+        *nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
+    }
+}
 
-        // Run any deferred commands.
-        skipPoll |= runCommandsLockedInterruptible();
-    } // release lock
+bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
+    bool needWake = mInboundQueue.isEmpty();
+    mInboundQueue.enqueueAtTail(entry);
 
-    // If we dispatched anything, don't poll just now.  Wait for the next iteration.
-    // Contents may have shifted during flight.
-    if (skipPoll) {
-        return;
+    switch (entry->type) {
+    case EventEntry::TYPE_KEY:
+        needWake |= detectPendingAppSwitchLocked(static_cast<KeyEntry*>(entry));
+        break;
     }
 
-    // Wait for callback or timeout or wake.  (make sure we round up, not down)
-    nsecs_t timeout = (nextWakeupTime - currentTime + 999999LL) / 1000000LL;
-    int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0;
-    mPollLoop->pollOnce(timeoutMillis);
+    return needWake;
+}
+
+bool InputDispatcher::isAppSwitchKey(int32_t keyCode) {
+    return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL;
+}
+
+bool InputDispatcher::isAppSwitchPendingLocked() {
+    return mAppSwitchDueTime != LONG_LONG_MAX;
+}
+
+bool InputDispatcher::detectPendingAppSwitchLocked(KeyEntry* inboundKeyEntry) {
+    if (inboundKeyEntry->action == AKEY_EVENT_ACTION_UP
+            && ! (inboundKeyEntry->flags & AKEY_EVENT_FLAG_CANCELED)
+            && isAppSwitchKey(inboundKeyEntry->keyCode)
+            && isEventFromReliableSourceLocked(inboundKeyEntry)) {
+#if DEBUG_APP_SWITCH
+        LOGD("App switch is pending!");
+#endif
+        mAppSwitchDueTime = inboundKeyEntry->eventTime + APP_SWITCH_TIMEOUT;
+        return true; // need wake
+    }
+    return false;
+}
+
+void InputDispatcher::resetPendingAppSwitchLocked(bool handled) {
+    mAppSwitchDueTime = LONG_LONG_MAX;
+
+#if DEBUG_APP_SWITCH
+    if (handled) {
+        LOGD("App switch has arrived.");
+    } else {
+        LOGD("App switch was abandoned.");
+    }
+#endif
 }
 
 bool InputDispatcher::runCommandsLockedInterruptible() {
@@ -285,78 +436,52 @@ InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command comman
     return commandEntry;
 }
 
-void InputDispatcher::processConfigurationChangedLockedInterruptible(
-        nsecs_t currentTime, ConfigurationChangedEntry* entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    LOGD("processConfigurationChanged - eventTime=%lld", entry->eventTime);
-#endif
-
-    // Reset key repeating in case a keyboard device was added or removed or something.
-    resetKeyRepeatLocked();
-
-    mLock.unlock();
-
-    mPolicy->notifyConfigurationChanged(entry->eventTime);
+void InputDispatcher::drainInboundQueueLocked() {
+    while (! mInboundQueue.isEmpty()) {
+        EventEntry* entry = mInboundQueue.dequeueAtHead();
+        releaseInboundEventLocked(entry, true /*wasDropped*/);
+    }
+}
 
-    mLock.lock();
+void InputDispatcher::releasePendingEventLocked(bool wasDropped) {
+    if (mPendingEvent) {
+        releaseInboundEventLocked(mPendingEvent, wasDropped);
+        mPendingEvent = NULL;
+    }
 }
 
-void InputDispatcher::processKeyLockedInterruptible(
-        nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    LOGD("processKey - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, action=0x%x, "
-            "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld",
-            entry->eventTime, entry->deviceId, entry->source, entry->policyFlags, entry->action,
-            entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
-            entry->downTime);
+void InputDispatcher::releaseInboundEventLocked(EventEntry* entry, bool wasDropped) {
+    if (wasDropped) {
+#if DEBUG_DISPATCH_CYCLE
+        LOGD("Pending event was dropped.");
 #endif
-
-    if (entry->action == AKEY_EVENT_ACTION_DOWN && ! entry->isInjected()) {
-        if (mKeyRepeatState.lastKeyEntry
-                && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
-            // We have seen two identical key downs in a row which indicates that the device
-            // driver is automatically generating key repeats itself.  We take note of the
-            // repeat here, but we disable our own next key repeat timer since it is clear that
-            // we will not need to synthesize key repeats ourselves.
-            entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
-            resetKeyRepeatLocked();
-            mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
-        } else {
-            // Not a repeat.  Save key down state in case we do see a repeat later.
-            resetKeyRepeatLocked();
-            mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout;
-        }
-        mKeyRepeatState.lastKeyEntry = entry;
-        entry->refCount += 1;
-    } else {
-        resetKeyRepeatLocked();
+        setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
     }
+    mAllocator.releaseEventEntry(entry);
+}
 
-    identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry);
+bool InputDispatcher::isEventFromReliableSourceLocked(EventEntry* entry) {
+    return ! entry->isInjected()
+            || entry->injectorUid == 0
+            || mPolicy->checkInjectEventsPermissionNonReentrant(
+                    entry->injectorPid, entry->injectorUid);
 }
 
-void InputDispatcher::processKeyRepeatLockedInterruptible(
+void InputDispatcher::resetKeyRepeatLocked() {
+    if (mKeyRepeatState.lastKeyEntry) {
+        mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry);
+        mKeyRepeatState.lastKeyEntry = NULL;
+    }
+}
+
+InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(
         nsecs_t currentTime, nsecs_t keyRepeatDelay) {
     KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
 
-    // Search the inbound queue for a key up corresponding to this device.
-    // It doesn't make sense to generate a key repeat event if the key is already up.
-    for (EventEntry* queuedEntry = mInboundQueue.head.next;
-            queuedEntry != & mInboundQueue.tail; queuedEntry = entry->next) {
-        if (queuedEntry->type == EventEntry::TYPE_KEY) {
-            KeyEntry* queuedKeyEntry = static_cast<KeyEntry*>(queuedEntry);
-            if (queuedKeyEntry->deviceId == entry->deviceId
-                    && entry->action == AKEY_EVENT_ACTION_UP) {
-                resetKeyRepeatLocked();
-                return;
-            }
-        }
-    }
-
-    // Synthesize a key repeat.
     // Reuse the repeated key entry if it is otherwise unreferenced.
     uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK;
     if (entry->refCount == 1) {
+        entry->recycle();
         entry->eventTime = currentTime;
         entry->policyFlags = policyFlags;
         entry->repeatCount += 1;
@@ -371,31 +496,194 @@ void InputDispatcher::processKeyRepeatLockedInterruptible(
 
         entry = newEntry;
     }
+    entry->syntheticRepeat = true;
+
+    // Increment reference count since we keep a reference to the event in
+    // mKeyRepeatState.lastKeyEntry in addition to the one we return.
+    entry->refCount += 1;
 
     if (entry->repeatCount == 1) {
         entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS;
     }
 
     mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatDelay;
+    return entry;
+}
+
+bool InputDispatcher::dispatchConfigurationChangedLocked(
+        nsecs_t currentTime, ConfigurationChangedEntry* entry) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+    LOGD("dispatchConfigurationChanged - eventTime=%lld", entry->eventTime);
+#endif
+
+    // Reset key repeating in case a keyboard device was added or removed or something.
+    resetKeyRepeatLocked();
+
+    // Enqueue a command to run outside the lock to tell the policy that the configuration changed.
+    CommandEntry* commandEntry = postCommandLocked(
+            & InputDispatcher::doNotifyConfigurationChangedInterruptible);
+    commandEntry->eventTime = entry->eventTime;
+    return true;
+}
 
+bool InputDispatcher::dispatchKeyLocked(
+        nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,
+        nsecs_t* nextWakeupTime) {
+    // Preprocessing.
+    if (! entry->dispatchInProgress) {
+        logOutboundKeyDetailsLocked("dispatchKey - ", entry);
+
+        if (entry->repeatCount == 0
+                && entry->action == AKEY_EVENT_ACTION_DOWN
+                && ! entry->isInjected()) {
+            if (mKeyRepeatState.lastKeyEntry
+                    && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
+                // We have seen two identical key downs in a row which indicates that the device
+                // driver is automatically generating key repeats itself.  We take note of the
+                // repeat here, but we disable our own next key repeat timer since it is clear that
+                // we will not need to synthesize key repeats ourselves.
+                entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
+                resetKeyRepeatLocked();
+                mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
+            } else {
+                // Not a repeat.  Save key down state in case we do see a repeat later.
+                resetKeyRepeatLocked();
+                mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout;
+            }
+            mKeyRepeatState.lastKeyEntry = entry;
+            entry->refCount += 1;
+        } else if (! entry->syntheticRepeat) {
+            resetKeyRepeatLocked();
+        }
+
+        entry->dispatchInProgress = true;
+        startFindingTargetsLocked();
+    }
+
+    // Identify targets.
+    if (! mCurrentInputTargetsValid) {
+        InputWindow* window = NULL;
+        int32_t injectionResult = findFocusedWindowLocked(currentTime,
+                entry, nextWakeupTime, & window);
+        if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+            return false;
+        }
+
+        setInjectionResultLocked(entry, injectionResult);
+        if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+            return true;
+        }
+
+        addMonitoringTargetsLocked();
+        finishFindingTargetsLocked(window);
+    }
+
+    // Give the policy a chance to intercept the key.
+    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
+        CommandEntry* commandEntry = postCommandLocked(
+                & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
+        commandEntry->inputChannel = mCurrentInputChannel;
+        commandEntry->keyEntry = entry;
+        entry->refCount += 1;
+        return false; // wait for the command to run
+    }
+    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
+        return true;
+    }
+
+    // Dispatch the key.
+    dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+
+    // Poke user activity.
+    pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, POWER_MANAGER_BUTTON_EVENT);
+    return true;
+}
+
+void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
-    LOGD("processKeyRepeat - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
+    LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
             "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, "
-            "repeatCount=%d, downTime=%lld",
+            "downTime=%lld",
+            prefix,
             entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
             entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
-            entry->repeatCount, entry->downTime);
+            entry->downTime);
 #endif
+}
+
+bool InputDispatcher::dispatchMotionLocked(
+        nsecs_t currentTime, MotionEntry* entry, nsecs_t* nextWakeupTime) {
+    // Preprocessing.
+    if (! entry->dispatchInProgress) {
+        logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
+
+        entry->dispatchInProgress = true;
+        startFindingTargetsLocked();
+    }
+
+    bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
+
+    // Identify targets.
+    if (! mCurrentInputTargetsValid) {
+        InputWindow* window = NULL;
+        int32_t injectionResult;
+        if (isPointerEvent) {
+            // Pointer event.  (eg. touchscreen)
+            injectionResult = findTouchedWindowLocked(currentTime,
+                    entry, nextWakeupTime, & window);
+        } else {
+            // Non touch event.  (eg. trackball)
+            injectionResult = findFocusedWindowLocked(currentTime,
+                    entry, nextWakeupTime, & window);
+        }
+        if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+            return false;
+        }
 
-    identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry);
+        setInjectionResultLocked(entry, injectionResult);
+        if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+            return true;
+        }
+
+        addMonitoringTargetsLocked();
+        finishFindingTargetsLocked(window);
+    }
+
+    // Dispatch the motion.
+    dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+
+    // Poke user activity.
+    int32_t eventType;
+    if (isPointerEvent) {
+        switch (entry->action) {
+        case AMOTION_EVENT_ACTION_DOWN:
+            eventType = POWER_MANAGER_TOUCH_EVENT;
+            break;
+        case AMOTION_EVENT_ACTION_UP:
+            eventType = POWER_MANAGER_TOUCH_UP_EVENT;
+            break;
+        default:
+            if (entry->eventTime - entry->downTime >= EVENT_IGNORE_DURATION) {
+                eventType = POWER_MANAGER_TOUCH_EVENT;
+            } else {
+                eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
+            }
+            break;
+        }
+    } else {
+        eventType = POWER_MANAGER_BUTTON_EVENT;
+    }
+    pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, eventType);
+    return true;
 }
 
-void InputDispatcher::processMotionLockedInterruptible(
-        nsecs_t currentTime, MotionEntry* entry) {
+
+void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
-    LOGD("processMotion - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
+    LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
             "action=0x%x, flags=0x%x, "
             "metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld",
+            prefix,
             entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
             entry->action, entry->flags,
             entry->metaState, entry->edgeFlags, entry->xPrecision, entry->yPrecision,
@@ -403,7 +691,7 @@ void InputDispatcher::processMotionLockedInterruptible(
 
     // Print the most recent sample that we have available, this may change due to batching.
     size_t sampleCount = 1;
-    MotionSample* sample = & entry->firstSample;
+    const MotionSample* sample = & entry->firstSample;
     for (; sample->next != NULL; sample = sample->next) {
         sampleCount += 1;
     }
@@ -425,94 +713,541 @@ void InputDispatcher::processMotionLockedInterruptible(
         LOGD("  ... Total movement samples currently batched %d ...", sampleCount);
     }
 #endif
-
-    identifyInputTargetsAndDispatchMotionLockedInterruptible(currentTime, entry);
 }
 
-void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible(
-        nsecs_t currentTime, KeyEntry* entry) {
+void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
+        EventEntry* eventEntry, bool resumeWithAppendedMotionSample) {
 #if DEBUG_DISPATCH_CYCLE
-    LOGD("identifyInputTargetsAndDispatchKey");
+    LOGD("dispatchEventToCurrentInputTargets - "
+            "resumeWithAppendedMotionSample=%s",
+            toString(resumeWithAppendedMotionSample));
 #endif
 
-    entry->dispatchInProgress = true;
-    mCurrentInputTargetsValid = false;
-    mLock.unlock();
+    assert(eventEntry->dispatchInProgress); // should already have been set to true
 
-    mReusableKeyEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags,
-            entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
-            entry->downTime, entry->eventTime);
+    for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
+        const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);
 
+        ssize_t connectionIndex = getConnectionIndex(inputTarget.inputChannel);
+        if (connectionIndex >= 0) {
+            sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+            prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
+                    resumeWithAppendedMotionSample);
+        } else {
+            LOGW("Framework requested delivery of an input event to channel '%s' but it "
+                    "is not registered with the input dispatcher.",
+                    inputTarget.inputChannel->getName().string());
+        }
+    }
+}
+
+void InputDispatcher::startFindingTargetsLocked() {
+    mCurrentInputTargetsValid = false;
     mCurrentInputTargets.clear();
-    int32_t injectionResult = mPolicy->waitForKeyEventTargets(& mReusableKeyEvent,
-            entry->policyFlags, entry->injectorPid, entry->injectorUid,
-            mCurrentInputTargets);
+    mCurrentInputChannel.clear();
+    mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
+}
 
-    mLock.lock();
+void InputDispatcher::finishFindingTargetsLocked(const InputWindow* window) {
+    mCurrentInputWindowType = window->layoutParamsType;
+    mCurrentInputChannel = window->inputChannel;
     mCurrentInputTargetsValid = true;
+}
+
+int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
+        const EventEntry* entry, const InputApplication* application, const InputWindow* window,
+        nsecs_t* nextWakeupTime) {
+    if (application == NULL && window == NULL) {
+        if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
+#if DEBUG_FOCUS
+            LOGD("Waiting for system to become ready for input.");
+#endif
+            mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
+            mInputTargetWaitStartTime = currentTime;
+            mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
+            mInputTargetWaitTimeoutExpired = false;
+        }
+    } 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>");
+#endif
+            nsecs_t timeout = window ? window->dispatchingTimeout :
+                application ? application->dispatchingTimeout : DEFAULT_INPUT_DISPATCHING_TIMEOUT;
+
+            mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
+            mInputTargetWaitStartTime = currentTime;
+            mInputTargetWaitTimeoutTime = currentTime + timeout;
+            mInputTargetWaitTimeoutExpired = false;
+        }
+    }
+
+    if (mInputTargetWaitTimeoutExpired) {
+        return INPUT_EVENT_INJECTION_TIMED_OUT;
+    }
+
+    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;
+        }
 
-    setInjectionResultLocked(entry, injectionResult);
+        // Force poll loop to wake up immediately on next iteration once we get the
+        // ANR response back from the policy.
+        *nextWakeupTime = LONG_LONG_MIN;
+        return INPUT_EVENT_INJECTION_PENDING;
+    } else {
+        // Force poll loop to wake up when timeout is due.
+        if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
+            *nextWakeupTime = mInputTargetWaitTimeoutTime;
+        }
+        return INPUT_EVENT_INJECTION_PENDING;
+    }
+}
 
-    if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
-        dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout) {
+    if (newTimeout > 0) {
+        // Extend the timeout.
+        mInputTargetWaitTimeoutTime = now() + newTimeout;
+    } else {
+        // Give up.
+        mInputTargetWaitTimeoutExpired = true;
     }
 }
 
-void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible(
-        nsecs_t currentTime, MotionEntry* entry) {
-#if DEBUG_DISPATCH_CYCLE
-    LOGD("identifyInputTargetsAndDispatchMotion");
+nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(
+        nsecs_t currentTime) {
+    if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
+        return currentTime - mInputTargetWaitStartTime;
+    }
+    return 0;
+}
+
+void InputDispatcher::resetANRTimeoutsLocked() {
+#if DEBUG_FOCUS
+        LOGD("Resetting ANR timeouts.");
 #endif
 
-    entry->dispatchInProgress = true;
-    mCurrentInputTargetsValid = false;
-    mLock.unlock();
+    // 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);
+    }
 
-    mReusableMotionEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags,
-            entry->edgeFlags, entry->metaState,
-            0, 0, entry->xPrecision, entry->yPrecision,
-            entry->downTime, entry->eventTime, entry->pointerCount, entry->pointerIds,
-            entry->firstSample.pointerCoords);
+    // Reset input target wait timeout.
+    mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
+}
 
+int32_t InputDispatcher::findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry,
+        nsecs_t* nextWakeupTime, InputWindow** outWindow) {
+    *outWindow = NULL;
     mCurrentInputTargets.clear();
-    int32_t injectionResult = mPolicy->waitForMotionEventTargets(& mReusableMotionEvent,
-            entry->policyFlags, entry->injectorPid, entry->injectorUid,
-            mCurrentInputTargets);
 
-    mLock.lock();
-    mCurrentInputTargetsValid = true;
+    int32_t injectionResult;
 
-    setInjectionResultLocked(entry, injectionResult);
+    // If there is no currently focused window and no focused application
+    // then drop the event.
+    if (! mFocusedWindow) {
+        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());
+#endif
+            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+                    mFocusedApplication, NULL, nextWakeupTime);
+            goto Unresponsive;
+        }
 
-    if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
-        dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+        LOGI("Dropping event because there is no focused window or focused application.");
+        injectionResult = INPUT_EVENT_INJECTION_FAILED;
+        goto Failed;
     }
+
+    // Check permissions.
+    if (! checkInjectionPermission(mFocusedWindow, entry->injectorPid, entry->injectorUid)) {
+        injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+        goto Failed;
+    }
+
+    // If the currently focused window is paused then keep waiting.
+    if (mFocusedWindow->paused) {
+#if DEBUG_FOCUS
+        LOGD("Waiting because focused window is paused.");
+#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));
+
+    // Done.
+Failed:
+Unresponsive:
+#if DEBUG_FOCUS
+    LOGD("findFocusedWindow finished: injectionResult=%d",
+            injectionResult);
+    logDispatchStateLocked();
+#endif
+    return injectionResult;
 }
 
-void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
-        EventEntry* eventEntry, bool resumeWithAppendedMotionSample) {
-#if DEBUG_DISPATCH_CYCLE
-    LOGD("dispatchEventToCurrentInputTargets - "
-            "resumeWithAppendedMotionSample=%s",
-            resumeWithAppendedMotionSample ? "true" : "false");
+int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry,
+        nsecs_t* nextWakeupTime, InputWindow** outWindow) {
+    enum InjectionPermission {
+        INJECTION_PERMISSION_UNKNOWN,
+        INJECTION_PERMISSION_GRANTED,
+        INJECTION_PERMISSION_DENIED
+    };
+
+    *outWindow = NULL;
+    mCurrentInputTargets.clear();
+
+    nsecs_t startTime = now();
+
+    // For security reasons, we defer updating the touch state until we are sure that
+    // event injection will be allowed.
+    //
+    // FIXME In the original code, screenWasOff could never be set to true.
+    //       The reason is that the POLICY_FLAG_WOKE_HERE
+    //       and POLICY_FLAG_BRIGHT_HERE flags were set only when preprocessing raw
+    //       EV_KEY, EV_REL and EV_ABS events.  As it happens, the touch event was
+    //       actually enqueued using the policyFlags that appeared in the final EV_SYN
+    //       events upon which no preprocessing took place.  So policyFlags was always 0.
+    //       In the new native input dispatcher we're a bit more careful about event
+    //       preprocessing so the touches we receive can actually have non-zero policyFlags.
+    //       Unfortunately we obtain undesirable behavior.
+    //
+    //       Here's what happens:
+    //
+    //       When the device dims in anticipation of going to sleep, touches
+    //       in windows which have FLAG_TOUCHABLE_WHEN_WAKING cause
+    //       the device to brighten and reset the user activity timer.
+    //       Touches on other windows (such as the launcher window)
+    //       are dropped.  Then after a moment, the device goes to sleep.  Oops.
+    //
+    //       Also notice how screenWasOff was being initialized using POLICY_FLAG_BRIGHT_HERE
+    //       instead of POLICY_FLAG_WOKE_HERE...
+    //
+    bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE;
+
+    int32_t action = entry->action;
+
+    // Update the touch state as needed based on the properties of the touch event.
+    int32_t injectionResult;
+    InjectionPermission injectionPermission;
+    if (action == AMOTION_EVENT_ACTION_DOWN) {
+        /* Case 1: ACTION_DOWN */
+
+        InputWindow* newTouchedWindow = NULL;
+        mTempTouchedOutsideTargets.clear();
+
+        int32_t x = int32_t(entry->firstSample.pointerCoords[0].x);
+        int32_t y = int32_t(entry->firstSample.pointerCoords[0].y);
+        InputWindow* topErrorWindow = NULL;
+        bool obscured = false;
+
+        // Traverse windows from front to back to find touched window and outside targets.
+        size_t numWindows = mWindows.size();
+        for (size_t i = 0; i < numWindows; i++) {
+            InputWindow* window = & mWindows.editItemAt(i);
+            int32_t flags = window->layoutParamsFlags;
+
+            if (flags & InputWindow::FLAG_SYSTEM_ERROR) {
+                if (! topErrorWindow) {
+                    topErrorWindow = window;
+                }
+            }
+
+            if (window->visible) {
+                if (! (flags & InputWindow::FLAG_NOT_TOUCHABLE)) {
+                    bool isTouchModal = (flags & (InputWindow::FLAG_NOT_FOCUSABLE
+                            | InputWindow::FLAG_NOT_TOUCH_MODAL)) == 0;
+                    if (isTouchModal || window->touchableAreaContainsPoint(x, y)) {
+                        if (! screenWasOff || flags & InputWindow::FLAG_TOUCHABLE_WHEN_WAKING) {
+                            newTouchedWindow = window;
+                            obscured = isWindowObscuredLocked(window);
+                        }
+                        break; // found touched window, exit window loop
+                    }
+                }
+
+                if (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH) {
+                    OutsideTarget outsideTarget;
+                    outsideTarget.window = window;
+                    outsideTarget.obscured = isWindowObscuredLocked(window);
+                    mTempTouchedOutsideTargets.push(outsideTarget);
+                }
+            }
+        }
+
+        // If there is an error window but it is not taking focus (typically because
+        // it is invisible) then wait for it.  Any other focused window may in
+        // fact be in ANR state.
+        if (topErrorWindow && newTouchedWindow != topErrorWindow) {
+#if DEBUG_FOCUS
+            LOGD("Waiting because system error window is pending.");
 #endif
+            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+                    NULL, NULL, nextWakeupTime);
+            injectionPermission = INJECTION_PERMISSION_UNKNOWN;
+            goto Unresponsive;
+        }
 
-    assert(eventEntry->dispatchInProgress); // should already have been set to true
+        // If we did not find a touched window then fail.
+        if (! newTouchedWindow) {
+            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());
+#endif
+                injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+                        mFocusedApplication, NULL, nextWakeupTime);
+                injectionPermission = INJECTION_PERMISSION_UNKNOWN;
+                goto Unresponsive;
+            }
 
-    for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
-        const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);
+            LOGI("Dropping event because there is no touched window or focused application.");
+            injectionResult = INPUT_EVENT_INJECTION_FAILED;
+            injectionPermission = INJECTION_PERMISSION_UNKNOWN;
+            goto Failed;
+        }
 
-        ssize_t connectionIndex = getConnectionIndex(inputTarget.inputChannel);
-        if (connectionIndex >= 0) {
-            sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
-            prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
-                    resumeWithAppendedMotionSample);
+        // Check permissions.
+        if (! checkInjectionPermission(newTouchedWindow, entry->injectorPid, entry->injectorUid)) {
+            injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+            injectionPermission = INJECTION_PERMISSION_DENIED;
+            goto Failed;
+        }
+
+        // If the touched window is paused then keep waiting.
+        if (newTouchedWindow->paused) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+            LOGD("Waiting because touched window is paused.");
+#endif
+            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+                    NULL, newTouchedWindow, nextWakeupTime);
+            injectionPermission = INJECTION_PERMISSION_GRANTED;
+            goto Unresponsive;
+        }
+
+        // Success!  Update the touch dispatch state for real.
+        releaseTouchedWindowLocked();
+
+        mTouchedWindow = newTouchedWindow;
+        mTouchedWindowIsObscured = obscured;
+
+        if (newTouchedWindow->hasWallpaper) {
+            mTouchedWallpaperWindows.appendVector(mWallpaperWindows);
+        }
+    } else {
+        /* Case 2: Everything but ACTION_DOWN */
+
+        // Check permissions.
+        if (! checkInjectionPermission(mTouchedWindow, entry->injectorPid, entry->injectorUid)) {
+            injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+            injectionPermission = INJECTION_PERMISSION_DENIED;
+            goto Failed;
+        }
+
+        // If the pointer is not currently down, then ignore the event.
+        if (! mTouchDown) {
+            LOGI("Dropping event because the pointer is not down.");
+            injectionResult = INPUT_EVENT_INJECTION_FAILED;
+            injectionPermission = INJECTION_PERMISSION_GRANTED;
+            goto Failed;
+        }
+
+        // If there is no currently touched window then fail.
+        if (! mTouchedWindow) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+            LOGD("Dropping event because there is no touched window to receive it.");
+#endif
+            injectionResult = INPUT_EVENT_INJECTION_FAILED;
+            injectionPermission = INJECTION_PERMISSION_GRANTED;
+            goto Failed;
+        }
+
+        // If the touched window is paused then keep waiting.
+        if (mTouchedWindow->paused) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+            LOGD("Waiting because touched window is paused.");
+#endif
+            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+                    NULL, mTouchedWindow, nextWakeupTime);
+            injectionPermission = INJECTION_PERMISSION_GRANTED;
+            goto Unresponsive;
+        }
+    }
+
+    // Success!  Output targets.
+    injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+    injectionPermission = INJECTION_PERMISSION_GRANTED;
+
+    {
+        size_t numWallpaperWindows = mTouchedWallpaperWindows.size();
+        for (size_t i = 0; i < numWallpaperWindows; i++) {
+            addWindowTargetLocked(mTouchedWallpaperWindows[i],
+                    InputTarget::FLAG_WINDOW_IS_OBSCURED, 0);
+        }
+
+        size_t numOutsideTargets = mTempTouchedOutsideTargets.size();
+        for (size_t i = 0; i < numOutsideTargets; i++) {
+            const OutsideTarget& outsideTarget = mTempTouchedOutsideTargets[i];
+            int32_t outsideTargetFlags = InputTarget::FLAG_OUTSIDE;
+            if (outsideTarget.obscured) {
+                outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+            }
+            addWindowTargetLocked(outsideTarget.window, outsideTargetFlags, 0);
+        }
+        mTempTouchedOutsideTargets.clear();
+
+        int32_t targetFlags = InputTarget::FLAG_SYNC;
+        if (mTouchedWindowIsObscured) {
+            targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+        }
+        addWindowTargetLocked(mTouchedWindow, targetFlags,
+                getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(currentTime));
+        *outWindow = mTouchedWindow;
+    }
+
+Failed:
+    // Check injection permission once and for all.
+    if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
+        if (checkInjectionPermission(action == AMOTION_EVENT_ACTION_DOWN ? NULL : mTouchedWindow,
+                entry->injectorPid, entry->injectorUid)) {
+            injectionPermission = INJECTION_PERMISSION_GRANTED;
         } else {
-            LOGW("Framework requested delivery of an input event to channel '%s' but it "
-                    "is not registered with the input dispatcher.",
-                    inputTarget.inputChannel->getName().string());
+            injectionPermission = INJECTION_PERMISSION_DENIED;
+        }
+    }
+
+    // Update final pieces of touch state if the injector had permission.
+    if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
+        if (action == AMOTION_EVENT_ACTION_DOWN) {
+            if (mTouchDown) {
+                // This is weird.  We got a down but we thought it was already down!
+                LOGW("Pointer down received while already down.");
+            } else {
+                mTouchDown = true;
+            }
+
+            if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+                // Since we failed to identify a target for this touch down, we may still
+                // be holding on to an earlier target from a previous touch down.  Release it.
+                releaseTouchedWindowLocked();
+            }
+        } else if (action == AMOTION_EVENT_ACTION_UP) {
+            mTouchDown = false;
+            releaseTouchedWindowLocked();
+        }
+    } else {
+        LOGW("Not updating touch focus because injection was denied.");
+    }
+
+Unresponsive:
+#if DEBUG_FOCUS
+    LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d",
+            injectionResult, injectionPermission);
+    logDispatchStateLocked();
+#endif
+    return injectionResult;
+}
+
+void InputDispatcher::releaseTouchedWindowLocked() {
+    mTouchedWindow = NULL;
+    mTouchedWindowIsObscured = false;
+    mTouchedWallpaperWindows.clear();
+}
+
+void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
+        nsecs_t timeSpentWaitingForApplication) {
+    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;
+}
+
+void InputDispatcher::addMonitoringTargetsLocked() {
+    for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+        mCurrentInputTargets.push();
+
+        InputTarget& target = mCurrentInputTargets.editTop();
+        target.inputChannel = mMonitoringChannels[i];
+        target.flags = 0;
+        target.timeout = -1;
+        target.timeSpentWaitingForApplication = 0;
+        target.xOffset = 0;
+        target.yOffset = 0;
+    }
+}
+
+bool InputDispatcher::checkInjectionPermission(const InputWindow* window,
+        int32_t injectorPid, int32_t injectorUid) {
+    if (injectorUid > 0 && (window == NULL || window->ownerUid != injectorUid)) {
+        bool result = mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
+        if (! result) {
+            if (window) {
+                LOGW("Permission denied: injecting event from pid %d uid %d to window "
+                        "with input channel %s owned by uid %d",
+                        injectorPid, injectorUid, window->inputChannel->getName().string(),
+                        window->ownerUid);
+            } else {
+                LOGW("Permission denied: injecting event from pid %d uid %d",
+                        injectorPid, injectorUid);
+            }
+            return false;
+        }
+    }
+    return true;
+}
+
+bool InputDispatcher::isWindowObscuredLocked(const InputWindow* window) {
+    size_t numWindows = mWindows.size();
+    for (size_t i = 0; i < numWindows; i++) {
+        const InputWindow* other = & mWindows.itemAt(i);
+        if (other == window) {
+            break;
+        }
+        if (other->visible && window->visibleFrameIntersects(other)) {
+            return true;
         }
     }
+    return false;
+}
+
+void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime,
+        int32_t windowType, int32_t eventType) {
+    CommandEntry* commandEntry = postCommandLocked(
+            & InputDispatcher::doPokeUserActivityLockedInterruptible);
+    commandEntry->eventTime = eventTime;
+    commandEntry->windowType = windowType;
+    commandEntry->userActivityEventType = eventType;
 }
 
 void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
@@ -523,15 +1258,21 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
             "xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s",
             connection->getInputChannelName(), inputTarget->flags, inputTarget->timeout,
             inputTarget->xOffset, inputTarget->yOffset,
-            resumeWithAppendedMotionSample ? "true" : "false");
+            toString(resumeWithAppendedMotionSample));
 #endif
 
     // Skip this event if the connection status is not normal.
-    // We don't want to queue outbound events at all if the connection is broken or
+    // We don't want to enqueue additional outbound events if the connection is broken or
     // not responding.
     if (connection->status != Connection::STATUS_NORMAL) {
-        LOGV("channel '%s' ~ Dropping event because the channel status is %s",
-                connection->getStatusLabel());
+        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;
     }
 
@@ -612,17 +1353,45 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
         }
     }
 
+    // Bring the input state back in line with reality in case it drifted off during an ANR.
+    if (connection->inputState.isOutOfSync()) {
+        mTempCancelationEvents.clear();
+        connection->inputState.synthesizeCancelationEvents(& mAllocator, mTempCancelationEvents);
+        connection->inputState.resetOutOfSync();
+
+        if (! mTempCancelationEvents.isEmpty()) {
+            LOGI("channel '%s' ~ Generated %d cancelation events to bring channel back in sync "
+                    "with reality.",
+                    connection->getInputChannelName(), mTempCancelationEvents.size());
+
+            for (size_t i = 0; i < mTempCancelationEvents.size(); i++) {
+                EventEntry* cancelationEventEntry = mTempCancelationEvents.itemAt(i);
+                switch (cancelationEventEntry->type) {
+                case EventEntry::TYPE_KEY:
+                    logOutboundKeyDetailsLocked("  ",
+                            static_cast<KeyEntry*>(cancelationEventEntry));
+                    break;
+                case EventEntry::TYPE_MOTION:
+                    logOutboundMotionDetailsLocked("  ",
+                            static_cast<MotionEntry*>(cancelationEventEntry));
+                    break;
+                }
+
+                DispatchEntry* cancelationDispatchEntry =
+                        mAllocator.obtainDispatchEntry(cancelationEventEntry,
+                        0, inputTarget->xOffset, inputTarget->yOffset, inputTarget->timeout);
+                connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry);
+
+                mAllocator.releaseEventEntry(cancelationEventEntry);
+            }
+        }
+    }
+
     // This is a new event.
     // Enqueue a new dispatch entry onto the outbound queue for this connection.
-    DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry); // increments ref
-    dispatchEntry->targetFlags = inputTarget->flags;
-    dispatchEntry->xOffset = inputTarget->xOffset;
-    dispatchEntry->yOffset = inputTarget->yOffset;
-    dispatchEntry->timeout = inputTarget->timeout;
-    dispatchEntry->inProgress = false;
-    dispatchEntry->headMotionSample = NULL;
-    dispatchEntry->tailMotionSample = NULL;
-
+    DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref
+            inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset,
+            inputTarget->timeout);
     if (dispatchEntry->isSyncTarget()) {
         eventEntry->pendingSyncDispatches += 1;
     }
@@ -647,12 +1416,13 @@ 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);
+        startDispatchCycleLocked(currentTime, connection,
+                inputTarget->timeSpentWaitingForApplication);
     }
 }
 
 void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
-        const sp<Connection>& connection) {
+        const sp<Connection>& connection, nsecs_t timeSpentWaitingForApplication) {
 #if DEBUG_DISPATCH_CYCLE
     LOGD("channel '%s' ~ startDispatchCycle",
             connection->getInputChannelName());
@@ -661,12 +1431,37 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
     assert(connection->status == Connection::STATUS_NORMAL);
     assert(! connection->outboundQueue.isEmpty());
 
-    DispatchEntry* dispatchEntry = connection->outboundQueue.head.next;
+    DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
     assert(! dispatchEntry->inProgress);
 
-    // TODO throttle successive ACTION_MOVE motion events for the same device
-    //      possible implementation could set a brief poll timeout here and resume starting the
-    //      dispatch cycle when elapsed
+    // Mark the dispatch entry as in progress.
+    dispatchEntry->inProgress = true;
+
+    // Update the connection's input state.
+    InputState::Consistency consistency = connection->inputState.trackEvent(
+            dispatchEntry->eventEntry);
+
+#if FILTER_INPUT_EVENTS
+    // Filter out inconsistent sequences of input events.
+    // The input system may drop or inject events in a way that could violate implicit
+    // invariants on input state and potentially cause an application to crash
+    // or think that a key or pointer is stuck down.  Technically we make no guarantees
+    // of consistency but it would be nice to improve on this where possible.
+    // XXX: This code is a proof of concept only.  Not ready for prime time.
+    if (consistency == InputState::TOLERABLE) {
+#if DEBUG_DISPATCH_CYCLE
+        LOGD("channel '%s' ~ Sending an event that is inconsistent with the connection's "
+                "current input state but that is likely to be tolerated by the application.",
+                connection->getInputChannelName());
+#endif
+    } else if (consistency == InputState::BROKEN) {
+        LOGI("channel '%s' ~ Dropping an event that is inconsistent with the connection's "
+                "current input state and that is likely to cause the application to crash.",
+                connection->getInputChannelName());
+        startNextDispatchCycleLocked(currentTime, connection);
+        return;
+    }
+#endif
 
     // Publish the event.
     status_t status;
@@ -790,12 +1585,10 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
     }
 
     // Record information about the newly started dispatch cycle.
-    dispatchEntry->inProgress = true;
-
     connection->lastEventTime = dispatchEntry->eventEntry->eventTime;
     connection->lastDispatchTime = currentTime;
 
-    nsecs_t timeout = dispatchEntry->timeout;
+    nsecs_t timeout = dispatchEntry->timeout - timeSpentWaitingForApplication;
     connection->setNextTimeoutTime(currentTime, timeout);
 
     // Notify other system components.
@@ -844,9 +1637,14 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
         return;
     }
 
+    startNextDispatchCycleLocked(currentTime, connection);
+}
+
+void InputDispatcher::startNextDispatchCycleLocked(nsecs_t currentTime,
+        const sp<Connection>& connection) {
     // Start the next dispatch cycle for this connection.
     while (! connection->outboundQueue.isEmpty()) {
-        DispatchEntry* dispatchEntry = connection->outboundQueue.head.next;
+        DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
         if (dispatchEntry->inProgress) {
              // Finish or resume current event in progress.
             if (dispatchEntry->tailMotionSample) {
@@ -855,7 +1653,7 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
                 dispatchEntry->inProgress = false;
                 dispatchEntry->headMotionSample = dispatchEntry->tailMotionSample;
                 dispatchEntry->tailMotionSample = NULL;
-                startDispatchCycleLocked(currentTime, connection);
+                startDispatchCycleLocked(currentTime, connection, 0);
                 return;
             }
             // Finished.
@@ -868,7 +1666,7 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
             // 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).
             // So just start the next event for this connection.
-            startDispatchCycleLocked(currentTime, connection);
+            startDispatchCycleLocked(currentTime, connection, 0);
             return;
         }
     }
@@ -884,58 +1682,74 @@ void InputDispatcher::timeoutDispatchCycleLocked(nsecs_t currentTime,
             connection->getInputChannelName());
 #endif
 
-    if (connection->status != Connection::STATUS_NORMAL) {
+    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;
     }
 
-    // Enter the not responding state.
-    connection->status = Connection::STATUS_NOT_RESPONDING;
-    connection->lastANRTime = currentTime;
-
     // Notify other system components.
-    // This enqueues a command which will eventually either call
-    // resumeAfterTimeoutDispatchCycleLocked or abortDispatchCycleLocked.
+    // 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",
-            connection->getInputChannelName());
+    LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked - newTimeout=%lld",
+            connection->getInputChannelName(), newTimeout);
 #endif
 
     if (connection->status != Connection::STATUS_NOT_RESPONDING) {
         return;
     }
 
-    // Resume normal dispatch.
-    connection->status = Connection::STATUS_NORMAL;
-    connection->setNextTimeoutTime(currentTime, newTimeout);
+    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
     LOGD("channel '%s' ~ abortDispatchCycle - broken=%s",
-            connection->getInputChannelName(), broken ? "true" : "false");
+            connection->getInputChannelName(), toString(broken));
 #endif
 
     // Clear the pending timeout.
     connection->nextTimeoutTime = LONG_LONG_MAX;
 
-    // Clear the outbound queue.
-    if (! connection->outboundQueue.isEmpty()) {
-        do {
-            DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead();
-            if (dispatchEntry->isSyncTarget()) {
-                decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
-            }
-            mAllocator.releaseDispatchEntry(dispatchEntry);
-        } while (! connection->outboundQueue.isEmpty());
+    // Input state will no longer be realistic.
+    connection->inputState.setOutOfSync();
 
-        deactivateConnectionLocked(connection.get());
-    }
+    // Clear the outbound queue.
+    drainOutboundQueueLocked(connection.get(), connection->outboundQueue.headSentinel.next);
 
     // Handle the case where the connection appears to be unrecoverably broken.
     // Ignore already broken or zombie connections.
@@ -950,6 +1764,26 @@ 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);
+        }
+        mAllocator.releaseDispatchEntry(dispatchEntry);
+
+        dispatchEntry = next;
+    }
+
+    if (connection->outboundQueue.isEmpty()) {
+        deactivateConnectionLocked(connection);
+    }
+}
+
 bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) {
     InputDispatcher* d = static_cast<InputDispatcher*>(data);
 
@@ -998,57 +1832,19 @@ bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* dat
 void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime);
-#endif
-
-    bool wasEmpty;
-    { // acquire lock
-        AutoMutex _l(mLock);
-
-        ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry(eventTime);
-
-        wasEmpty = mInboundQueue.isEmpty();
-        mInboundQueue.enqueueAtTail(newEntry);
-    } // release lock
-
-    if (wasEmpty) {
-        mPollLoop->wake();
-    }
-}
-
-void InputDispatcher::notifyAppSwitchComing(nsecs_t eventTime) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    LOGD("notifyAppSwitchComing - eventTime=%lld", eventTime);
-#endif
-
-    // Remove movement keys from the queue from most recent to least recent, stopping at the
-    // first non-movement key.
-    // TODO: Include a detailed description of why we do this...
-
-    { // acquire lock
-        AutoMutex _l(mLock);
-
-        for (EventEntry* entry = mInboundQueue.tail.prev; entry != & mInboundQueue.head; ) {
-            EventEntry* prev = entry->prev;
-
-            if (entry->type == EventEntry::TYPE_KEY) {
-                KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
-                if (isMovementKey(keyEntry->keyCode)) {
-                    LOGV("Dropping movement key during app switch: keyCode=%d, action=%d",
-                            keyEntry->keyCode, keyEntry->action);
-                    mInboundQueue.dequeue(keyEntry);
-
-                    setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
-
-                    mAllocator.releaseKeyEntry(keyEntry);
-                } else {
-                    // stop at last non-movement key
-                    break;
-                }
-            }
+#endif
 
-            entry = prev;
-        }
+    bool needWake;
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry(eventTime);
+        needWake = enqueueInboundEventLocked(newEntry);
     } // release lock
+
+    if (needWake) {
+        mPollLoop->wake();
+    }
 }
 
 void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source,
@@ -1061,7 +1857,7 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t sou
             keyCode, scanCode, metaState, downTime);
 #endif
 
-    bool wasEmpty;
+    bool needWake;
     { // acquire lock
         AutoMutex _l(mLock);
 
@@ -1070,11 +1866,10 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t sou
                 deviceId, source, policyFlags, action, flags, keyCode, scanCode,
                 metaState, repeatCount, downTime);
 
-        wasEmpty = mInboundQueue.isEmpty();
-        mInboundQueue.enqueueAtTail(newEntry);
+        needWake = enqueueInboundEventLocked(newEntry);
     } // release lock
 
-    if (wasEmpty) {
+    if (needWake) {
         mPollLoop->wake();
     }
 }
@@ -1101,7 +1896,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t
     }
 #endif
 
-    bool wasEmpty;
+    bool needWake;
     { // acquire lock
         AutoMutex _l(mLock);
 
@@ -1112,8 +1907,8 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t
             // Try to append a move sample to the tail of the inbound queue for this device.
             // Give up if we encounter a non-move motion event for this device since that
             // means we cannot append any new samples until a new motion event has started.
-            for (EventEntry* entry = mInboundQueue.tail.prev;
-                    entry != & mInboundQueue.head; entry = entry->prev) {
+            for (EventEntry* entry = mInboundQueue.tailSentinel.prev;
+                    entry != & mInboundQueue.headSentinel; entry = entry->prev) {
                 if (entry->type != EventEntry::TYPE_MOTION) {
                     // Keep looking for motion events.
                     continue;
@@ -1140,18 +1935,6 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t
                 LOGD("Appended motion sample onto batch for most recent "
                         "motion event for this device in the inbound queue.");
 #endif
-
-                // Sanity check for special case because dispatch is interruptible.
-                // The dispatch logic is partially interruptible and releases its lock while
-                // identifying targets.  However, as soon as the targets have been identified,
-                // the dispatcher proceeds to write a dispatch entry into all relevant outbound
-                // queues and then promptly removes the motion entry from the queue.
-                //
-                // Consequently, we should never observe the case where the inbound queue contains
-                // an in-progress motion entry unless the current input targets are invalid
-                // (currently being computed).  Check for this!
-                assert(! (motionEntry->dispatchInProgress && mCurrentInputTargetsValid));
-
                 return; // done!
             }
 
@@ -1178,7 +1961,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t
                 for (size_t i = 0; i < mActiveConnections.size(); i++) {
                     Connection* connection = mActiveConnections.itemAt(i);
                     if (! connection->outboundQueue.isEmpty()) {
-                        DispatchEntry* dispatchEntry = connection->outboundQueue.tail.prev;
+                        DispatchEntry* dispatchEntry = connection->outboundQueue.tailSentinel.prev;
                         if (dispatchEntry->isSyncTarget()) {
                             if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
                                 goto NoBatchingOrStreaming;
@@ -1220,11 +2003,10 @@ NoBatchingOrStreaming:;
                 xPrecision, yPrecision, downTime,
                 pointerCount, pointerIds, pointerCoords);
 
-        wasEmpty = mInboundQueue.isEmpty();
-        mInboundQueue.enqueueAtTail(newEntry);
+        needWake = enqueueInboundEventLocked(newEntry);
     } // release lock
 
-    if (wasEmpty) {
+    if (needWake) {
         mPollLoop->wake();
     }
 }
@@ -1240,11 +2022,15 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
     nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
 
     EventEntry* injectedEntry;
-    bool wasEmpty;
+    bool needWake;
     { // acquire lock
         AutoMutex _l(mLock);
 
-        injectedEntry = createEntryFromInputEventLocked(event);
+        injectedEntry = createEntryFromInjectedInputEventLocked(event);
+        if (! injectedEntry) {
+            return INPUT_EVENT_INJECTION_FAILED;
+        }
+
         injectedEntry->refCount += 1;
         injectedEntry->injectorPid = injectorPid;
         injectedEntry->injectorUid = injectorUid;
@@ -1253,12 +2039,10 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
             injectedEntry->injectionIsAsync = true;
         }
 
-        wasEmpty = mInboundQueue.isEmpty();
-        mInboundQueue.enqueueAtTail(injectedEntry);
-
+        needWake = enqueueInboundEventLocked(injectedEntry);
     } // release lock
 
-    if (wasEmpty) {
+    if (needWake) {
         mPollLoop->wake();
     }
 
@@ -1361,11 +2145,42 @@ void InputDispatcher::decrementPendingSyncDispatchesLocked(EventEntry* entry) {
     }
 }
 
-InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked(
+static bool isValidKeyAction(int32_t action) {
+    switch (action) {
+    case AKEY_EVENT_ACTION_DOWN:
+    case AKEY_EVENT_ACTION_UP:
+        return true;
+    default:
+        return false;
+    }
+}
+
+static bool isValidMotionAction(int32_t action) {
+    switch (action & AMOTION_EVENT_ACTION_MASK) {
+    case AMOTION_EVENT_ACTION_DOWN:
+    case AMOTION_EVENT_ACTION_UP:
+    case AMOTION_EVENT_ACTION_CANCEL:
+    case AMOTION_EVENT_ACTION_MOVE:
+    case AMOTION_EVENT_ACTION_POINTER_DOWN:
+    case AMOTION_EVENT_ACTION_POINTER_UP:
+    case AMOTION_EVENT_ACTION_OUTSIDE:
+        return true;
+    default:
+        return false;
+    }
+}
+
+InputDispatcher::EventEntry* InputDispatcher::createEntryFromInjectedInputEventLocked(
         const InputEvent* event) {
     switch (event->getType()) {
     case AINPUT_EVENT_TYPE_KEY: {
         const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event);
+        if (! isValidKeyAction(keyEvent->getAction())) {
+            LOGE("Dropping injected key event since it has invalid action code 0x%x",
+                    keyEvent->getAction());
+            return NULL;
+        }
+
         uint32_t policyFlags = POLICY_FLAG_INJECTED;
 
         KeyEntry* keyEntry = mAllocator.obtainKeyEntry(keyEvent->getEventTime(),
@@ -1378,6 +2193,17 @@ InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked(
 
     case AINPUT_EVENT_TYPE_MOTION: {
         const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
+        if (! isValidMotionAction(motionEvent->getAction())) {
+            LOGE("Dropping injected motion event since it has invalid action code 0x%x.",
+                    motionEvent->getAction());
+            return NULL;
+        }
+        if (motionEvent->getPointerCount() == 0
+                || motionEvent->getPointerCount() > MAX_POINTERS) {
+            LOGE("Dropping injected motion event since it has an invalid pointer count %d.",
+                    motionEvent->getPointerCount());
+        }
+
         uint32_t policyFlags = POLICY_FLAG_INJECTED;
 
         const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
@@ -1405,33 +2231,143 @@ InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked(
     }
 }
 
-void InputDispatcher::resetKeyRepeatLocked() {
-    if (mKeyRepeatState.lastKeyEntry) {
-        mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry);
-        mKeyRepeatState.lastKeyEntry = NULL;
+void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {
+#if DEBUG_FOCUS
+    LOGD("setInputWindows");
+#endif
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        sp<InputChannel> touchedWindowChannel;
+        if (mTouchedWindow) {
+            touchedWindowChannel = mTouchedWindow->inputChannel;
+            mTouchedWindow = NULL;
+        }
+        size_t numTouchedWallpapers = mTouchedWallpaperWindows.size();
+        if (numTouchedWallpapers != 0) {
+            for (size_t i = 0; i < numTouchedWallpapers; i++) {
+                mTempTouchedWallpaperChannels.push(mTouchedWallpaperWindows[i]->inputChannel);
+            }
+            mTouchedWallpaperWindows.clear();
+        }
+
+        bool hadFocusedWindow = mFocusedWindow != NULL;
+
+        mFocusedWindow = NULL;
+        mWallpaperWindows.clear();
+
+        mWindows.clear();
+        mWindows.appendVector(inputWindows);
+
+        size_t numWindows = mWindows.size();
+        for (size_t i = 0; i < numWindows; i++) {
+            InputWindow* window = & mWindows.editItemAt(i);
+            if (window->hasFocus) {
+                mFocusedWindow = window;
+            }
+
+            if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) {
+                mWallpaperWindows.push(window);
+
+                for (size_t j = 0; j < numTouchedWallpapers; j++) {
+                    if (window->inputChannel == mTempTouchedWallpaperChannels[i]) {
+                        mTouchedWallpaperWindows.push(window);
+                    }
+                }
+            }
+
+            if (window->inputChannel == touchedWindowChannel) {
+                mTouchedWindow = window;
+            }
+        }
+
+        mTempTouchedWallpaperChannels.clear();
+
+        if ((hadFocusedWindow && ! mFocusedWindow)
+                || (mFocusedWindow && ! mFocusedWindow->visible)) {
+            preemptInputDispatchInnerLocked();
+        }
+
+#if DEBUG_FOCUS
+        logDispatchStateLocked();
+#endif
+    } // release lock
+
+    // Wake up poll loop since it may need to make new input dispatching choices.
+    mPollLoop->wake();
+}
+
+void InputDispatcher::setFocusedApplication(const InputApplication* inputApplication) {
+#if DEBUG_FOCUS
+    LOGD("setFocusedApplication");
+#endif
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        releaseFocusedApplicationLocked();
+
+        if (inputApplication) {
+            mFocusedApplicationStorage = *inputApplication;
+            mFocusedApplication = & mFocusedApplicationStorage;
+        }
+
+#if DEBUG_FOCUS
+        logDispatchStateLocked();
+#endif
+    } // release lock
+
+    // Wake up poll loop since it may need to make new input dispatching choices.
+    mPollLoop->wake();
+}
+
+void InputDispatcher::releaseFocusedApplicationLocked() {
+    if (mFocusedApplication) {
+        mFocusedApplication = NULL;
+        mFocusedApplicationStorage.handle.clear();
     }
 }
 
-void InputDispatcher::preemptInputDispatch() {
-#if DEBUG_DISPATCH_CYCLE
-    LOGD("preemptInputDispatch");
+void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
+#if DEBUG_FOCUS
+    LOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
 #endif
 
-    bool preemptedOne = false;
+    bool changed;
     { // acquire lock
         AutoMutex _l(mLock);
 
-        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->outboundQueue.tail.prev->targetFlags &= ~ InputTarget::FLAG_SYNC;
-                preemptedOne = true;
+        if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
+            if (mDispatchFrozen && ! frozen) {
+                resetANRTimeoutsLocked();
             }
+
+            mDispatchEnabled = enabled;
+            mDispatchFrozen = frozen;
+            changed = true;
+        } else {
+            changed = false;
         }
+
+#if DEBUG_FOCUS
+        logDispatchStateLocked();
+#endif
+    } // release lock
+
+    if (changed) {
+        // Wake up poll loop since it may need to make new input dispatching choices.
+        mPollLoop->wake();
+    }
+}
+
+void InputDispatcher::preemptInputDispatch() {
+#if DEBUG_FOCUS
+    LOGD("preemptInputDispatch");
+#endif
+
+    bool preemptedOne;
+    { // acquire lock
+        AutoMutex _l(mLock);
+        preemptedOne = preemptInputDispatchInnerLocked();
     } // release lock
 
     if (preemptedOne) {
@@ -1440,9 +2376,99 @@ void InputDispatcher::preemptInputDispatch() {
     }
 }
 
-status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
+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);
+    LOGD("%s", dump.string());
+}
+
+void InputDispatcher::dumpDispatchStateLocked(String8& dump) {
+    dump.appendFormat("  dispatchEnabled: %d\n", mDispatchEnabled);
+    dump.appendFormat("  dispatchFrozen: %d\n", mDispatchFrozen);
+
+    if (mFocusedApplication) {
+        dump.appendFormat("  focusedApplication: name='%s', dispatchingTimeout=%0.3fms\n",
+                mFocusedApplication->name.string(),
+                mFocusedApplication->dispatchingTimeout / 1000000.0);
+    } else {
+        dump.append("  focusedApplication: <null>\n");
+    }
+    dump.appendFormat("  focusedWindow: '%s'\n",
+            mFocusedWindow != NULL ? mFocusedWindow->inputChannel->getName().string() : "<null>");
+    dump.appendFormat("  touchedWindow: '%s', touchDown=%d\n",
+            mTouchedWindow != NULL ? mTouchedWindow->inputChannel->getName().string() : "<null>",
+            mTouchDown);
+    for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) {
+        dump.appendFormat("  touchedWallpaperWindows[%d]: '%s'\n",
+                i, mTouchedWallpaperWindows[i]->inputChannel->getName().string());
+    }
+    for (size_t i = 0; i < mWindows.size(); i++) {
+        dump.appendFormat("  windows[%d]: '%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
+                "visible=%s, flags=0x%08x, type=0x%08x, "
+                "frame=[%d,%d][%d,%d], "
+                "visibleFrame=[%d,%d][%d,%d], "
+                "touchableArea=[%d,%d][%d,%d], "
+                "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
+                i, mWindows[i].inputChannel->getName().string(),
+                toString(mWindows[i].paused),
+                toString(mWindows[i].hasFocus),
+                toString(mWindows[i].hasWallpaper),
+                toString(mWindows[i].visible),
+                mWindows[i].layoutParamsFlags, mWindows[i].layoutParamsType,
+                mWindows[i].frameLeft, mWindows[i].frameTop,
+                mWindows[i].frameRight, mWindows[i].frameBottom,
+                mWindows[i].visibleFrameLeft, mWindows[i].visibleFrameTop,
+                mWindows[i].visibleFrameRight, mWindows[i].visibleFrameBottom,
+                mWindows[i].touchableAreaLeft, mWindows[i].touchableAreaTop,
+                mWindows[i].touchableAreaRight, mWindows[i].touchableAreaBottom,
+                mWindows[i].ownerPid, mWindows[i].ownerUid,
+                mWindows[i].dispatchingTimeout / 1000000.0);
+    }
+
+    for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+        const sp<InputChannel>& channel = mMonitoringChannels[i];
+        dump.appendFormat("  monitoringChannel[%d]: '%s'\n",
+                i, channel->getName().string());
+    }
+
+    for (size_t i = 0; i < mActiveConnections.size(); i++) {
+        const Connection* connection = mActiveConnections[i];
+        dump.appendFormat("  activeConnection[%d]: '%s', status=%s, hasPendingSyncTarget=%s, "
+                "inputState.isNeutral=%s, inputState.isOutOfSync=%s\n",
+                i, connection->getInputChannelName(), connection->getStatusLabel(),
+                toString(connection->hasPendingSyncTarget()),
+                toString(connection->inputState.isNeutral()),
+                toString(connection->inputState.isOutOfSync()));
+    }
+
+    if (isAppSwitchPendingLocked()) {
+        dump.appendFormat("  appSwitch: pending, due in %01.1fms\n",
+                (mAppSwitchDueTime - now()) / 1000000.0);
+    } else {
+        dump.append("  appSwitch: not pending\n");
+    }
+}
+
+status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) {
 #if DEBUG_REGISTRATION
-    LOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().string());
+    LOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().string(),
+            toString(monitor));
 #endif
 
     { // acquire lock
@@ -1465,6 +2491,10 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan
         int32_t receiveFd = inputChannel->getReceivePipeFd();
         mConnectionsByReceiveFd.add(receiveFd, connection);
 
+        if (monitor) {
+            mMonitoringChannels.push(inputChannel);
+        }
+
         mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
 
         runCommandsLockedInterruptible();
@@ -1492,6 +2522,13 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh
 
         connection->status = Connection::STATUS_ZOMBIE;
 
+        for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+            if (mMonitoringChannels[i] == inputChannel) {
+                mMonitoringChannels.removeAt(i);
+                break;
+            }
+        }
+
         mPollLoop->removeCallback(inputChannel->getReceivePipeFd());
 
         nsecs_t currentTime = now();
@@ -1578,6 +2615,15 @@ void InputDispatcher::onDispatchCycleBrokenLocked(
     commandEntry->connection = connection;
 }
 
+void InputDispatcher::doNotifyConfigurationChangedInterruptible(
+        CommandEntry* commandEntry) {
+    mLock.unlock();
+
+    mPolicy->notifyConfigurationChanged(commandEntry->eventTime);
+
+    mLock.lock();
+}
+
 void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(
         CommandEntry* commandEntry) {
     sp<Connection> connection = commandEntry->connection;
@@ -1598,17 +2644,12 @@ void InputDispatcher::doNotifyInputChannelANRLockedInterruptible(
     if (connection->status != Connection::STATUS_ZOMBIE) {
         mLock.unlock();
 
-        nsecs_t newTimeout;
-        bool resume = mPolicy->notifyInputChannelANR(connection->inputChannel, newTimeout);
+        nsecs_t newTimeout = mPolicy->notifyInputChannelANR(connection->inputChannel);
 
         mLock.lock();
 
         nsecs_t currentTime = now();
-        if (resume) {
-            resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout);
-        } else {
-            abortDispatchCycleLocked(currentTime, connection, false /*(not) broken*/);
-        }
+        resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout);
     }
 }
 
@@ -1625,6 +2666,57 @@ void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible(
     }
 }
 
+void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
+        CommandEntry* commandEntry) {
+    KeyEntry* entry = commandEntry->keyEntry;
+    mReusableKeyEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags,
+            entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
+            entry->downTime, entry->eventTime);
+
+    mLock.unlock();
+
+    bool consumed = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputChannel,
+            & mReusableKeyEvent, entry->policyFlags);
+
+    mLock.lock();
+
+    entry->interceptKeyResult = consumed
+            ? KeyEntry::INTERCEPT_KEY_RESULT_SKIP
+            : KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
+    mAllocator.releaseKeyEntry(entry);
+}
+
+void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
+    mLock.unlock();
+
+    mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->windowType,
+            commandEntry->userActivityEventType);
+
+    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::dump(String8& dump) {
+    dumpDispatchStateLocked(dump);
+}
+
 
 // --- InputDispatcher::Allocator ---
 
@@ -1668,6 +2760,8 @@ InputDispatcher::KeyEntry* InputDispatcher::Allocator::obtainKeyEntry(nsecs_t ev
     entry->metaState = metaState;
     entry->repeatCount = repeatCount;
     entry->downTime = downTime;
+    entry->syntheticRepeat = false;
+    entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
     return entry;
 }
 
@@ -1702,10 +2796,18 @@ InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsec
 }
 
 InputDispatcher::DispatchEntry* InputDispatcher::Allocator::obtainDispatchEntry(
-        EventEntry* eventEntry) {
+        EventEntry* eventEntry,
+        int32_t targetFlags, float xOffset, float yOffset, nsecs_t timeout) {
     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;
     return entry;
 }
 
@@ -1788,6 +2890,25 @@ void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry,
     motionEntry->lastSample = sample;
 }
 
+
+// --- InputDispatcher::EventEntry ---
+
+void InputDispatcher::EventEntry::recycle() {
+    injectionResult = INPUT_EVENT_INJECTION_PENDING;
+    dispatchInProgress = false;
+    pendingSyncDispatches = 0;
+}
+
+
+// --- InputDispatcher::KeyEntry ---
+
+void InputDispatcher::KeyEntry::recycle() {
+    EventEntry::recycle();
+    syntheticRepeat = false;
+    interceptKeyResult = INTERCEPT_KEY_RESULT_UNKNOWN;
+}
+
+
 // --- InputDispatcher::MotionEntry ---
 
 uint32_t InputDispatcher::MotionEntry::countSamples() const {
@@ -1798,6 +2919,189 @@ uint32_t InputDispatcher::MotionEntry::countSamples() const {
     return count;
 }
 
+
+// --- InputDispatcher::InputState ---
+
+InputDispatcher::InputState::InputState() :
+        mIsOutOfSync(false) {
+}
+
+InputDispatcher::InputState::~InputState() {
+}
+
+bool InputDispatcher::InputState::isNeutral() const {
+    return mKeyMementos.isEmpty() && mMotionMementos.isEmpty();
+}
+
+bool InputDispatcher::InputState::isOutOfSync() const {
+    return mIsOutOfSync;
+}
+
+void InputDispatcher::InputState::setOutOfSync() {
+    if (! isNeutral()) {
+        mIsOutOfSync = true;
+    }
+}
+
+void InputDispatcher::InputState::resetOutOfSync() {
+    mIsOutOfSync = false;
+}
+
+InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackEvent(
+        const EventEntry* entry) {
+    switch (entry->type) {
+    case EventEntry::TYPE_KEY:
+        return trackKey(static_cast<const KeyEntry*>(entry));
+
+    case EventEntry::TYPE_MOTION:
+        return trackMotion(static_cast<const MotionEntry*>(entry));
+
+    default:
+        return CONSISTENT;
+    }
+}
+
+InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackKey(
+        const KeyEntry* entry) {
+    int32_t action = entry->action;
+    for (size_t i = 0; i < mKeyMementos.size(); i++) {
+        KeyMemento& memento = mKeyMementos.editItemAt(i);
+        if (memento.deviceId == entry->deviceId
+                && memento.source == entry->source
+                && memento.keyCode == entry->keyCode
+                && memento.scanCode == entry->scanCode) {
+            switch (action) {
+            case AKEY_EVENT_ACTION_UP:
+                mKeyMementos.removeAt(i);
+                if (isNeutral()) {
+                    mIsOutOfSync = false;
+                }
+                return CONSISTENT;
+
+            case AKEY_EVENT_ACTION_DOWN:
+                return TOLERABLE;
+
+            default:
+                return BROKEN;
+            }
+        }
+    }
+
+    switch (action) {
+    case AKEY_EVENT_ACTION_DOWN: {
+        mKeyMementos.push();
+        KeyMemento& memento = mKeyMementos.editTop();
+        memento.deviceId = entry->deviceId;
+        memento.source = entry->source;
+        memento.keyCode = entry->keyCode;
+        memento.scanCode = entry->scanCode;
+        memento.downTime = entry->downTime;
+        return CONSISTENT;
+    }
+
+    default:
+        return BROKEN;
+    }
+}
+
+InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackMotion(
+        const MotionEntry* entry) {
+    int32_t action = entry->action & AMOTION_EVENT_ACTION_MASK;
+    for (size_t i = 0; i < mMotionMementos.size(); i++) {
+        MotionMemento& memento = mMotionMementos.editItemAt(i);
+        if (memento.deviceId == entry->deviceId
+                && memento.source == entry->source) {
+            switch (action) {
+            case AMOTION_EVENT_ACTION_UP:
+            case AMOTION_EVENT_ACTION_CANCEL:
+                mMotionMementos.removeAt(i);
+                if (isNeutral()) {
+                    mIsOutOfSync = false;
+                }
+                return CONSISTENT;
+
+            case AMOTION_EVENT_ACTION_DOWN:
+                return TOLERABLE;
+
+            case AMOTION_EVENT_ACTION_POINTER_DOWN:
+                if (entry->pointerCount == memento.pointerCount + 1) {
+                    memento.setPointers(entry);
+                    return CONSISTENT;
+                }
+                return BROKEN;
+
+            case AMOTION_EVENT_ACTION_POINTER_UP:
+                if (entry->pointerCount == memento.pointerCount - 1) {
+                    memento.setPointers(entry);
+                    return CONSISTENT;
+                }
+                return BROKEN;
+
+            case AMOTION_EVENT_ACTION_MOVE:
+                if (entry->pointerCount == memento.pointerCount) {
+                    return CONSISTENT;
+                }
+                return BROKEN;
+
+            default:
+                return BROKEN;
+            }
+        }
+    }
+
+    switch (action) {
+    case AMOTION_EVENT_ACTION_DOWN: {
+        mMotionMementos.push();
+        MotionMemento& memento = mMotionMementos.editTop();
+        memento.deviceId = entry->deviceId;
+        memento.source = entry->source;
+        memento.xPrecision = entry->xPrecision;
+        memento.yPrecision = entry->yPrecision;
+        memento.downTime = entry->downTime;
+        memento.setPointers(entry);
+        return CONSISTENT;
+    }
+
+    default:
+        return BROKEN;
+    }
+}
+
+void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) {
+    pointerCount = entry->pointerCount;
+    for (uint32_t i = 0; i < entry->pointerCount; i++) {
+        pointerIds[i] = entry->pointerIds[i];
+        pointerCoords[i] = entry->lastSample->pointerCoords[i];
+    }
+}
+
+void InputDispatcher::InputState::synthesizeCancelationEvents(
+        Allocator* allocator, Vector<EventEntry*>& outEvents) const {
+    for (size_t i = 0; i < mKeyMementos.size(); i++) {
+        const KeyMemento& memento = mKeyMementos.itemAt(i);
+        outEvents.push(allocator->obtainKeyEntry(now(),
+                memento.deviceId, memento.source, 0,
+                AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_CANCELED,
+                memento.keyCode, memento.scanCode, 0, 0, memento.downTime));
+    }
+
+    for (size_t i = 0; i < mMotionMementos.size(); i++) {
+        const MotionMemento& memento = mMotionMementos.itemAt(i);
+        outEvents.push(allocator->obtainMotionEntry(now(),
+                memento.deviceId, memento.source, 0,
+                AMOTION_EVENT_ACTION_CANCEL, 0, 0, 0,
+                memento.xPrecision, memento.yPrecision, memento.downTime,
+                memento.pointerCount, memento.pointerIds, memento.pointerCoords));
+    }
+}
+
+void InputDispatcher::InputState::clear() {
+    mKeyMementos.clear();
+    mMotionMementos.clear();
+    mIsOutOfSync = false;
+}
+
+
 // --- InputDispatcher::Connection ---
 
 InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) :
@@ -1818,6 +3122,14 @@ void InputDispatcher::Connection::setNextTimeoutTime(nsecs_t currentTime, nsecs_
     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:
@@ -1839,8 +3151,8 @@ const char* InputDispatcher::Connection::getStatusLabel() const {
 
 InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchEntryForEvent(
         const EventEntry* eventEntry) const {
-    for (DispatchEntry* dispatchEntry = outboundQueue.tail.prev;
-            dispatchEntry != & outboundQueue.head; dispatchEntry = dispatchEntry->prev) {
+    for (DispatchEntry* dispatchEntry = outboundQueue.tailSentinel.prev;
+            dispatchEntry != & outboundQueue.headSentinel; dispatchEntry = dispatchEntry->prev) {
         if (dispatchEntry->eventEntry == eventEntry) {
             return dispatchEntry;
         }
@@ -1848,9 +3160,11 @@ InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchE
     return NULL;
 }
 
+
 // --- InputDispatcher::CommandEntry ---
 
-InputDispatcher::CommandEntry::CommandEntry() {
+InputDispatcher::CommandEntry::CommandEntry() :
+    keyEntry(NULL) {
 }
 
 InputDispatcher::CommandEntry::~CommandEntry() {
index ed4f07b..09fce38 100644 (file)
@@ -72,52 +72,12 @@ status_t InputManager::stop() {
     return OK;
 }
 
-status_t InputManager::registerInputChannel(const sp<InputChannel>& inputChannel) {
-    return mDispatcher->registerInputChannel(inputChannel);
+sp<InputReaderInterface> InputManager::getReader() {
+    return mReader;
 }
 
-status_t InputManager::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
-    return mDispatcher->unregisterInputChannel(inputChannel);
-}
-
-int32_t InputManager::injectInputEvent(const InputEvent* event,
-        int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
-    return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis);
-}
-
-void InputManager::preemptInputDispatch() {
-    mDispatcher->preemptInputDispatch();
-}
-
-void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) {
-    mReader->getInputConfiguration(outConfiguration);
-}
-
-status_t InputManager::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) {
-    return mReader->getInputDeviceInfo(deviceId, outDeviceInfo);
-}
-
-void InputManager::getInputDeviceIds(Vector<int32_t>& outDeviceIds) {
-    mReader->getInputDeviceIds(outDeviceIds);
-}
-
-int32_t InputManager::getScanCodeState(int32_t deviceId, uint32_t sourceMask,
-        int32_t scanCode) {
-    return mReader->getScanCodeState(deviceId, sourceMask, scanCode);
-}
-
-int32_t InputManager::getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
-        int32_t keyCode) {
-    return mReader->getKeyCodeState(deviceId, sourceMask, keyCode);
-}
-
-int32_t InputManager::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) {
-    return mReader->getSwitchState(deviceId, sourceMask, sw);
-}
-
-bool InputManager::hasKeys(int32_t deviceId, uint32_t sourceMask,
-        size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) {
-    return mReader->hasKeys(deviceId, sourceMask, numCodes, keyCodes, outFlags);
+sp<InputDispatcherInterface> InputManager::getDispatcher() {
+    return mDispatcher;
 }
 
 } // namespace android
index d57b38c..88084c0 100644 (file)
@@ -573,6 +573,60 @@ bool InputReader::markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, s
     } // release device registy reader lock
 }
 
+void InputReader::dump(String8& dump) {
+    dumpDeviceInfo(dump);
+}
+
+static void dumpMotionRange(String8& dump,
+        const char* name, const InputDeviceInfo::MotionRange* range) {
+    if (range) {
+        dump.appendFormat("      %s = { min: %0.3f, max: %0.3f, flat: %0.3f, fuzz: %0.3f }\n",
+                name, range->min, range->max, range->flat, range->fuzz);
+    }
+}
+
+#define DUMP_MOTION_RANGE(range) \
+    dumpMotionRange(dump, #range, deviceInfo.getMotionRange(AINPUT_MOTION_RANGE_##range));
+
+void InputReader::dumpDeviceInfo(String8& dump) {
+    Vector<int32_t> deviceIds;
+    getInputDeviceIds(deviceIds);
+
+    InputDeviceInfo deviceInfo;
+    for (size_t i = 0; i < deviceIds.size(); i++) {
+        int32_t deviceId = deviceIds[i];
+
+        status_t result = getInputDeviceInfo(deviceId, & deviceInfo);
+        if (result == NAME_NOT_FOUND) {
+            continue;
+        } else if (result != OK) {
+            dump.appendFormat("  ** Unexpected error %d getting information about input devices.\n",
+                    result);
+            continue;
+        }
+
+        dump.appendFormat("  Device %d: '%s'\n",
+                deviceInfo.getId(), deviceInfo.getName().string());
+        dump.appendFormat("    sources = 0x%08x\n",
+                deviceInfo.getSources());
+        dump.appendFormat("    keyboardType = %d\n",
+                deviceInfo.getKeyboardType());
+
+        dump.append("    motion ranges:\n");
+        DUMP_MOTION_RANGE(X);
+        DUMP_MOTION_RANGE(Y);
+        DUMP_MOTION_RANGE(PRESSURE);
+        DUMP_MOTION_RANGE(SIZE);
+        DUMP_MOTION_RANGE(TOUCH_MAJOR);
+        DUMP_MOTION_RANGE(TOUCH_MINOR);
+        DUMP_MOTION_RANGE(TOOL_MAJOR);
+        DUMP_MOTION_RANGE(TOOL_MINOR);
+        DUMP_MOTION_RANGE(ORIENTATION);
+    }
+}
+
+#undef DUMP_MOTION_RANGE
+
 
 // --- InputReaderThread ---
 
@@ -740,10 +794,6 @@ int32_t InputMapper::getMetaState() {
 }
 
 bool InputMapper::applyStandardPolicyActions(nsecs_t when, int32_t policyActions) {
-    if (policyActions & InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING) {
-        getDispatcher()->notifyAppSwitchComing(when);
-    }
-
     return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH;
 }
 
@@ -1249,20 +1299,12 @@ void TouchInputMapper::initializeLocked() {
     mLocked.orientedRanges.haveOrientation = false;
 }
 
-static void logAxisInfo(RawAbsoluteAxisInfo axis, const char* name) {
-    if (axis.valid) {
-        LOGI(INDENT "Raw %s axis: min=%d, max=%d, flat=%d, fuzz=%d",
-                name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz);
-    } else {
-        LOGI(INDENT "Raw %s axis: unknown range", name);
-    }
-}
-
 void TouchInputMapper::configure() {
     InputMapper::configure();
 
     // Configure basic parameters.
     configureParameters();
+    logParameters();
 
     // Configure absolute axis information.
     configureRawAxes();
@@ -1287,6 +1329,18 @@ void TouchInputMapper::configureParameters() {
     mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents();
 }
 
+void TouchInputMapper::logParameters() {
+    if (mParameters.useBadTouchFilter) {
+        LOGI(INDENT "Bad touch filter enabled.");
+    }
+    if (mParameters.useAveragingTouchFilter) {
+        LOGI(INDENT "Averaging touch filter enabled.");
+    }
+    if (mParameters.useJumpyTouchFilter) {
+        LOGI(INDENT "Jumpy touch filter enabled.");
+    }
+}
+
 void TouchInputMapper::configureRawAxes() {
     mRawAxes.x.clear();
     mRawAxes.y.clear();
@@ -1298,6 +1352,15 @@ void TouchInputMapper::configureRawAxes() {
     mRawAxes.orientation.clear();
 }
 
+static void logAxisInfo(RawAbsoluteAxisInfo axis, const char* name) {
+    if (axis.valid) {
+        LOGI(INDENT "Raw %s axis: min=%d, max=%d, flat=%d, fuzz=%d",
+                name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz);
+    } else {
+        LOGI(INDENT "Raw %s axis: unknown range", name);
+    }
+}
+
 void TouchInputMapper::logRawAxes() {
     logAxisInfo(mRawAxes.x, "x");
     logAxisInfo(mRawAxes.y, "y");
@@ -1331,8 +1394,10 @@ bool TouchInputMapper::configureSurfaceLocked() {
 
     bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height;
     if (sizeChanged) {
-        LOGI("Device configured: id=0x%x, name=%s (display size was changed)",
+        LOGI("Device reconfigured (display size changed): id=0x%x, name=%s",
                 getDeviceId(), getDeviceName().string());
+        LOGI(INDENT "Width: %dpx", width);
+        LOGI(INDENT "Height: %dpx", height);
 
         mLocked.surfaceWidth = width;
         mLocked.surfaceHeight = height;
@@ -1500,9 +1565,41 @@ bool TouchInputMapper::configureSurfaceLocked() {
         mLocked.orientedRanges.y.fuzz = orientedYScale;
     }
 
+    if (sizeChanged) {
+        logMotionRangesLocked();
+    }
+
     return true;
 }
 
+static void logMotionRangeInfo(InputDeviceInfo::MotionRange* range, const char* name) {
+    if (range) {
+        LOGI(INDENT "Output %s range: min=%f, max=%f, flat=%f, fuzz=%f",
+                name, range->min, range->max, range->flat, range->fuzz);
+    } else {
+        LOGI(INDENT "Output %s range: unsupported", name);
+    }
+}
+
+void TouchInputMapper::logMotionRangesLocked() {
+    logMotionRangeInfo(& mLocked.orientedRanges.x, "x");
+    logMotionRangeInfo(& mLocked.orientedRanges.y, "y");
+    logMotionRangeInfo(mLocked.orientedRanges.havePressure
+            ? & mLocked.orientedRanges.pressure : NULL, "pressure");
+    logMotionRangeInfo(mLocked.orientedRanges.haveSize
+            ? & mLocked.orientedRanges.size : NULL, "size");
+    logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea
+            ? & mLocked.orientedRanges.touchMajor : NULL, "touchMajor");
+    logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea
+            ? & mLocked.orientedRanges.touchMinor : NULL, "touchMinor");
+    logMotionRangeInfo(mLocked.orientedRanges.haveToolArea
+            ? & mLocked.orientedRanges.toolMajor : NULL, "toolMajor");
+    logMotionRangeInfo(mLocked.orientedRanges.haveToolArea
+            ? & mLocked.orientedRanges.toolMinor : NULL, "toolMinor");
+    logMotionRangeInfo(mLocked.orientedRanges.haveOrientation
+            ? & mLocked.orientedRanges.orientation : NULL, "orientation");
+}
+
 void TouchInputMapper::configureVirtualKeysLocked() {
     assert(mRawAxes.x.valid && mRawAxes.y.valid);
 
@@ -1768,16 +1865,18 @@ void TouchInputMapper::resolveCalibration() {
 }
 
 void TouchInputMapper::logCalibration() {
+    LOGI(INDENT "Calibration:");
+
     // Touch Area
     switch (mCalibration.touchAreaCalibration) {
     case Calibration::TOUCH_AREA_CALIBRATION_NONE:
-        LOGI(INDENT "  touch.touchArea.calibration: none");
+        LOGI(INDENT INDENT "touch.touchArea.calibration: none");
         break;
     case Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC:
-        LOGI(INDENT "  touch.touchArea.calibration: geometric");
+        LOGI(INDENT INDENT "touch.touchArea.calibration: geometric");
         break;
     case Calibration::TOUCH_AREA_CALIBRATION_PRESSURE:
-        LOGI(INDENT "  touch.touchArea.calibration: pressure");
+        LOGI(INDENT INDENT "touch.touchArea.calibration: pressure");
         break;
     default:
         assert(false);
@@ -1786,40 +1885,40 @@ void TouchInputMapper::logCalibration() {
     // Tool Area
     switch (mCalibration.toolAreaCalibration) {
     case Calibration::TOOL_AREA_CALIBRATION_NONE:
-        LOGI(INDENT "  touch.toolArea.calibration: none");
+        LOGI(INDENT INDENT "touch.toolArea.calibration: none");
         break;
     case Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC:
-        LOGI(INDENT "  touch.toolArea.calibration: geometric");
+        LOGI(INDENT INDENT "touch.toolArea.calibration: geometric");
         break;
     case Calibration::TOOL_AREA_CALIBRATION_LINEAR:
-        LOGI(INDENT "  touch.toolArea.calibration: linear");
+        LOGI(INDENT INDENT "touch.toolArea.calibration: linear");
         break;
     default:
         assert(false);
     }
 
     if (mCalibration.haveToolAreaLinearScale) {
-        LOGI(INDENT "  touch.toolArea.linearScale: %f", mCalibration.toolAreaLinearScale);
+        LOGI(INDENT INDENT "touch.toolArea.linearScale: %f", mCalibration.toolAreaLinearScale);
     }
 
     if (mCalibration.haveToolAreaLinearBias) {
-        LOGI(INDENT "  touch.toolArea.linearBias: %f", mCalibration.toolAreaLinearBias);
+        LOGI(INDENT INDENT "touch.toolArea.linearBias: %f", mCalibration.toolAreaLinearBias);
     }
 
     if (mCalibration.haveToolAreaIsSummed) {
-        LOGI(INDENT "  touch.toolArea.isSummed: %d", mCalibration.toolAreaIsSummed);
+        LOGI(INDENT INDENT "touch.toolArea.isSummed: %d", mCalibration.toolAreaIsSummed);
     }
 
     // Pressure
     switch (mCalibration.pressureCalibration) {
     case Calibration::PRESSURE_CALIBRATION_NONE:
-        LOGI(INDENT "  touch.pressure.calibration: none");
+        LOGI(INDENT INDENT "touch.pressure.calibration: none");
         break;
     case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
-        LOGI(INDENT "  touch.pressure.calibration: physical");
+        LOGI(INDENT INDENT "touch.pressure.calibration: physical");
         break;
     case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
-        LOGI(INDENT "  touch.pressure.calibration: amplitude");
+        LOGI(INDENT INDENT "touch.pressure.calibration: amplitude");
         break;
     default:
         assert(false);
@@ -1827,10 +1926,10 @@ void TouchInputMapper::logCalibration() {
 
     switch (mCalibration.pressureSource) {
     case Calibration::PRESSURE_SOURCE_PRESSURE:
-        LOGI(INDENT "  touch.pressure.source: pressure");
+        LOGI(INDENT INDENT "touch.pressure.source: pressure");
         break;
     case Calibration::PRESSURE_SOURCE_TOUCH:
-        LOGI(INDENT "  touch.pressure.source: touch");
+        LOGI(INDENT INDENT "touch.pressure.source: touch");
         break;
     case Calibration::PRESSURE_SOURCE_DEFAULT:
         break;
@@ -1839,16 +1938,16 @@ void TouchInputMapper::logCalibration() {
     }
 
     if (mCalibration.havePressureScale) {
-        LOGI(INDENT "  touch.pressure.scale: %f", mCalibration.pressureScale);
+        LOGI(INDENT INDENT "touch.pressure.scale: %f", mCalibration.pressureScale);
     }
 
     // Size
     switch (mCalibration.sizeCalibration) {
     case Calibration::SIZE_CALIBRATION_NONE:
-        LOGI(INDENT "  touch.size.calibration: none");
+        LOGI(INDENT INDENT "touch.size.calibration: none");
         break;
     case Calibration::SIZE_CALIBRATION_NORMALIZED:
-        LOGI(INDENT "  touch.size.calibration: normalized");
+        LOGI(INDENT INDENT "touch.size.calibration: normalized");
         break;
     default:
         assert(false);
@@ -1857,10 +1956,10 @@ void TouchInputMapper::logCalibration() {
     // Orientation
     switch (mCalibration.orientationCalibration) {
     case Calibration::ORIENTATION_CALIBRATION_NONE:
-        LOGI(INDENT "  touch.orientation.calibration: none");
+        LOGI(INDENT INDENT "touch.orientation.calibration: none");
         break;
     case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
-        LOGI(INDENT "  touch.orientation.calibration: interpolated");
+        LOGI(INDENT INDENT "touch.orientation.calibration: interpolated");
         break;
     default:
         assert(false);
index 6d3eeee..fe76cd0 100644 (file)
@@ -119,7 +119,8 @@ int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) {
         if (outData != NULL) *outData = pending.data;
         return pending.ident;
     }
-    
+
+    // Wait for wakeAndLock() waiters to run then set mPolling to true.
     mLock.lock();
     while (mWaiters != 0) {
         mResume.wait(mLock);
@@ -127,6 +128,7 @@ int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) {
     mPolling = true;
     mLock.unlock();
 
+    // Poll.
     int32_t result;
     size_t requestedCount = mRequestedFds.size();
 
@@ -168,6 +170,7 @@ int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) {
     }
 #endif
 
+    // Process the poll results.
     mPendingCallbacks.clear();
     mPendingFds.clear();
     mPendingFdsPos = 0;
@@ -218,6 +221,7 @@ int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) {
     }
 
 Done:
+    // Set mPolling to false and wake up the wakeAndLock() waiters.
     mLock.lock();
     mPolling = false;
     if (mWaiters != 0) {
@@ -357,11 +361,13 @@ ssize_t PollLoop::getRequestIndexLocked(int fd) {
 
 void PollLoop::wakeAndLock() {
     mLock.lock();
+
     mWaiters += 1;
     while (mPolling) {
         wake();
         mAwake.wait(mLock);
     }
+
     mWaiters -= 1;
     if (mWaiters == 0) {
         mResume.signal();