Merge "These are not javadoc tags, they are annotations in sample code. Escape them properly." into gingerbread
diff --git a/include/gui/SensorEventQueue.h b/include/gui/SensorEventQueue.h
index 6581ae3..97dd391 100644
--- a/include/gui/SensorEventQueue.h
+++ b/include/gui/SensorEventQueue.h
@@ -42,7 +42,7 @@
 
 class ISensorEventConnection;
 class Sensor;
-class PollLoop;
+class Looper;
 
 // ----------------------------------------------------------------------------
 
@@ -69,11 +69,11 @@
     status_t disableSensor(int32_t handle) const;
 
 private:
-    sp<PollLoop> getPollLoop() const;
+    sp<Looper> getLooper() const;
     sp<ISensorEventConnection> mSensorEventConnection;
     sp<SensorChannel> mSensorChannel;
     mutable Mutex mLock;
-    mutable sp<PollLoop> mPollLoop;
+    mutable sp<Looper> mLooper;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/ui/FramebufferNativeWindow.h b/include/ui/FramebufferNativeWindow.h
index 0f4594f..c913355 100644
--- a/include/ui/FramebufferNativeWindow.h
+++ b/include/ui/FramebufferNativeWindow.h
@@ -56,6 +56,9 @@
     status_t setUpdateRectangle(const Rect& updateRect);
     status_t compositionComplete();
     
+    // for debugging only
+    int getCurrentBufferIndex() const;
+
 private:
     friend class LightRefBase<FramebufferNativeWindow>;    
     ~FramebufferNativeWindow(); // this class cannot be overloaded
@@ -77,6 +80,7 @@
     int32_t mNumBuffers;
     int32_t mNumFreeBuffers;
     int32_t mBufferHead;
+    int32_t mCurrentBufferIndex;
     bool mUpdateOnDemand;
 };
     
diff --git a/include/ui/GraphicLog.h b/include/ui/GraphicLog.h
new file mode 100644
index 0000000..ee1b09a
--- /dev/null
+++ b/include/ui/GraphicLog.h
@@ -0,0 +1,70 @@
+/*
+ * 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_GRAPHIC_LOG_H
+#define _UI_GRAPHIC_LOG_H
+
+#include <utils/Singleton.h>
+#include <cutils/compiler.h>
+
+namespace android {
+
+class GraphicLog : public Singleton<GraphicLog>
+{
+    int32_t mEnabled;
+    static void logImpl(int32_t tag, int32_t buffer);
+    static void logImpl(int32_t tag, int32_t identity, int32_t buffer);
+
+public:
+    enum {
+        SF_APP_DEQUEUE_BEFORE   = 60100,
+        SF_APP_DEQUEUE_AFTER    = 60101,
+        SF_APP_LOCK_BEFORE      = 60102,
+        SF_APP_LOCK_AFTER       = 60103,
+        SF_APP_QUEUE            = 60104,
+
+        SF_REPAINT              = 60105,
+        SF_COMPOSITION_COMPLETE = 60106,
+        SF_UNLOCK_CLIENTS       = 60107,
+        SF_SWAP_BUFFERS         = 60108,
+        SF_REPAINT_DONE         = 60109,
+
+        SF_FB_POST_BEFORE       = 60110,
+        SF_FB_POST_AFTER        = 60111,
+        SF_FB_DEQUEUE_BEFORE    = 60112,
+        SF_FB_DEQUEUE_AFTER     = 60113,
+        SF_FB_LOCK_BEFORE       = 60114,
+        SF_FB_LOCK_AFTER        = 60115,
+    };
+
+    inline void log(int32_t tag, int32_t buffer) {
+        if (CC_UNLIKELY(mEnabled))
+            logImpl(tag, buffer);
+    }
+    inline void log(int32_t tag, int32_t identity, int32_t buffer) {
+        if (CC_UNLIKELY(mEnabled))
+            logImpl(tag, identity, buffer);
+    }
+
+    GraphicLog();
+
+    void setEnabled(bool enable);
+};
+
+}
+
+#endif // _UI_GRAPHIC_LOG_H
+
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index a06208a..e466ddd 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -25,7 +25,7 @@
 #include <utils/Timers.h>
 #include <utils/RefBase.h>
 #include <utils/String8.h>
-#include <utils/PollLoop.h>
+#include <utils/Looper.h>
 #include <utils/Pool.h>
 
 #include <stddef.h>
@@ -81,9 +81,8 @@
  */
 struct InputTarget {
     enum {
-        /* This flag indicates that subsequent event delivery should be held until the
-         * current event is delivered to this target or a timeout occurs. */
-        FLAG_SYNC = 0x01,
+        /* This flag indicates that the event is being delivered to a foreground application. */
+        FLAG_FOREGROUND = 0x01,
 
         /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside
          * of the area of this target and so should instead be delivered as an
@@ -109,12 +108,6 @@
     // Flags for the input target.
     int32_t flags;
 
-    // The timeout for event delivery to this target in nanoseconds, or -1 to wait indefinitely.
-    nsecs_t timeout;
-
-    // The time already spent waiting for this target in nanoseconds, or 0 if none.
-    nsecs_t timeSpentWaitingForApplication;
-
     // The x and y offset to add to a MotionEvent as it is delivered.
     // (ignored for KeyEvents)
     float xOffset, yOffset;
@@ -190,6 +183,7 @@
     };
 
     sp<InputChannel> inputChannel;
+    String8 name;
     int32_t layoutParamsFlags;
     int32_t layoutParamsType;
     nsecs_t dispatchingTimeout;
@@ -206,9 +200,11 @@
     int32_t touchableAreaRight;
     int32_t touchableAreaBottom;
     bool visible;
+    bool canReceiveKeys;
     bool hasFocus;
     bool hasWallpaper;
     bool paused;
+    int32_t layer;
     int32_t ownerPid;
     int32_t ownerUid;
 
@@ -257,18 +253,12 @@
 
     /* Notifies the system that an application is not responding.
      * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
-    virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle) = 0;
+    virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
+            const sp<InputChannel>& inputChannel) = 0;
 
     /* Notifies the system that an input channel is unrecoverably broken. */
     virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel) = 0;
 
-    /* Notifies the system that an input channel is not responding.
-     * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
-    virtual nsecs_t notifyInputChannelANR(const sp<InputChannel>& inputChannel) = 0;
-
-    /* Notifies the system that an input channel recovered from ANR. */
-    virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) = 0;
-
     /* Gets the key repeat initial timeout or -1 if automatic key repeating is disabled. */
     virtual nsecs_t getKeyRepeatTimeout() = 0;
 
@@ -361,16 +351,6 @@
      */
     virtual void setInputDispatchMode(bool enabled, bool frozen) = 0;
 
-    /* Preempts input dispatch in progress by making pending synchronous
-     * dispatches asynchronous instead.  This method is generally called during a focus
-     * transition from one application to the next so as to enable the new application
-     * to start receiving input as soon as possible without having to wait for the
-     * old application to finish up.
-     *
-     * This method may be called on any thread (usually by the input manager).
-     */
-    virtual void preemptInputDispatch() = 0;
-
     /* Registers or unregister input channels that may be used as targets for input events.
      * If monitor is true, the channel will receive a copy of all input events.
      *
@@ -424,7 +404,6 @@
     virtual void setInputWindows(const Vector<InputWindow>& inputWindows);
     virtual void setFocusedApplication(const InputApplication* inputApplication);
     virtual void setInputDispatchMode(bool enabled, bool frozen);
-    virtual void preemptInputDispatch();
 
     virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor);
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
@@ -454,7 +433,7 @@
         int32_t injectorUid;      // -1 if not injected
 
         bool dispatchInProgress; // initially false, set to true while dispatching
-        int32_t pendingSyncDispatches; // the number of synchronous dispatches in progress
+        int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
 
         inline bool isInjected() { return injectorPid >= 0; }
 
@@ -522,7 +501,6 @@
         int32_t targetFlags;
         float xOffset;
         float yOffset;
-        nsecs_t timeout;
 
         // True if dispatch has started.
         bool inProgress;
@@ -540,12 +518,8 @@
         //   will be set to NULL.
         MotionSample* tailMotionSample;
 
-        inline bool isSyncTarget() const {
-            return targetFlags & InputTarget::FLAG_SYNC;
-        }
-
-        inline void preemptSyncTarget() {
-            targetFlags &= ~ InputTarget::FLAG_SYNC;
+        inline bool hasForegroundTarget() const {
+            return targetFlags & InputTarget::FLAG_FOREGROUND;
         }
     };
 
@@ -628,6 +602,8 @@
             dequeue(first);
             return first;
         }
+
+        uint32_t count() const;
     };
 
     /* Allocates queue entries and performs reference counting as needed. */
@@ -647,7 +623,7 @@
                 nsecs_t downTime, uint32_t pointerCount,
                 const int32_t* pointerIds, const PointerCoords* pointerCoords);
         DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry,
-                int32_t targetFlags, float xOffset, float yOffset, nsecs_t timeout);
+                int32_t targetFlags, float xOffset, float yOffset);
         CommandEntry* obtainCommandEntry(Command command);
 
         void releaseEventEntry(EventEntry* entry);
@@ -761,8 +737,6 @@
             STATUS_NORMAL,
             // An unrecoverable communication error has occurred.
             STATUS_BROKEN,
-            // The client is not responding.
-            STATUS_NOT_RESPONDING,
             // The input channel has been unregistered.
             STATUS_ZOMBIE
         };
@@ -772,11 +746,9 @@
         InputPublisher inputPublisher;
         InputState inputState;
         Queue<DispatchEntry> outboundQueue;
-        nsecs_t nextTimeoutTime; // next timeout time (LONG_LONG_MAX if none)
 
         nsecs_t lastEventTime; // the time when the event was originally captured
         nsecs_t lastDispatchTime; // the time when the last event was dispatched
-        nsecs_t lastANRTime; // the time when the last ANR was recorded
 
         explicit Connection(const sp<InputChannel>& inputChannel);
 
@@ -788,18 +760,6 @@
         // Returns NULL if not found.
         DispatchEntry* findQueuedDispatchEntryForEvent(const EventEntry* eventEntry) const;
 
-        // Determine whether this connection has a pending synchronous dispatch target.
-        // Since there can only ever be at most one such target at a time, if there is one,
-        // it must be at the tail because nothing else can be enqueued after it.
-        inline bool hasPendingSyncTarget() const {
-            return ! outboundQueue.isEmpty() && outboundQueue.tailSentinel.prev->isSyncTarget();
-        }
-
-        // Assuming there is a pending sync target, make it async.
-        inline void preemptSyncTarget() {
-            outboundQueue.tailSentinel.prev->preemptSyncTarget();
-        }
-
         // Gets the time since the current event was originally obtained from the input driver.
         inline double getEventLatencyMillis(nsecs_t currentTime) const {
             return (currentTime - lastEventTime) / 1000000.0;
@@ -810,15 +770,7 @@
             return (currentTime - lastDispatchTime) / 1000000.0;
         }
 
-        // Gets the time since the current event ANR was declared, if applicable.
-        inline double getANRLatencyMillis(nsecs_t currentTime) const {
-            return (currentTime - lastANRTime) / 1000000.0;
-        }
-
         status_t initialize();
-
-        void setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout);
-        void resetTimeout(nsecs_t currentTime);
     };
 
     sp<InputDispatcherPolicyInterface> mPolicy;
@@ -826,7 +778,7 @@
     Mutex mLock;
 
     Allocator mAllocator;
-    sp<PollLoop> mPollLoop;
+    sp<Looper> mLooper;
 
     EventEntry* mPendingEvent;
     Queue<EventEntry> mInboundQueue;
@@ -837,7 +789,7 @@
     void dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, nsecs_t keyRepeatDelay,
             nsecs_t* nextWakeupTime);
 
-    // Enqueues an inbound event.  Returns true if mPollLoop->wake() should be called.
+    // Enqueues an inbound event.  Returns true if mLooper->wake() should be called.
     bool enqueueInboundEventLocked(EventEntry* entry);
 
     // App switch latency optimization.
@@ -851,7 +803,7 @@
     // All registered connections mapped by receive pipe file descriptor.
     KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd;
 
-    ssize_t getConnectionIndex(const sp<InputChannel>& inputChannel);
+    ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel);
 
     // Active connections are connections that have a non-empty outbound queue.
     // We don't use a ref-counted pointer here because we explicitly abort connections
@@ -859,12 +811,6 @@
     // and the connection itself to be deactivated.
     Vector<Connection*> mActiveConnections;
 
-    // List of connections that have timed out.  Only used by dispatchOnce()
-    // We don't use a ref-counted pointer here because it is not possible for a connection
-    // to be unregistered while processing timed out connections since we hold the lock for
-    // the duration.
-    Vector<Connection*> mTimedOutConnections;
-
     // Input channels that will receive a copy of all input events.
     Vector<sp<InputChannel> > mMonitoringChannels;
 
@@ -877,7 +823,7 @@
     void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
 
     Condition mInjectionSyncFinishedCondition;
-    void decrementPendingSyncDispatchesLocked(EventEntry* entry);
+    void decrementPendingForegroundDispatchesLocked(EventEntry* entry);
 
     // Throttling state.
     struct ThrottleState {
@@ -951,8 +897,6 @@
     void logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry);
 
     // The input targets that were most recently identified for dispatch.
-    // If there is a synchronous event dispatch in progress, the current input targets will
-    // remain unchanged until the dispatch has completed or been aborted.
     bool mCurrentInputTargetsValid; // false while targets are being recomputed
     Vector<InputTarget> mCurrentInputTargets;
     int32_t mCurrentInputWindowType;
@@ -975,8 +919,9 @@
     int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
             const InputApplication* application, const InputWindow* window,
             nsecs_t* nextWakeupTime);
-    void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout);
-    nsecs_t getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(nsecs_t currentTime);
+    void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
+            const sp<InputChannel>& inputChannel);
+    nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime);
     void resetANRTimeoutsLocked();
 
     int32_t findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry,
@@ -984,14 +929,16 @@
     int32_t findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry,
             nsecs_t* nextWakeupTime, InputWindow** outWindow);
 
-    void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
-            nsecs_t timeSpentWaitingForApplication);
+    void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags);
     void addMonitoringTargetsLocked();
     void pokeUserActivityLocked(nsecs_t eventTime, int32_t windowType, int32_t eventType);
     bool checkInjectionPermission(const InputWindow* window,
             int32_t injectorPid, int32_t injectorUid);
     bool isWindowObscuredLocked(const InputWindow* window);
+    bool isWindowFinishedWithPreviousInputLocked(const InputWindow* window);
     void releaseTouchedWindowLocked();
+    String8 getApplicationWindowLabelLocked(const InputApplication* application,
+            const InputWindow* window);
 
     // Manage the dispatch cycle for a single connection.
     // These methods are deliberately not Interruptible because doing all of the work
@@ -1000,20 +947,13 @@
     void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
             EventEntry* eventEntry, const InputTarget* inputTarget,
             bool resumeWithAppendedMotionSample);
-    void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
-            nsecs_t timeSpentWaitingForApplication);
+    void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
     void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
     void startNextDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
-    void timeoutDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
-    void resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
-            const sp<Connection>& connection, nsecs_t newTimeout);
     void abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
             bool broken);
-    void drainOutboundQueueLocked(Connection* connection, DispatchEntry* firstDispatchEntryToDrain);
-    static bool handleReceiveCallback(int receiveFd, int events, void* data);
-
-    // Preempting input dispatch.
-    bool preemptInputDispatchInnerLocked();
+    void drainOutboundQueueLocked(Connection* connection);
+    static int handleReceiveCallback(int receiveFd, int events, void* data);
 
     // Dump state.
     void dumpDispatchStateLocked(String8& dump);
@@ -1027,20 +967,23 @@
     void onDispatchCycleStartedLocked(
             nsecs_t currentTime, const sp<Connection>& connection);
     void onDispatchCycleFinishedLocked(
-            nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR);
-    void onDispatchCycleANRLocked(
             nsecs_t currentTime, const sp<Connection>& connection);
     void onDispatchCycleBrokenLocked(
             nsecs_t currentTime, const sp<Connection>& connection);
+    void onANRLocked(
+            nsecs_t currentTime, const InputApplication* application, const InputWindow* window,
+            nsecs_t eventTime, nsecs_t waitStartTime);
 
     // Outbound policy interactions.
     void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry);
     void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry);
-    void doNotifyInputChannelANRLockedInterruptible(CommandEntry* commandEntry);
-    void doNotifyInputChannelRecoveredFromANRLockedInterruptible(CommandEntry* commandEntry);
+    void doNotifyANRLockedInterruptible(CommandEntry* commandEntry);
     void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry);
     void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry);
-    void doTargetsNotReadyTimeoutLockedInterruptible(CommandEntry* commandEntry);
+
+    // Statistics gathering.
+    void updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry,
+            int32_t injectionResult, nsecs_t timeSpentWaitingForApplication);
 };
 
 /* Enqueues and dispatches input events, endlessly. */
diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h
index 82831e2..dc9e27a 100644
--- a/include/ui/InputTransport.h
+++ b/include/ui/InputTransport.h
@@ -33,7 +33,6 @@
 #include <semaphore.h>
 #include <ui/Input.h>
 #include <utils/Errors.h>
-#include <utils/PollLoop.h>
 #include <utils/Timers.h>
 #include <utils/RefBase.h>
 #include <utils/String8.h>
diff --git a/include/utils/Looper.h b/include/utils/Looper.h
new file mode 100644
index 0000000..92e4b0a
--- /dev/null
+++ b/include/utils/Looper.h
@@ -0,0 +1,210 @@
+/*
+ * 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 UTILS_LOOPER_H
+#define UTILS_LOOPER_H
+
+#include <utils/threads.h>
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+
+#include <android/looper.h>
+
+/*
+ * Declare a concrete type for the NDK's looper forward declaration.
+ */
+struct ALooper {
+};
+
+namespace android {
+
+/**
+ * A polling loop that supports monitoring file descriptor events, optionally
+ * using callbacks.  The implementation uses epoll() internally.
+ *
+ * A looper can be associated with a thread although there is no requirement that it must be.
+ */
+class Looper : public ALooper, public RefBase {
+protected:
+    virtual ~Looper();
+
+public:
+    /**
+     * Creates a looper.
+     *
+     * If allowNonCallbaks is true, the looper will allow file descriptors to be
+     * registered without associated callbacks.  This assumes that the caller of
+     * pollOnce() is prepared to handle callback-less events itself.
+     */
+    Looper(bool allowNonCallbacks);
+
+    /**
+     * Returns whether this looper instance allows the registration of file descriptors
+     * using identifiers instead of callbacks.
+     */
+    bool getAllowNonCallbacks() const;
+
+    /**
+     * Waits for events to be available, with optional timeout in milliseconds.
+     * Invokes callbacks for all file descriptors on which an event occurred.
+     *
+     * If the timeout is zero, returns immediately without blocking.
+     * If the timeout is negative, waits indefinitely until an event appears.
+     *
+     * Returns ALOOPER_POLL_WAKE if the poll was awoken using wake() before
+     * the timeout expired and no callbacks were invoked and no other file
+     * descriptors were ready.
+     *
+     * Returns ALOOPER_POLL_CALLBACK if one or more callbacks were invoked.
+     *
+     * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given
+     * timeout expired.
+     *
+     * Returns ALOOPER_POLL_ERROR if an error occurred.
+     *
+     * Returns a value >= 0 containing an identifier if its file descriptor has data
+     * and it has no callback function (requiring the caller here to handle it).
+     * In this (and only this) case outFd, outEvents and outData will contain the poll
+     * events and data associated with the fd, otherwise they will be set to NULL.
+     *
+     * This method does not return until it has finished invoking the appropriate callbacks
+     * for all file descriptors that were signalled.
+     */
+    int pollOnce(int timeoutMillis,
+            int* outFd = NULL, int* outEvents = NULL, void** outData = NULL);
+
+    /**
+     * Like pollOnce(), but performs all pending callbacks until all
+     * data has been consumed or a file descriptor is available with no callback.
+     * This function will never return ALOOPER_POLL_CALLBACK.
+     */
+    int pollAll(int timeoutMillis,
+            int* outFd = NULL, int* outEvents = NULL, void** outData = NULL);
+
+    /**
+     * Wakes the poll asynchronously.
+     *
+     * This method can be called on any thread.
+     * This method returns immediately.
+     */
+    void wake();
+
+    /**
+     * Adds a new file descriptor to be polled by the looper.
+     * If the same file descriptor was previously added, it is replaced.
+     *
+     * "fd" is the file descriptor to be added.
+     * "ident" is an identifier for this event, which is returned from ALooper_pollOnce().
+     * The identifier must be >= 0, or ALOOPER_POLL_CALLBACK if providing a non-NULL callback.
+     * "events" are the poll events to wake up on.  Typically this is ALOOPER_EVENT_INPUT.
+     * "callback" is the function to call when there is an event on the file descriptor.
+     * "data" is a private data pointer to supply to the callback.
+     *
+     * There are two main uses of this function:
+     *
+     * (1) If "callback" is non-NULL, then this function will be called when there is
+     * data on the file descriptor.  It should execute any events it has pending,
+     * appropriately reading from the file descriptor.  The 'ident' is ignored in this case.
+     *
+     * (2) If "callback" is NULL, the 'ident' will be returned by ALooper_pollOnce
+     * when its file descriptor has data available, requiring the caller to take
+     * care of processing it.
+     *
+     * Returns 1 if the file descriptor was added, 0 if the arguments were invalid.
+     *
+     * This method can be called on any thread.
+     * This method may block briefly if it needs to wake the poll.
+     */
+    int addFd(int fd, int ident,
+            int events, ALooper_callbackFunc callback, void* data = NULL);
+
+    /**
+     * Removes a previously added file descriptor from the looper.
+     *
+     * When this method returns, it is safe to close the file descriptor since the looper
+     * will no longer have a reference to it.  However, it is possible for the callback to
+     * already be running or for it to run one last time if the file descriptor was already
+     * signalled.  Calling code is responsible for ensuring that this case is safely handled.
+     * For example, if the callback takes care of removing itself during its own execution either
+     * by returning 0 or by calling this method, then it can be guaranteed to not be invoked
+     * again at any later time unless registered anew.
+     *
+     * Returns 1 if the file descriptor was removed, 0 if none was previously registered.
+     *
+     * This method can be called on any thread.
+     * This method may block briefly if it needs to wake the poll.
+     */
+    int removeFd(int fd);
+
+    /**
+     * Prepares a looper associated with the calling thread, and returns it.
+     * If the thread already has a looper, it is returned.  Otherwise, a new
+     * one is created, associated with the thread, and returned.
+     *
+     * The opts may be ALOOPER_PREPARE_ALLOW_NON_CALLBACKS or 0.
+     */
+    static sp<Looper> prepare(int opts);
+
+    /**
+     * Sets the given looper to be associated with the calling thread.
+     * If another looper is already associated with the thread, it is replaced.
+     *
+     * If "looper" is NULL, removes the currently associated looper.
+     */
+    static void setForThread(const sp<Looper>& looper);
+
+    /**
+     * Returns the looper associated with the calling thread, or NULL if
+     * there is not one.
+     */
+    static sp<Looper> getForThread();
+
+private:
+    struct Request {
+        int fd;
+        int ident;
+        ALooper_callbackFunc callback;
+        void* data;
+    };
+
+    struct Response {
+        int events;
+        Request request;
+    };
+
+    const bool mAllowNonCallbacks; // immutable
+
+    int mEpollFd; // immutable
+    int mWakeReadPipeFd;  // immutable
+    int mWakeWritePipeFd; // immutable
+
+    // Locked list of file descriptor monitoring requests.
+    Mutex mLock;
+    KeyedVector<int, Request> mRequests;
+
+    // This state is only used privately by pollOnce and does not require a lock since
+    // it runs on a single thread.
+    Vector<Response> mResponses;
+    size_t mResponseIndex;
+
+    int pollInner(int timeoutMillis);
+
+    static void threadDestructor(void *st);
+};
+
+} // namespace android
+
+#endif // UTILS_LOOPER_H
diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h
deleted file mode 100644
index c2dfe5d..0000000
--- a/include/utils/PollLoop.h
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * 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 UTILS_POLL_LOOP_H
-#define UTILS_POLL_LOOP_H
-
-#include <utils/Vector.h>
-#include <utils/threads.h>
-
-#include <sys/poll.h>
-
-#include <android/looper.h>
-
-struct ALooper : public android::RefBase {
-protected:
-    virtual ~ALooper() { }
-
-public:
-    ALooper() { }
-};
-
-namespace android {
-
-/**
- * A basic file descriptor polling loop based on poll() with callbacks.
- */
-class PollLoop : public ALooper {
-protected:
-    virtual ~PollLoop();
-
-public:
-    PollLoop(bool allowNonCallbacks);
-
-    /**
-     * A callback that it to be invoked when an event occurs on a file descriptor.
-     * Specifies the events that were triggered and the user data provided when the
-     * callback was set.
-     *
-     * Returns true if the callback should be kept, false if it should be removed automatically
-     * after the callback returns.
-     */
-    typedef bool (*Callback)(int fd, int events, void* data);
-
-    enum {
-        POLL_CALLBACK = ALOOPER_POLL_CALLBACK,
-        POLL_TIMEOUT = ALOOPER_POLL_TIMEOUT,
-        POLL_ERROR = ALOOPER_POLL_ERROR,
-    };
-    
-    /**
-     * Performs a single call to poll() with optional timeout in milliseconds.
-     * Invokes callbacks for all file descriptors on which an event occurred.
-     *
-     * If the timeout is zero, returns immediately without blocking.
-     * If the timeout is negative, waits indefinitely until awoken.
-     *
-     * Returns ALOOPER_POLL_CALLBACK if a callback was invoked.
-     *
-     * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given
-     * timeout expired.
-     *
-     * Returns ALOPER_POLL_ERROR if an error occurred.
-     *
-     * Returns a value >= 0 containing a file descriptor if it has data
-     * and it has no callback function (requiring the caller here to handle it).
-     * In this (and only this) case outEvents and outData will contain the poll
-     * events and data associated with the fd.
-     *
-     * This method must only be called on the thread owning the PollLoop.
-     * This method blocks until either a file descriptor is signalled, a timeout occurs,
-     * or wake() is called.
-     * This method does not return until it has finished invoking the appropriate callbacks
-     * for all file descriptors that were signalled.
-     */
-    int32_t pollOnce(int timeoutMillis, int* outEvents = NULL, void** outData = NULL);
-
-    /**
-     * Wakes the loop asynchronously.
-     *
-     * This method can be called on any thread.
-     * This method returns immediately.
-     */
-    void wake();
-
-    /**
-     * Control whether this PollLoop instance allows using IDs instead
-     * of callbacks.
-     */
-    bool getAllowNonCallbacks() const;
-    
-    /**
-     * Sets the callback for a file descriptor, replacing the existing one, if any.
-     * It is an error to call this method with events == 0 or callback == NULL.
-     *
-     * Note that a callback can be invoked with the POLLERR, POLLHUP or POLLNVAL events
-     * even if it is not explicitly requested when registered.
-     *
-     * This method can be called on any thread.
-     * This method may block briefly if it needs to wake the poll loop.
-     */
-    void setCallback(int fd, int ident, int events, Callback callback, void* data = NULL);
-
-    /**
-     * Convenience for above setCallback when ident is not used.  In this case
-     * the ident is set to POLL_CALLBACK.
-     */
-    void setCallback(int fd, int events, Callback callback, void* data = NULL);
-    
-    /**
-     * Like setCallback(), but for the NDK callback function.
-     */
-    void setLooperCallback(int fd, int ident, int events, ALooper_callbackFunc* callback,
-            void* data);
-    
-    /**
-     * Removes the callback for a file descriptor, if one exists.
-     *
-     * When this method returns, it is safe to close the file descriptor since the poll loop
-     * will no longer have a reference to it.  However, it is possible for the callback to
-     * already be running or for it to run one last time if the file descriptor was already
-     * signalled.  Calling code is responsible for ensuring that this case is safely handled.
-     * For example, if the callback takes care of removing itself during its own execution either
-     * by returning false or calling this method, then it can be guaranteed to not be invoked
-     * again at any later time unless registered anew.
-     *
-     * This method can be called on any thread.
-     * This method may block briefly if it needs to wake the poll loop.
-     *
-     * Returns true if a callback was actually removed, false if none was registered.
-     */
-    bool removeCallback(int fd);
-
-    /**
-     * Set the given PollLoop to be associated with the
-     * calling thread.  There must be a 1:1 relationship between
-     * PollLoop and thread.
-     */
-    static void setForThread(const sp<PollLoop>& pollLoop);
-    
-    /**
-     * Return the PollLoop associated with the calling thread.
-     */
-    static sp<PollLoop> getForThread();
-    
-private:
-    struct RequestedCallback {
-        Callback callback;
-        ALooper_callbackFunc* looperCallback;
-        int ident;
-        void* data;
-    };
-
-    struct PendingCallback {
-        int fd;
-        int ident;
-        int events;
-        Callback callback;
-        ALooper_callbackFunc* looperCallback;
-        void* data;
-    };
-    
-    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;      // 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;
-
-    // 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();
-    void closeWakePipe();
-
-    void setCallbackCommon(int fd, int ident, int events, Callback callback,
-            ALooper_callbackFunc* looperCallback, void* data);
-    ssize_t getRequestIndexLocked(int fd);
-    void wakeAndLock();
-    static void threadDestructor(void *st);
-};
-
-} // namespace android
-
-#endif // UTILS_POLL_LOOP_H
diff --git a/libs/gui/SensorEventQueue.cpp b/libs/gui/SensorEventQueue.cpp
index 7eb6da5..c3a9f22 100644
--- a/libs/gui/SensorEventQueue.cpp
+++ b/libs/gui/SensorEventQueue.cpp
@@ -21,7 +21,7 @@
 
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
-#include <utils/PollLoop.h>
+#include <utils/Looper.h>
 
 #include <gui/Sensor.h>
 #include <gui/SensorChannel.h>
@@ -81,28 +81,28 @@
     return size;
 }
 
-sp<PollLoop> SensorEventQueue::getPollLoop() const
+sp<Looper> SensorEventQueue::getLooper() const
 {
     Mutex::Autolock _l(mLock);
-    if (mPollLoop == 0) {
-        mPollLoop = new PollLoop(true);
-        mPollLoop->setCallback(getFd(), getFd(), POLLIN, NULL, NULL);
+    if (mLooper == 0) {
+        mLooper = new Looper(true);
+        mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, NULL, NULL);
     }
-    return mPollLoop;
+    return mLooper;
 }
 
 status_t SensorEventQueue::waitForEvent() const
 {
     const int fd = getFd();
-    sp<PollLoop> pollLoop(getPollLoop());
-    int32_t result = pollLoop->pollOnce(-1, NULL, NULL);
-    return (result == fd) ? NO_ERROR : -1;
+    sp<Looper> looper(getLooper());
+    int32_t result = looper->pollOnce(-1);
+    return (result == fd) ? status_t(NO_ERROR) : status_t(-1);
 }
 
 status_t SensorEventQueue::wake() const
 {
-    sp<PollLoop> pollLoop(getPollLoop());
-    pollLoop->wake();
+    sp<Looper> looper(getLooper());
+    looper->wake();
     return NO_ERROR;
 }
 
diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp
index cb76091..560ea67 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -32,6 +32,7 @@
 #include <ui/DisplayInfo.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/GraphicBufferMapper.h>
+#include <ui/GraphicLog.h>
 #include <ui/Rect.h>
 
 #include <surfaceflinger/Surface.h>
@@ -568,7 +569,13 @@
     if (err != NO_ERROR)
         return err;
 
+    GraphicLog& logger(GraphicLog::getInstance());
+    logger.log(GraphicLog::SF_APP_DEQUEUE_BEFORE, mIdentity, -1);
+
     ssize_t bufIdx = mSharedBufferClient->dequeue();
+
+    logger.log(GraphicLog::SF_APP_DEQUEUE_AFTER, mIdentity, bufIdx);
+
     if (bufIdx < 0) {
         LOGE("error dequeuing a buffer (%s)", strerror(bufIdx));
         return bufIdx;
@@ -617,13 +624,20 @@
         return err;
 
     int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer));
+
+    GraphicLog& logger(GraphicLog::getInstance());
+    logger.log(GraphicLog::SF_APP_LOCK_BEFORE, mIdentity, bufIdx);
+
     err = mSharedBufferClient->lock(bufIdx);
+
+    logger.log(GraphicLog::SF_APP_LOCK_AFTER, mIdentity, bufIdx);
+
     LOGE_IF(err, "error locking buffer %d (%s)", bufIdx, strerror(-err));
     return err;
 }
 
 int Surface::queueBuffer(android_native_buffer_t* buffer)
-{   
+{
     status_t err = validate();
     if (err != NO_ERROR)
         return err;
@@ -633,6 +647,9 @@
     }
     
     int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer));
+
+    GraphicLog::getInstance().log(GraphicLog::SF_APP_QUEUE, mIdentity, bufIdx);
+
     mSharedBufferClient->setTransform(bufIdx, mNextBufferTransform);
     mSharedBufferClient->setCrop(bufIdx, mNextBufferCrop);
     mSharedBufferClient->setDirtyRegion(bufIdx, mDirtyRegion);
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index 9f49348..c4a09d6 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -9,6 +9,7 @@
 	GraphicBuffer.cpp \
 	GraphicBufferAllocator.cpp \
 	GraphicBufferMapper.cpp \
+	GraphicLog.cpp \
 	KeyLayoutMap.cpp \
 	KeyCharacterMap.cpp \
 	Input.cpp \
diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp
index 6f8948d..a36d555 100644
--- a/libs/ui/FramebufferNativeWindow.cpp
+++ b/libs/ui/FramebufferNativeWindow.cpp
@@ -29,6 +29,7 @@
 
 #include <ui/Rect.h>
 #include <ui/FramebufferNativeWindow.h>
+#include <ui/GraphicLog.h>
 
 #include <EGL/egl.h>
 
@@ -174,6 +175,14 @@
     return fb->setSwapInterval(fb, interval);
 }
 
+// only for debugging / logging
+int FramebufferNativeWindow::getCurrentBufferIndex() const
+{
+    Mutex::Autolock _l(mutex);
+    const int index = mCurrentBufferIndex;
+    return index;
+}
+
 int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window, 
         android_native_buffer_t** buffer)
 {
@@ -181,18 +190,24 @@
     Mutex::Autolock _l(self->mutex);
     framebuffer_device_t* fb = self->fbDev;
 
+    int index = self->mBufferHead++;
+    if (self->mBufferHead >= self->mNumBuffers)
+        self->mBufferHead = 0;
+
+    GraphicLog& logger(GraphicLog::getInstance());
+    logger.log(GraphicLog::SF_FB_DEQUEUE_BEFORE, index);
+
     // wait for a free buffer
     while (!self->mNumFreeBuffers) {
         self->mCondition.wait(self->mutex);
     }
     // get this buffer
     self->mNumFreeBuffers--;
-    int index = self->mBufferHead++;
-    if (self->mBufferHead >= self->mNumBuffers)
-        self->mBufferHead = 0;
+    self->mCurrentBufferIndex = index;
 
     *buffer = self->buffers[index].get();
 
+    logger.log(GraphicLog::SF_FB_DEQUEUE_AFTER, index);
     return 0;
 }
 
@@ -202,11 +217,17 @@
     FramebufferNativeWindow* self = getSelf(window);
     Mutex::Autolock _l(self->mutex);
 
+    const int index = self->mCurrentBufferIndex;
+    GraphicLog& logger(GraphicLog::getInstance());
+    logger.log(GraphicLog::SF_FB_LOCK_BEFORE, index);
+
     // wait that the buffer we're locking is not front anymore
     while (self->front == buffer) {
         self->mCondition.wait(self->mutex);
     }
 
+    logger.log(GraphicLog::SF_FB_LOCK_AFTER, index);
+
     return NO_ERROR;
 }
 
@@ -217,7 +238,15 @@
     Mutex::Autolock _l(self->mutex);
     framebuffer_device_t* fb = self->fbDev;
     buffer_handle_t handle = static_cast<NativeBuffer*>(buffer)->handle;
+
+    const int index = self->mCurrentBufferIndex;
+    GraphicLog& logger(GraphicLog::getInstance());
+    logger.log(GraphicLog::SF_FB_POST_BEFORE, index);
+
     int res = fb->post(fb, handle);
+
+    logger.log(GraphicLog::SF_FB_POST_AFTER, index);
+
     self->front = static_cast<NativeBuffer*>(buffer);
     self->mNumFreeBuffers++;
     self->mCondition.broadcast();
diff --git a/libs/ui/GraphicLog.cpp b/libs/ui/GraphicLog.cpp
new file mode 100644
index 0000000..b55ce23
--- /dev/null
+++ b/libs/ui/GraphicLog.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <utils/Endian.h>
+#include <utils/Timers.h>
+
+#include <ui/GraphicLog.h>
+
+namespace android {
+
+ANDROID_SINGLETON_STATIC_INSTANCE(GraphicLog)
+
+static inline
+void writeInt32(uint8_t* base, size_t& pos, int32_t value) {
+    int32_t v = htole32(value);
+    base[pos] = EVENT_TYPE_INT;
+    memcpy(&base[pos+1], &v, sizeof(int32_t));
+    pos += 1+sizeof(int32_t);
+}
+
+static inline
+void writeInt64(uint8_t* base,  size_t& pos, int64_t value) {
+    int64_t v = htole64(value);
+    base[pos] = EVENT_TYPE_LONG;
+    memcpy(&base[pos+1], &v, sizeof(int64_t));
+    pos += 1+sizeof(int64_t);
+}
+
+void GraphicLog::logImpl(int32_t tag, int32_t buffer)
+{
+    uint8_t scratch[2 + 2 + sizeof(int32_t) + sizeof(int64_t)];
+    size_t pos = 0;
+    scratch[pos++] = EVENT_TYPE_LIST;
+    scratch[pos++] = 2;
+    writeInt32(scratch, pos, buffer);
+    writeInt64(scratch, pos, ns2ms( systemTime( SYSTEM_TIME_MONOTONIC ) ));
+    android_bWriteLog(tag, scratch, sizeof(scratch));
+}
+
+void GraphicLog::logImpl(int32_t tag, int32_t identity, int32_t buffer)
+{
+    uint8_t scratch[2 + 3 + sizeof(int32_t) + sizeof(int32_t) + sizeof(int64_t)];
+    size_t pos = 0;
+    scratch[pos++] = EVENT_TYPE_LIST;
+    scratch[pos++] = 3;
+    writeInt32(scratch, pos, buffer);
+    writeInt32(scratch, pos, identity);
+    writeInt64(scratch, pos, ns2ms( systemTime( SYSTEM_TIME_MONOTONIC ) ));
+    android_bWriteLog(tag, scratch, sizeof(scratch));
+}
+
+GraphicLog::GraphicLog()
+    : mEnabled(0)
+{
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get("debug.graphic_log", property, NULL) > 0) {
+        mEnabled = atoi(property);
+    }
+}
+
+void GraphicLog::setEnabled(bool enable)
+{
+    mEnabled = enable;
+}
+
+}
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index b8a26b0..3b3c483 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -95,7 +95,7 @@
     mFocusedApplication(NULL),
     mCurrentInputTargetsValid(false),
     mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
-    mPollLoop = new PollLoop(false);
+    mLooper = new Looper(false);
 
     mInboundQueue.headSentinel.refCount = -1;
     mInboundQueue.headSentinel.type = EventEntry::TYPE_SENTINEL;
@@ -156,7 +156,7 @@
         timeoutMillis = 0;
     }
 
-    mPollLoop->pollOnce(timeoutMillis);
+    mLooper->pollOnce(timeoutMillis);
 }
 
 void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
@@ -196,42 +196,6 @@
         *nextWakeupTime = mAppSwitchDueTime;
     }
 
-    // Detect and process timeouts for all connections and determine if there are any
-    // synchronous event dispatches pending.  This step is entirely non-interruptible.
-    bool havePendingSyncTarget = false;
-    size_t activeConnectionCount = mActiveConnections.size();
-    for (size_t i = 0; i < activeConnectionCount; i++) {
-        Connection* connection = mActiveConnections.itemAt(i);
-
-        if (connection->hasPendingSyncTarget()) {
-            if (isAppSwitchDue) {
-                connection->preemptSyncTarget();
-            } else {
-                havePendingSyncTarget = true;
-            }
-        }
-
-        nsecs_t connectionTimeoutTime  = connection->nextTimeoutTime;
-        if (connectionTimeoutTime <= currentTime) {
-            mTimedOutConnections.add(connection);
-        } else if (connectionTimeoutTime < *nextWakeupTime) {
-            *nextWakeupTime = connectionTimeoutTime;
-        }
-    }
-
-    size_t timedOutConnectionCount = mTimedOutConnections.size();
-    for (size_t i = 0; i < timedOutConnectionCount; i++) {
-        Connection* connection = mTimedOutConnections.itemAt(i);
-        timeoutDispatchCycleLocked(currentTime, connection);
-        *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
-    }
-    mTimedOutConnections.clear();
-
-    // If we have a pending synchronous target, skip dispatch.
-    if (havePendingSyncTarget) {
-        return;
-    }
-
     // Ready to start a new event.
     // If we don't already have a pending event, go grab one.
     if (! mPendingEvent) {
@@ -557,7 +521,7 @@
         }
 
         entry->dispatchInProgress = true;
-        startFindingTargetsLocked();
+        startFindingTargetsLocked(); // resets mCurrentInputTargetsValid
     }
 
     // Identify targets.
@@ -618,7 +582,7 @@
         logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
 
         entry->dispatchInProgress = true;
-        startFindingTargetsLocked();
+        startFindingTargetsLocked(); // resets mCurrentInputTargetsValid
     }
 
     bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
@@ -728,7 +692,7 @@
     for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
         const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);
 
-        ssize_t connectionIndex = getConnectionIndex(inputTarget.inputChannel);
+        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
         if (connectionIndex >= 0) {
             sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
             prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
@@ -770,9 +734,8 @@
     } else {
         if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
 #if DEBUG_FOCUS
-            LOGD("Waiting for application to become ready for input: name=%s, window=%s",
-                    application ? application->name.string() : "<unknown>",
-                    window ? window->inputChannel->getName().string() : "<unknown>");
+            LOGD("Waiting for application to become ready for input: %s",
+                    getApplicationWindowLabelLocked(application, window).string());
 #endif
             nsecs_t timeout = window ? window->dispatchingTimeout :
                 application ? application->dispatchingTimeout : DEFAULT_INPUT_DISPATCHING_TIMEOUT;
@@ -789,21 +752,7 @@
     }
 
     if (currentTime >= mInputTargetWaitTimeoutTime) {
-        LOGI("Application is not ready for input: name=%s, window=%s,"
-                "%01.1fms since event, %01.1fms since wait started",
-                application ? application->name.string() : "<unknown>",
-                window ? window->inputChannel->getName().string() : "<unknown>",
-                (currentTime - entry->eventTime) / 1000000.0,
-                (currentTime - mInputTargetWaitStartTime) / 1000000.0);
-
-        CommandEntry* commandEntry = postCommandLocked(
-                & InputDispatcher::doTargetsNotReadyTimeoutLockedInterruptible);
-        if (application) {
-            commandEntry->inputApplicationHandle = application->handle;
-        }
-        if (window) {
-            commandEntry->inputChannel = window->inputChannel;
-        }
+        onANRLocked(currentTime, application, window, entry->eventTime, mInputTargetWaitStartTime);
 
         // Force poll loop to wake up immediately on next iteration once we get the
         // ANR response back from the policy.
@@ -818,17 +767,25 @@
     }
 }
 
-void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout) {
+void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
+        const sp<InputChannel>& inputChannel) {
     if (newTimeout > 0) {
         // Extend the timeout.
         mInputTargetWaitTimeoutTime = now() + newTimeout;
     } else {
         // Give up.
         mInputTargetWaitTimeoutExpired = true;
+
+        // Input state will not be realistic.  Mark it out of sync.
+        ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
+        if (connectionIndex >= 0) {
+            sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+            connection->inputState.setOutOfSync();
+        }
     }
 }
 
-nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(
+nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(
         nsecs_t currentTime) {
     if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
         return currentTime - mInputTargetWaitStartTime;
@@ -841,13 +798,6 @@
         LOGD("Resetting ANR timeouts.");
 #endif
 
-    // Reset timeouts for all active connections.
-    nsecs_t currentTime = now();
-    for (size_t i = 0; i < mActiveConnections.size(); i++) {
-        Connection* connection = mActiveConnections[i];
-        connection->resetTimeout(currentTime);
-    }
-
     // Reset input target wait timeout.
     mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
 }
@@ -865,8 +815,8 @@
         if (mFocusedApplication) {
 #if DEBUG_FOCUS
             LOGD("Waiting because there is no focused window but there is a "
-                    "focused application that may eventually add a window: '%s'.",
-                    mFocusedApplication->name.string());
+                    "focused application that may eventually add a window: %s.",
+                    getApplicationWindowLabelLocked(mFocusedApplication, NULL).string());
 #endif
             injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
                     mFocusedApplication, NULL, nextWakeupTime);
@@ -894,19 +844,31 @@
         goto Unresponsive;
     }
 
+    // If the currently focused window is still working on previous events then keep waiting.
+    if (! isWindowFinishedWithPreviousInputLocked(mFocusedWindow)) {
+#if DEBUG_FOCUS
+        LOGD("Waiting because focused window still processing previous input.");
+#endif
+        injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+                mFocusedApplication, mFocusedWindow, nextWakeupTime);
+        goto Unresponsive;
+    }
+
     // Success!  Output targets.
     injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
     *outWindow = mFocusedWindow;
-    addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_SYNC,
-            getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(currentTime));
+    addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND);
 
     // Done.
 Failed:
 Unresponsive:
+    nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
+    updateDispatchStatisticsLocked(currentTime, entry,
+            injectionResult, timeSpentWaitingForApplication);
 #if DEBUG_FOCUS
-    LOGD("findFocusedWindow finished: injectionResult=%d",
-            injectionResult);
-    logDispatchStateLocked();
+    LOGD("findFocusedWindow finished: injectionResult=%d, "
+            "timeSpendWaitingForApplication=%0.1fms",
+            injectionResult, timeSpentWaitingForApplication / 1000000.0);
 #endif
     return injectionResult;
 }
@@ -1018,8 +980,8 @@
             if (mFocusedApplication) {
 #if DEBUG_FOCUS
                 LOGD("Waiting because there is no touched window but there is a "
-                        "focused application that may eventually add a new window: '%s'.",
-                        mFocusedApplication->name.string());
+                        "focused application that may eventually add a new window: %s.",
+                        getApplicationWindowLabelLocked(mFocusedApplication, NULL).string());
 #endif
                 injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
                         mFocusedApplication, NULL, nextWakeupTime);
@@ -1051,6 +1013,17 @@
             goto Unresponsive;
         }
 
+        // If the touched window is still working on previous events then keep waiting.
+        if (! isWindowFinishedWithPreviousInputLocked(newTouchedWindow)) {
+#if DEBUG_FOCUS
+            LOGD("Waiting because touched window still processing previous input.");
+#endif
+            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+                    NULL, newTouchedWindow, nextWakeupTime);
+            injectionPermission = INJECTION_PERMISSION_GRANTED;
+            goto Unresponsive;
+        }
+
         // Success!  Update the touch dispatch state for real.
         releaseTouchedWindowLocked();
 
@@ -1098,6 +1071,17 @@
             injectionPermission = INJECTION_PERMISSION_GRANTED;
             goto Unresponsive;
         }
+
+        // If the touched window is still working on previous events then keep waiting.
+        if (! isWindowFinishedWithPreviousInputLocked(mTouchedWindow)) {
+#if DEBUG_FOCUS
+            LOGD("Waiting because touched window still processing previous input.");
+#endif
+            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+                    NULL, mTouchedWindow, nextWakeupTime);
+            injectionPermission = INJECTION_PERMISSION_GRANTED;
+            goto Unresponsive;
+        }
     }
 
     // Success!  Output targets.
@@ -1108,7 +1092,7 @@
         size_t numWallpaperWindows = mTouchedWallpaperWindows.size();
         for (size_t i = 0; i < numWallpaperWindows; i++) {
             addWindowTargetLocked(mTouchedWallpaperWindows[i],
-                    InputTarget::FLAG_WINDOW_IS_OBSCURED, 0);
+                    InputTarget::FLAG_WINDOW_IS_OBSCURED);
         }
 
         size_t numOutsideTargets = mTempTouchedOutsideTargets.size();
@@ -1118,16 +1102,15 @@
             if (outsideTarget.obscured) {
                 outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
             }
-            addWindowTargetLocked(outsideTarget.window, outsideTargetFlags, 0);
+            addWindowTargetLocked(outsideTarget.window, outsideTargetFlags);
         }
         mTempTouchedOutsideTargets.clear();
 
-        int32_t targetFlags = InputTarget::FLAG_SYNC;
+        int32_t targetFlags = InputTarget::FLAG_FOREGROUND;
         if (mTouchedWindowIsObscured) {
             targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
         }
-        addWindowTargetLocked(mTouchedWindow, targetFlags,
-                getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(currentTime));
+        addWindowTargetLocked(mTouchedWindow, targetFlags);
         *outWindow = mTouchedWindow;
     }
 
@@ -1166,10 +1149,13 @@
     }
 
 Unresponsive:
+    nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
+    updateDispatchStatisticsLocked(currentTime, entry,
+            injectionResult, timeSpentWaitingForApplication);
 #if DEBUG_FOCUS
-    LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d",
-            injectionResult, injectionPermission);
-    logDispatchStateLocked();
+    LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d,"
+            "timeSpendWaitingForApplication=%0.1fms",
+            injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
 #endif
     return injectionResult;
 }
@@ -1180,15 +1166,12 @@
     mTouchedWallpaperWindows.clear();
 }
 
-void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
-        nsecs_t timeSpentWaitingForApplication) {
+void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags) {
     mCurrentInputTargets.push();
 
     InputTarget& target = mCurrentInputTargets.editTop();
     target.inputChannel = window->inputChannel;
     target.flags = targetFlags;
-    target.timeout = window->dispatchingTimeout;
-    target.timeSpentWaitingForApplication = timeSpentWaitingForApplication;
     target.xOffset = - window->frameLeft;
     target.yOffset = - window->frameTop;
 }
@@ -1200,8 +1183,6 @@
         InputTarget& target = mCurrentInputTargets.editTop();
         target.inputChannel = mMonitoringChannels[i];
         target.flags = 0;
-        target.timeout = -1;
-        target.timeSpentWaitingForApplication = 0;
         target.xOffset = 0;
         target.yOffset = 0;
     }
@@ -1241,6 +1222,34 @@
     return false;
 }
 
+bool InputDispatcher::isWindowFinishedWithPreviousInputLocked(const InputWindow* window) {
+    ssize_t connectionIndex = getConnectionIndexLocked(window->inputChannel);
+    if (connectionIndex >= 0) {
+        sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+        return connection->outboundQueue.isEmpty();
+    } else {
+        return true;
+    }
+}
+
+String8 InputDispatcher::getApplicationWindowLabelLocked(const InputApplication* application,
+        const InputWindow* window) {
+    if (application) {
+        if (window) {
+            String8 label(application->name);
+            label.append(" - ");
+            label.append(window->name);
+            return label;
+        } else {
+            return application->name;
+        }
+    } else if (window) {
+        return window->name;
+    } else {
+        return String8("<unknown application or window>");
+    }
+}
+
 void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime,
         int32_t windowType, int32_t eventType) {
     CommandEntry* commandEntry = postCommandLocked(
@@ -1254,25 +1263,18 @@
         const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
         bool resumeWithAppendedMotionSample) {
 #if DEBUG_DISPATCH_CYCLE
-    LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, timeout=%lldns, "
+    LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, "
             "xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s",
-            connection->getInputChannelName(), inputTarget->flags, inputTarget->timeout,
+            connection->getInputChannelName(), inputTarget->flags,
             inputTarget->xOffset, inputTarget->yOffset,
             toString(resumeWithAppendedMotionSample));
 #endif
 
     // Skip this event if the connection status is not normal.
-    // We don't want to enqueue additional outbound events if the connection is broken or
-    // not responding.
+    // We don't want to enqueue additional outbound events if the connection is broken.
     if (connection->status != Connection::STATUS_NORMAL) {
         LOGW("channel '%s' ~ Dropping event because the channel status is %s",
                 connection->getInputChannelName(), connection->getStatusLabel());
-
-        // If the connection is not responding but the user is poking the application anyways,
-        // retrigger the original timeout.
-        if (connection->status == Connection::STATUS_NOT_RESPONDING) {
-            timeoutDispatchCycleLocked(currentTime, connection);
-        }
         return;
     }
 
@@ -1379,7 +1381,7 @@
 
                 DispatchEntry* cancelationDispatchEntry =
                         mAllocator.obtainDispatchEntry(cancelationEventEntry,
-                        0, inputTarget->xOffset, inputTarget->yOffset, inputTarget->timeout);
+                        0, inputTarget->xOffset, inputTarget->yOffset); // increments ref
                 connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry);
 
                 mAllocator.releaseEventEntry(cancelationEventEntry);
@@ -1390,10 +1392,9 @@
     // This is a new event.
     // Enqueue a new dispatch entry onto the outbound queue for this connection.
     DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref
-            inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset,
-            inputTarget->timeout);
-    if (dispatchEntry->isSyncTarget()) {
-        eventEntry->pendingSyncDispatches += 1;
+            inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset);
+    if (dispatchEntry->hasForegroundTarget()) {
+        eventEntry->pendingForegroundDispatches += 1;
     }
 
     // Handle the case where we could not stream a new motion sample because the consumer has
@@ -1416,13 +1417,12 @@
     // If the outbound queue was previously empty, start the dispatch cycle going.
     if (wasEmpty) {
         activateConnectionLocked(connection.get());
-        startDispatchCycleLocked(currentTime, connection,
-                inputTarget->timeSpentWaitingForApplication);
+        startDispatchCycleLocked(currentTime, connection);
     }
 }
 
 void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
-        const sp<Connection>& connection, nsecs_t timeSpentWaitingForApplication) {
+        const sp<Connection>& connection) {
 #if DEBUG_DISPATCH_CYCLE
     LOGD("channel '%s' ~ startDispatchCycle",
             connection->getInputChannelName());
@@ -1588,9 +1588,6 @@
     connection->lastEventTime = dispatchEntry->eventEntry->eventTime;
     connection->lastDispatchTime = currentTime;
 
-    nsecs_t timeout = dispatchEntry->timeout - timeSpentWaitingForApplication;
-    connection->setNextTimeoutTime(currentTime, timeout);
-
     // Notify other system components.
     onDispatchCycleStartedLocked(currentTime, connection);
 }
@@ -1610,21 +1607,8 @@
         return;
     }
 
-    // Clear the pending timeout.
-    connection->nextTimeoutTime = LONG_LONG_MAX;
-
-    if (connection->status == Connection::STATUS_NOT_RESPONDING) {
-        // Recovering from an ANR.
-        connection->status = Connection::STATUS_NORMAL;
-
-        // Notify other system components.
-        onDispatchCycleFinishedLocked(currentTime, connection, true /*recoveredFromANR*/);
-    } else {
-        // Normal finish.  Not much to do here.
-
-        // Notify other system components.
-        onDispatchCycleFinishedLocked(currentTime, connection, false /*recoveredFromANR*/);
-    }
+    // Notify other system components.
+    onDispatchCycleFinishedLocked(currentTime, connection);
 
     // Reset the publisher since the event has been consumed.
     // We do this now so that the publisher can release some of its internal resources
@@ -1653,20 +1637,20 @@
                 dispatchEntry->inProgress = false;
                 dispatchEntry->headMotionSample = dispatchEntry->tailMotionSample;
                 dispatchEntry->tailMotionSample = NULL;
-                startDispatchCycleLocked(currentTime, connection, 0);
+                startDispatchCycleLocked(currentTime, connection);
                 return;
             }
             // Finished.
             connection->outboundQueue.dequeueAtHead();
-            if (dispatchEntry->isSyncTarget()) {
-                decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
+            if (dispatchEntry->hasForegroundTarget()) {
+                decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry);
             }
             mAllocator.releaseDispatchEntry(dispatchEntry);
         } else {
             // If the head is not in progress, then we must have already dequeued the in
-            // progress event, which means we actually aborted it (due to ANR).
+            // progress event, which means we actually aborted it.
             // So just start the next event for this connection.
-            startDispatchCycleLocked(currentTime, connection, 0);
+            startDispatchCycleLocked(currentTime, connection);
             return;
         }
     }
@@ -1675,66 +1659,6 @@
     deactivateConnectionLocked(connection.get());
 }
 
-void InputDispatcher::timeoutDispatchCycleLocked(nsecs_t currentTime,
-        const sp<Connection>& connection) {
-#if DEBUG_DISPATCH_CYCLE
-    LOGD("channel '%s' ~ timeoutDispatchCycle",
-            connection->getInputChannelName());
-#endif
-
-    if (connection->status == Connection::STATUS_NORMAL) {
-        // Enter the not responding state.
-        connection->status = Connection::STATUS_NOT_RESPONDING;
-        connection->lastANRTime = currentTime;
-    } else if (connection->status != Connection::STATUS_NOT_RESPONDING) {
-        // Connection is broken or dead.
-        return;
-    }
-
-    // Notify other system components.
-    // This enqueues a command which will eventually call resumeAfterTimeoutDispatchCycleLocked.
-    onDispatchCycleANRLocked(currentTime, connection);
-}
-
-void InputDispatcher::resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
-        const sp<Connection>& connection, nsecs_t newTimeout) {
-#if DEBUG_DISPATCH_CYCLE
-    LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked - newTimeout=%lld",
-            connection->getInputChannelName(), newTimeout);
-#endif
-
-    if (connection->status != Connection::STATUS_NOT_RESPONDING) {
-        return;
-    }
-
-    if (newTimeout > 0) {
-        // The system has decided to give the application some more time.
-        // Keep waiting synchronously and resume normal dispatch.
-        connection->status = Connection::STATUS_NORMAL;
-        connection->setNextTimeoutTime(currentTime, newTimeout);
-    } else {
-        // The system is about to throw up an ANR dialog and has requested that we abort dispatch.
-        // Reset the timeout.
-        connection->nextTimeoutTime = LONG_LONG_MAX;
-
-        // Input state will no longer be realistic.
-        connection->inputState.setOutOfSync();
-
-        if (! connection->outboundQueue.isEmpty()) {
-            // Make the current pending dispatch asynchronous (if it isn't already) so that
-            // subsequent events can be delivered to the ANR dialog or to another application.
-            DispatchEntry* currentDispatchEntry = connection->outboundQueue.headSentinel.next;
-            currentDispatchEntry->preemptSyncTarget();
-
-            // Drain all but the first entry in the outbound queue.  We keep the first entry
-            // since that is the one that dispatch is stuck on.  We throw away the others
-            // so that we don't spam the application with stale messages if it eventually
-            // wakes up and recovers from the ANR.
-            drainOutboundQueueLocked(connection.get(), currentDispatchEntry->next);
-        }
-    }
-}
-
 void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime,
         const sp<Connection>& connection, bool broken) {
 #if DEBUG_DISPATCH_CYCLE
@@ -1742,20 +1666,16 @@
             connection->getInputChannelName(), toString(broken));
 #endif
 
-    // Clear the pending timeout.
-    connection->nextTimeoutTime = LONG_LONG_MAX;
-
     // Input state will no longer be realistic.
     connection->inputState.setOutOfSync();
 
     // Clear the outbound queue.
-    drainOutboundQueueLocked(connection.get(), connection->outboundQueue.headSentinel.next);
+    drainOutboundQueueLocked(connection.get());
 
     // Handle the case where the connection appears to be unrecoverably broken.
     // Ignore already broken or zombie connections.
     if (broken) {
-        if (connection->status == Connection::STATUS_NORMAL
-                || connection->status == Connection::STATUS_NOT_RESPONDING) {
+        if (connection->status == Connection::STATUS_NORMAL) {
             connection->status = Connection::STATUS_BROKEN;
 
             // Notify other system components.
@@ -1764,27 +1684,19 @@
     }
 }
 
-void InputDispatcher::drainOutboundQueueLocked(Connection* connection,
-        DispatchEntry* firstDispatchEntryToDrain) {
-    for (DispatchEntry* dispatchEntry = firstDispatchEntryToDrain;
-            dispatchEntry != & connection->outboundQueue.tailSentinel;) {
-        DispatchEntry* next = dispatchEntry->next;
-        connection->outboundQueue.dequeue(dispatchEntry);
-
-        if (dispatchEntry->isSyncTarget()) {
-            decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
+void InputDispatcher::drainOutboundQueueLocked(Connection* connection) {
+    while (! connection->outboundQueue.isEmpty()) {
+        DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead();
+        if (dispatchEntry->hasForegroundTarget()) {
+            decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry);
         }
         mAllocator.releaseDispatchEntry(dispatchEntry);
-
-        dispatchEntry = next;
     }
 
-    if (connection->outboundQueue.isEmpty()) {
-        deactivateConnectionLocked(connection);
-    }
+    deactivateConnectionLocked(connection);
 }
 
-bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) {
+int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) {
     InputDispatcher* d = static_cast<InputDispatcher*>(data);
 
     { // acquire lock
@@ -1794,24 +1706,24 @@
         if (connectionIndex < 0) {
             LOGE("Received spurious receive callback for unknown input channel.  "
                     "fd=%d, events=0x%x", receiveFd, events);
-            return false; // remove the callback
+            return 0; // remove the callback
         }
 
         nsecs_t currentTime = now();
 
         sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex);
-        if (events & (POLLERR | POLLHUP | POLLNVAL)) {
+        if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
             LOGE("channel '%s' ~ Consumer closed input channel or an error occurred.  "
                     "events=0x%x", connection->getInputChannelName(), events);
             d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
             d->runCommandsLockedInterruptible();
-            return false; // remove the callback
+            return 0; // remove the callback
         }
 
-        if (! (events & POLLIN)) {
+        if (! (events & ALOOPER_EVENT_INPUT)) {
             LOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
                     "events=0x%x", connection->getInputChannelName(), events);
-            return true;
+            return 1;
         }
 
         status_t status = connection->inputPublisher.receiveFinishedSignal();
@@ -1820,12 +1732,12 @@
                     connection->getInputChannelName(), status);
             d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
             d->runCommandsLockedInterruptible();
-            return false; // remove the callback
+            return 0; // remove the callback
         }
 
         d->finishDispatchCycleLocked(currentTime, connection);
         d->runCommandsLockedInterruptible();
-        return true;
+        return 1;
     } // release lock
 }
 
@@ -1843,7 +1755,7 @@
     } // release lock
 
     if (needWake) {
-        mPollLoop->wake();
+        mLooper->wake();
     }
 }
 
@@ -1870,7 +1782,7 @@
     } // release lock
 
     if (needWake) {
-        mPollLoop->wake();
+        mLooper->wake();
     }
 }
 
@@ -1941,56 +1853,63 @@
             // STREAMING CASE
             //
             // There is no pending motion event (of any kind) for this device in the inbound queue.
-            // Search the outbound queues for a synchronously dispatched motion event for this
-            // device.  If found, then we append the new sample to that event and then try to
-            // push it out to all current targets.  It is possible that some targets will already
-            // have consumed the motion event.  This case is automatically handled by the
-            // logic in prepareDispatchCycleLocked by tracking where resumption takes place.
-            //
-            // The reason we look for a synchronously dispatched motion event is because we
-            // want to be sure that no other motion events have been dispatched since the move.
-            // It's also convenient because it means that the input targets are still valid.
-            // This code could be improved to support streaming of asynchronously dispatched
-            // motion events (which might be significantly more efficient) but it may become
-            // a little more complicated as a result.
-            //
-            // Note: This code crucially depends on the invariant that an outbound queue always
-            //       contains at most one synchronous event and it is always last (but it might
-            //       not be first!).
+            // Search the outbound queue for the current foreground targets to find a dispatched
+            // motion event that is still in progress.  If found, then, appen the new sample to
+            // that event and push it out to all current targets.  The logic in
+            // prepareDispatchCycleLocked takes care of the case where some targets may
+            // already have consumed the motion event by starting a new dispatch cycle if needed.
             if (mCurrentInputTargetsValid) {
-                for (size_t i = 0; i < mActiveConnections.size(); i++) {
-                    Connection* connection = mActiveConnections.itemAt(i);
-                    if (! connection->outboundQueue.isEmpty()) {
-                        DispatchEntry* dispatchEntry = connection->outboundQueue.tailSentinel.prev;
-                        if (dispatchEntry->isSyncTarget()) {
-                            if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
-                                goto NoBatchingOrStreaming;
-                            }
-
-                            MotionEntry* syncedMotionEntry = static_cast<MotionEntry*>(
-                                    dispatchEntry->eventEntry);
-                            if (syncedMotionEntry->action != AMOTION_EVENT_ACTION_MOVE
-                                    || syncedMotionEntry->deviceId != deviceId
-                                    || syncedMotionEntry->pointerCount != pointerCount
-                                    || syncedMotionEntry->isInjected()) {
-                                goto NoBatchingOrStreaming;
-                            }
-
-                            // Found synced move entry.  Append sample and resume dispatch.
-                            mAllocator.appendMotionSample(syncedMotionEntry, eventTime,
-                                    pointerCoords);
-    #if DEBUG_BATCHING
-                            LOGD("Appended motion sample onto batch for most recent synchronously "
-                                    "dispatched motion event for this device in the outbound queues.");
-    #endif
-                            nsecs_t currentTime = now();
-                            dispatchEventToCurrentInputTargetsLocked(currentTime, syncedMotionEntry,
-                                    true /*resumeWithAppendedMotionSample*/);
-
-                            runCommandsLockedInterruptible();
-                            return; // done!
-                        }
+                for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
+                    const InputTarget& inputTarget = mCurrentInputTargets[i];
+                    if ((inputTarget.flags & InputTarget::FLAG_FOREGROUND) == 0) {
+                        // Skip non-foreground targets.  We only want to stream if there is at
+                        // least one foreground target whose dispatch is still in progress.
+                        continue;
                     }
+
+                    ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
+                    if (connectionIndex < 0) {
+                        // Connection must no longer be valid.
+                        continue;
+                    }
+
+                    sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+                    if (connection->outboundQueue.isEmpty()) {
+                        // This foreground target has an empty outbound queue.
+                        continue;
+                    }
+
+                    DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
+                    if (! dispatchEntry->inProgress
+                            || dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
+                        // No motion event is being dispatched.
+                        continue;
+                    }
+
+                    MotionEntry* motionEntry = static_cast<MotionEntry*>(
+                            dispatchEntry->eventEntry);
+                    if (motionEntry->action != AMOTION_EVENT_ACTION_MOVE
+                            || motionEntry->deviceId != deviceId
+                            || motionEntry->pointerCount != pointerCount
+                            || motionEntry->isInjected()) {
+                        // The motion event is not compatible with this move.
+                        continue;
+                    }
+
+                    // Hurray!  This foreground target is currently dispatching a move event
+                    // that we can stream onto.  Append the motion sample and resume dispatch.
+                    mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords);
+#if DEBUG_BATCHING
+                    LOGD("Appended motion sample onto batch for most recently dispatched "
+                            "motion event for this device in the outbound queues.  "
+                            "Attempting to stream the motion sample.");
+#endif
+                    nsecs_t currentTime = now();
+                    dispatchEventToCurrentInputTargetsLocked(currentTime, motionEntry,
+                            true /*resumeWithAppendedMotionSample*/);
+
+                    runCommandsLockedInterruptible();
+                    return; // done!
                 }
             }
 
@@ -2007,7 +1926,7 @@
     } // release lock
 
     if (needWake) {
-        mPollLoop->wake();
+        mLooper->wake();
     }
 }
 
@@ -2043,7 +1962,7 @@
     } // release lock
 
     if (needWake) {
-        mPollLoop->wake();
+        mLooper->wake();
     }
 
     int32_t injectionResult;
@@ -2074,15 +1993,15 @@
 
             if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
                     && syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
-                while (injectedEntry->pendingSyncDispatches != 0) {
+                while (injectedEntry->pendingForegroundDispatches != 0) {
 #if DEBUG_INJECTION
-                    LOGD("injectInputEvent - Waiting for %d pending synchronous dispatches.",
-                            injectedEntry->pendingSyncDispatches);
+                    LOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
+                            injectedEntry->pendingForegroundDispatches);
 #endif
                     nsecs_t remainingTimeout = endTime - now();
                     if (remainingTimeout <= 0) {
 #if DEBUG_INJECTION
-                    LOGD("injectInputEvent - Timed out waiting for pending synchronous "
+                    LOGD("injectInputEvent - Timed out waiting for pending foreground "
                             "dispatches to finish.");
 #endif
                         injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
@@ -2137,10 +2056,10 @@
     }
 }
 
-void InputDispatcher::decrementPendingSyncDispatchesLocked(EventEntry* entry) {
-    entry->pendingSyncDispatches -= 1;
+void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* entry) {
+    entry->pendingForegroundDispatches -= 1;
 
-    if (entry->isInjected() && entry->pendingSyncDispatches == 0) {
+    if (entry->isInjected() && entry->pendingForegroundDispatches == 0) {
         mInjectionSyncFinishedCondition.broadcast();
     }
 }
@@ -2238,6 +2157,10 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
+        sp<InputChannel> oldFocusedWindowChannel = mFocusedWindow
+                ? mFocusedWindow->inputChannel : NULL;
+        int32_t oldFocusedWindowLayer = mFocusedWindow ? mFocusedWindow->layer : -1;
+
         sp<InputChannel> touchedWindowChannel;
         if (mTouchedWindow) {
             touchedWindowChannel = mTouchedWindow->inputChannel;
@@ -2251,8 +2174,6 @@
             mTouchedWallpaperWindows.clear();
         }
 
-        bool hadFocusedWindow = mFocusedWindow != NULL;
-
         mFocusedWindow = NULL;
         mWallpaperWindows.clear();
 
@@ -2283,9 +2204,36 @@
 
         mTempTouchedWallpaperChannels.clear();
 
-        if ((hadFocusedWindow && ! mFocusedWindow)
-                || (mFocusedWindow && ! mFocusedWindow->visible)) {
-            preemptInputDispatchInnerLocked();
+        bool preempt = false;
+        if (mFocusedWindow
+                && mFocusedWindow->inputChannel != oldFocusedWindowChannel
+                && mFocusedWindow->canReceiveKeys) {
+            // If the new input focus is an error window or appears above the current
+            // input focus, drop the current touched window so that we can start
+            // delivering events to the new input focus as soon as possible.
+            if (mFocusedWindow->layoutParamsFlags & InputWindow::FLAG_SYSTEM_ERROR) {
+#if DEBUG_FOCUS
+                LOGD("Preempting: New SYSTEM_ERROR window; resetting state");
+#endif
+                preempt = true;
+            } else if (oldFocusedWindowChannel.get() != NULL
+                    && mFocusedWindow->layer > oldFocusedWindowLayer) {
+#if DEBUG_FOCUS
+                LOGD("Preempting: Transferring focus to new window at higher layer: "
+                        "old win layer=%d, new win layer=%d",
+                        oldFocusedWindowLayer, mFocusedWindow->layer);
+#endif
+                preempt = true;
+            }
+        }
+        if (mTouchedWindow && ! mTouchedWindow->visible) {
+#if DEBUG_FOCUS
+            LOGD("Preempting: Touched window became invisible.");
+#endif
+            preempt = true;
+        }
+        if (preempt) {
+            releaseTouchedWindowLocked();
         }
 
 #if DEBUG_FOCUS
@@ -2294,7 +2242,7 @@
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
-    mPollLoop->wake();
+    mLooper->wake();
 }
 
 void InputDispatcher::setFocusedApplication(const InputApplication* inputApplication) {
@@ -2317,7 +2265,7 @@
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
-    mPollLoop->wake();
+    mLooper->wake();
 }
 
 void InputDispatcher::releaseFocusedApplicationLocked() {
@@ -2355,43 +2303,10 @@
 
     if (changed) {
         // Wake up poll loop since it may need to make new input dispatching choices.
-        mPollLoop->wake();
+        mLooper->wake();
     }
 }
 
-void InputDispatcher::preemptInputDispatch() {
-#if DEBUG_FOCUS
-    LOGD("preemptInputDispatch");
-#endif
-
-    bool preemptedOne;
-    { // acquire lock
-        AutoMutex _l(mLock);
-        preemptedOne = preemptInputDispatchInnerLocked();
-    } // release lock
-
-    if (preemptedOne) {
-        // Wake up the poll loop so it can get a head start dispatching the next event.
-        mPollLoop->wake();
-    }
-}
-
-bool InputDispatcher::preemptInputDispatchInnerLocked() {
-    bool preemptedOne = false;
-    for (size_t i = 0; i < mActiveConnections.size(); i++) {
-        Connection* connection = mActiveConnections[i];
-        if (connection->hasPendingSyncTarget()) {
-#if DEBUG_DISPATCH_CYCLE
-            LOGD("channel '%s' ~ Preempted pending synchronous dispatch",
-                    connection->getInputChannelName());
-#endif
-            connection->preemptSyncTarget();
-            preemptedOne = true;
-        }
-    }
-    return preemptedOne;
-}
-
 void InputDispatcher::logDispatchStateLocked() {
     String8 dump;
     dumpDispatchStateLocked(dump);
@@ -2447,12 +2362,14 @@
                 i, channel->getName().string());
     }
 
+    dump.appendFormat("  inboundQueue: length=%u", mInboundQueue.count());
+
     for (size_t i = 0; i < mActiveConnections.size(); i++) {
         const Connection* connection = mActiveConnections[i];
-        dump.appendFormat("  activeConnection[%d]: '%s', status=%s, hasPendingSyncTarget=%s, "
+        dump.appendFormat("  activeConnection[%d]: '%s', status=%s, outboundQueueLength=%u"
                 "inputState.isNeutral=%s, inputState.isOutOfSync=%s\n",
                 i, connection->getInputChannelName(), connection->getStatusLabel(),
-                toString(connection->hasPendingSyncTarget()),
+                connection->outboundQueue.count(),
                 toString(connection->inputState.isNeutral()),
                 toString(connection->inputState.isOutOfSync()));
     }
@@ -2474,7 +2391,7 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
-        if (getConnectionIndex(inputChannel) >= 0) {
+        if (getConnectionIndexLocked(inputChannel) >= 0) {
             LOGW("Attempted to register already registered input channel '%s'",
                     inputChannel->getName().string());
             return BAD_VALUE;
@@ -2495,7 +2412,7 @@
             mMonitoringChannels.push(inputChannel);
         }
 
-        mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
+        mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
 
         runCommandsLockedInterruptible();
     } // release lock
@@ -2510,7 +2427,7 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
-        ssize_t connectionIndex = getConnectionIndex(inputChannel);
+        ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
         if (connectionIndex < 0) {
             LOGW("Attempted to unregister already unregistered input channel '%s'",
                     inputChannel->getName().string());
@@ -2529,7 +2446,7 @@
             }
         }
 
-        mPollLoop->removeCallback(inputChannel->getReceivePipeFd());
+        mLooper->removeFd(inputChannel->getReceivePipeFd());
 
         nsecs_t currentTime = now();
         abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
@@ -2539,11 +2456,11 @@
 
     // Wake the poll loop because removing the connection may have changed the current
     // synchronization state.
-    mPollLoop->wake();
+    mLooper->wake();
     return OK;
 }
 
-ssize_t InputDispatcher::getConnectionIndex(const sp<InputChannel>& inputChannel) {
+ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
     ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd());
     if (connectionIndex >= 0) {
         sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
@@ -2578,31 +2495,7 @@
 }
 
 void InputDispatcher::onDispatchCycleFinishedLocked(
-        nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR) {
-    if (recoveredFromANR) {
-        LOGI("channel '%s' ~ Recovered from ANR.  %01.1fms since event, "
-                "%01.1fms since dispatch, %01.1fms since ANR",
-                connection->getInputChannelName(),
-                connection->getEventLatencyMillis(currentTime),
-                connection->getDispatchLatencyMillis(currentTime),
-                connection->getANRLatencyMillis(currentTime));
-
-        CommandEntry* commandEntry = postCommandLocked(
-                & InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible);
-        commandEntry->connection = connection;
-    }
-}
-
-void InputDispatcher::onDispatchCycleANRLocked(
         nsecs_t currentTime, const sp<Connection>& connection) {
-    LOGI("channel '%s' ~ Not responding!  %01.1fms since event, %01.1fms since dispatch",
-            connection->getInputChannelName(),
-            connection->getEventLatencyMillis(currentTime),
-            connection->getDispatchLatencyMillis(currentTime));
-
-    CommandEntry* commandEntry = postCommandLocked(
-            & InputDispatcher::doNotifyInputChannelANRLockedInterruptible);
-    commandEntry->connection = connection;
 }
 
 void InputDispatcher::onDispatchCycleBrokenLocked(
@@ -2615,6 +2508,25 @@
     commandEntry->connection = connection;
 }
 
+void InputDispatcher::onANRLocked(
+        nsecs_t currentTime, const InputApplication* application, const InputWindow* window,
+        nsecs_t eventTime, nsecs_t waitStartTime) {
+    LOGI("Application is not responding: %s.  "
+            "%01.1fms since event, %01.1fms since wait started",
+            getApplicationWindowLabelLocked(application, window).string(),
+            (currentTime - eventTime) / 1000000.0,
+            (currentTime - waitStartTime) / 1000000.0);
+
+    CommandEntry* commandEntry = postCommandLocked(
+            & InputDispatcher::doNotifyANRLockedInterruptible);
+    if (application) {
+        commandEntry->inputApplicationHandle = application->handle;
+    }
+    if (window) {
+        commandEntry->inputChannel = window->inputChannel;
+    }
+}
+
 void InputDispatcher::doNotifyConfigurationChangedInterruptible(
         CommandEntry* commandEntry) {
     mLock.unlock();
@@ -2637,33 +2549,16 @@
     }
 }
 
-void InputDispatcher::doNotifyInputChannelANRLockedInterruptible(
+void InputDispatcher::doNotifyANRLockedInterruptible(
         CommandEntry* commandEntry) {
-    sp<Connection> connection = commandEntry->connection;
+    mLock.unlock();
 
-    if (connection->status != Connection::STATUS_ZOMBIE) {
-        mLock.unlock();
+    nsecs_t newTimeout = mPolicy->notifyANR(
+            commandEntry->inputApplicationHandle, commandEntry->inputChannel);
 
-        nsecs_t newTimeout = mPolicy->notifyInputChannelANR(connection->inputChannel);
+    mLock.lock();
 
-        mLock.lock();
-
-        nsecs_t currentTime = now();
-        resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout);
-    }
-}
-
-void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible(
-        CommandEntry* commandEntry) {
-    sp<Connection> connection = commandEntry->connection;
-
-    if (connection->status != Connection::STATUS_ZOMBIE) {
-        mLock.unlock();
-
-        mPolicy->notifyInputChannelRecoveredFromANR(connection->inputChannel);
-
-        mLock.lock();
-    }
+    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel);
 }
 
 void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
@@ -2695,22 +2590,9 @@
     mLock.lock();
 }
 
-void InputDispatcher::doTargetsNotReadyTimeoutLockedInterruptible(
-        CommandEntry* commandEntry) {
-    mLock.unlock();
-
-    nsecs_t newTimeout;
-    if (commandEntry->inputChannel.get()) {
-        newTimeout = mPolicy->notifyInputChannelANR(commandEntry->inputChannel);
-    } else if (commandEntry->inputApplicationHandle.get()) {
-        newTimeout = mPolicy->notifyANR(commandEntry->inputApplicationHandle);
-    } else {
-        newTimeout = 0;
-    }
-
-    mLock.lock();
-
-    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout);
+void InputDispatcher::updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry,
+        int32_t injectionResult, nsecs_t timeSpentWaitingForApplication) {
+    // TODO Write some statistics about how long we spend waiting.
 }
 
 void InputDispatcher::dump(String8& dump) {
@@ -2718,6 +2600,18 @@
 }
 
 
+// --- InputDispatcher::Queue ---
+
+template <typename T>
+uint32_t InputDispatcher::Queue<T>::count() const {
+    uint32_t result = 0;
+    for (const T* entry = headSentinel.next; entry != & tailSentinel; entry = entry->next) {
+        result += 1;
+    }
+    return result;
+}
+
+
 // --- InputDispatcher::Allocator ---
 
 InputDispatcher::Allocator::Allocator() {
@@ -2733,7 +2627,7 @@
     entry->injectionIsAsync = false;
     entry->injectorPid = -1;
     entry->injectorUid = -1;
-    entry->pendingSyncDispatches = 0;
+    entry->pendingForegroundDispatches = 0;
 }
 
 InputDispatcher::ConfigurationChangedEntry*
@@ -2797,14 +2691,13 @@
 
 InputDispatcher::DispatchEntry* InputDispatcher::Allocator::obtainDispatchEntry(
         EventEntry* eventEntry,
-        int32_t targetFlags, float xOffset, float yOffset, nsecs_t timeout) {
+        int32_t targetFlags, float xOffset, float yOffset) {
     DispatchEntry* entry = mDispatchEntryPool.alloc();
     entry->eventEntry = eventEntry;
     eventEntry->refCount += 1;
     entry->targetFlags = targetFlags;
     entry->xOffset = xOffset;
     entry->yOffset = yOffset;
-    entry->timeout = timeout;
     entry->inProgress = false;
     entry->headMotionSample = NULL;
     entry->tailMotionSample = NULL;
@@ -2896,7 +2789,7 @@
 void InputDispatcher::EventEntry::recycle() {
     injectionResult = INPUT_EVENT_INJECTION_PENDING;
     dispatchInProgress = false;
-    pendingSyncDispatches = 0;
+    pendingForegroundDispatches = 0;
 }
 
 
@@ -3106,9 +2999,7 @@
 
 InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) :
         status(STATUS_NORMAL), inputChannel(inputChannel), inputPublisher(inputChannel),
-        nextTimeoutTime(LONG_LONG_MAX),
-        lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX),
-        lastANRTime(LONG_LONG_MAX) {
+        lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX) {
 }
 
 InputDispatcher::Connection::~Connection() {
@@ -3118,18 +3009,6 @@
     return inputPublisher.initialize();
 }
 
-void InputDispatcher::Connection::setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout) {
-    nextTimeoutTime = (timeout >= 0) ? currentTime + timeout : LONG_LONG_MAX;
-}
-
-void InputDispatcher::Connection::resetTimeout(nsecs_t currentTime) {
-    if (outboundQueue.isEmpty()) {
-        nextTimeoutTime = LONG_LONG_MAX;
-    } else {
-        setNextTimeoutTime(currentTime, outboundQueue.headSentinel.next->timeout);
-    }
-}
-
 const char* InputDispatcher::Connection::getStatusLabel() const {
     switch (status) {
     case STATUS_NORMAL:
@@ -3138,9 +3017,6 @@
     case STATUS_BROKEN:
         return "BROKEN";
 
-    case STATUS_NOT_RESPONDING:
-        return "NOT_RESPONDING";
-
     case STATUS_ZOMBIE:
         return "ZOMBIE";
 
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 2e20268..eb75ed8 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -86,7 +86,7 @@
 	$(commonSources) \
 	BackupData.cpp \
 	BackupHelpers.cpp \
-	PollLoop.cpp
+	Looper.cpp
 
 ifeq ($(TARGET_OS),linux)
 LOCAL_LDLIBS += -lrt -ldl
diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp
new file mode 100644
index 0000000..fd287da
--- /dev/null
+++ b/libs/utils/Looper.cpp
@@ -0,0 +1,368 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+// A looper implementation based on epoll().
+//
+#define LOG_TAG "Looper"
+
+//#define LOG_NDEBUG 0
+
+// Debugs poll and wake interactions.
+#define DEBUG_POLL_AND_WAKE 0
+
+// Debugs callback registration and invocation.
+#define DEBUG_CALLBACKS 0
+
+#include <cutils/log.h>
+#include <utils/Looper.h>
+#include <utils/Timers.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/epoll.h>
+
+
+namespace android {
+
+static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER;
+static bool gHaveTLS = false;
+static pthread_key_t gTLS = 0;
+
+// Hint for number of file descriptors to be associated with the epoll instance.
+static const int EPOLL_SIZE_HINT = 8;
+
+// Maximum number of file descriptors for which to retrieve poll events each iteration.
+static const int EPOLL_MAX_EVENTS = 16;
+
+Looper::Looper(bool allowNonCallbacks) :
+        mAllowNonCallbacks(allowNonCallbacks),
+        mResponseIndex(0) {
+    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
+    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);
+
+    int wakeFds[2];
+    int result = pipe(wakeFds);
+    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);
+
+    mWakeReadPipeFd = wakeFds[0];
+    mWakeWritePipeFd = wakeFds[1];
+
+    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
+    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",
+            errno);
+
+    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
+    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
+            errno);
+
+    struct epoll_event eventItem;
+    eventItem.events = EPOLLIN;
+    eventItem.data.fd = mWakeReadPipeFd;
+    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
+    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
+            errno);
+}
+
+Looper::~Looper() {
+    close(mWakeReadPipeFd);
+    close(mWakeWritePipeFd);
+    close(mEpollFd);
+}
+
+void Looper::threadDestructor(void *st) {
+    Looper* const self = static_cast<Looper*>(st);
+    if (self != NULL) {
+        self->decStrong((void*)threadDestructor);
+    }
+}
+
+void Looper::setForThread(const sp<Looper>& looper) {
+    sp<Looper> old = getForThread(); // also has side-effect of initializing TLS
+
+    if (looper != NULL) {
+        looper->incStrong((void*)threadDestructor);
+    }
+
+    pthread_setspecific(gTLS, looper.get());
+
+    if (old != NULL) {
+        old->decStrong((void*)threadDestructor);
+    }
+}
+
+sp<Looper> Looper::getForThread() {
+    if (!gHaveTLS) {
+        pthread_mutex_lock(&gTLSMutex);
+        if (pthread_key_create(&gTLS, threadDestructor) != 0) {
+            pthread_mutex_unlock(&gTLSMutex);
+            return NULL;
+        }
+        gHaveTLS = true;
+        pthread_mutex_unlock(&gTLSMutex);
+    }
+
+    return (Looper*)pthread_getspecific(gTLS);
+}
+
+sp<Looper> Looper::prepare(int opts) {
+    bool allowNonCallbacks = opts & ALOOPER_PREPARE_ALLOW_NON_CALLBACKS;
+    sp<Looper> looper = Looper::getForThread();
+    if (looper == NULL) {
+        looper = new Looper(allowNonCallbacks);
+        Looper::setForThread(looper);
+    }
+    if (looper->getAllowNonCallbacks() != allowNonCallbacks) {
+        LOGW("Looper already prepared for this thread with a different value for the "
+                "ALOOPER_PREPARE_ALLOW_NON_CALLBACKS option.");
+    }
+    return looper;
+}
+
+bool Looper::getAllowNonCallbacks() const {
+    return mAllowNonCallbacks;
+}
+
+int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
+    int result = 0;
+    for (;;) {
+        while (mResponseIndex < mResponses.size()) {
+            const Response& response = mResponses.itemAt(mResponseIndex++);
+            if (! response.request.callback) {
+#if DEBUG_POLL_AND_WAKE
+                LOGD("%p ~ pollOnce - returning signalled identifier %d: "
+                        "fd=%d, events=0x%x, data=%p", this,
+                        response.request.ident, response.request.fd,
+                        response.events, response.request.data);
+#endif
+                if (outFd != NULL) *outFd = response.request.fd;
+                if (outEvents != NULL) *outEvents = response.events;
+                if (outData != NULL) *outData = response.request.data;
+                return response.request.ident;
+            }
+        }
+
+        if (result != 0) {
+#if DEBUG_POLL_AND_WAKE
+            LOGD("%p ~ pollOnce - returning result %d", this, result);
+#endif
+            if (outFd != NULL) *outFd = 0;
+            if (outEvents != NULL) *outEvents = NULL;
+            if (outData != NULL) *outData = NULL;
+            return result;
+        }
+
+        result = pollInner(timeoutMillis);
+    }
+}
+
+int Looper::pollInner(int timeoutMillis) {
+#if DEBUG_POLL_AND_WAKE
+    LOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
+#endif
+    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
+    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
+    if (eventCount < 0) {
+        if (errno != EINTR) {
+            LOGW("Poll failed with an unexpected error, errno=%d", errno);
+        }
+        return ALOOPER_POLL_ERROR;
+    }
+
+    if (eventCount == 0) {
+#if DEBUG_POLL_AND_WAKE
+        LOGD("%p ~ pollOnce - timeout", this);
+#endif
+        return ALOOPER_POLL_TIMEOUT;
+    }
+
+    int result = ALOOPER_POLL_WAKE;
+    mResponses.clear();
+    mResponseIndex = 0;
+
+#if DEBUG_POLL_AND_WAKE
+    LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
+#endif
+    { // acquire lock
+        AutoMutex _l(mLock);
+        for (int i = 0; i < eventCount; i++) {
+            int fd = eventItems[i].data.fd;
+            uint32_t epollEvents = eventItems[i].events;
+            if (fd == mWakeReadPipeFd) {
+                if (epollEvents & EPOLLIN) {
+#if DEBUG_POLL_AND_WAKE
+                    LOGD("%p ~ pollOnce - awoken", this);
+#endif
+                    char buffer[16];
+                    ssize_t nRead;
+                    do {
+                        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
+                    } while (nRead == sizeof(buffer));
+                } else {
+                    LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
+                }
+            } else {
+                ssize_t requestIndex = mRequests.indexOfKey(fd);
+                if (requestIndex >= 0) {
+                    int events = 0;
+                    if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT;
+                    if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT;
+                    if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR;
+                    if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP;
+
+                    Response response;
+                    response.events = events;
+                    response.request = mRequests.valueAt(requestIndex);
+                    mResponses.push(response);
+                } else {
+                    LOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
+                            "no longer registered.", epollEvents, fd);
+                }
+            }
+        }
+    }
+
+    for (size_t i = 0; i < mResponses.size(); i++) {
+        const Response& response = mResponses.itemAt(i);
+        if (response.request.callback) {
+#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
+            LOGD("%p ~ pollOnce - invoking callback: fd=%d, events=0x%x, data=%p", this,
+                    response.request.fd, response.events, response.request.data);
+#endif
+            int callbackResult = response.request.callback(
+                    response.request.fd, response.events, response.request.data);
+            if (callbackResult == 0) {
+                removeFd(response.request.fd);
+            }
+
+            result = ALOOPER_POLL_CALLBACK;
+        }
+    }
+    return result;
+}
+
+int Looper::pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
+    if (timeoutMillis <= 0) {
+        int result;
+        do {
+            result = pollOnce(timeoutMillis, outFd, outEvents, outData);
+        } while (result == ALOOPER_POLL_CALLBACK);
+        return result;
+    } else {
+        nsecs_t endTime = systemTime(SYSTEM_TIME_MONOTONIC)
+                + milliseconds_to_nanoseconds(timeoutMillis);
+
+        for (;;) {
+            int result = pollOnce(timeoutMillis, outFd, outEvents, outData);
+            if (result != ALOOPER_POLL_CALLBACK) {
+                return result;
+            }
+
+            nsecs_t timeoutNanos = endTime - systemTime(SYSTEM_TIME_MONOTONIC);
+            if (timeoutNanos <= 0) {
+                return ALOOPER_POLL_TIMEOUT;
+            }
+
+            timeoutMillis = int(nanoseconds_to_milliseconds(timeoutNanos + 999999LL));
+        }
+    }
+}
+
+void Looper::wake() {
+#if DEBUG_POLL_AND_WAKE
+    LOGD("%p ~ wake", this);
+#endif
+
+    ssize_t nWrite = write(mWakeWritePipeFd, "W", 1);
+    if (nWrite != 1) {
+        if (errno != EAGAIN) {
+            LOGW("Could not write wake signal, errno=%d", errno);
+        }
+    }
+}
+
+int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data) {
+#if DEBUG_CALLBACKS
+    LOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident,
+            events, callback, data);
+#endif
+
+    int epollEvents = 0;
+    if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN;
+    if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT;
+    if (events & ALOOPER_EVENT_ERROR) epollEvents |= EPOLLERR;
+    if (events & ALOOPER_EVENT_HANGUP) epollEvents |= EPOLLHUP;
+
+    if (epollEvents == 0) {
+        LOGE("Invalid attempt to set a callback with no selected poll events.");
+        return -1;
+    }
+
+    if (! callback) {
+        if (! mAllowNonCallbacks) {
+            LOGE("Invalid attempt to set NULL callback but not allowed for this looper.");
+            return -1;
+        }
+
+        if (ident < 0) {
+            LOGE("Invalid attempt to set NULL callback with ident <= 0.");
+            return -1;
+        }
+    }
+
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        Request request;
+        request.fd = fd;
+        request.ident = ident;
+        request.callback = callback;
+        request.data = data;
+
+        struct epoll_event eventItem;
+        eventItem.events = epollEvents;
+        eventItem.data.fd = fd;
+
+        ssize_t requestIndex = mRequests.indexOfKey(fd);
+        if (requestIndex < 0) {
+            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
+            if (epollResult < 0) {
+                LOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
+                return -1;
+            }
+            mRequests.add(fd, request);
+        } else {
+            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
+            if (epollResult < 0) {
+                LOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno);
+                return -1;
+            }
+            mRequests.replaceValueAt(requestIndex, request);
+        }
+    } // release lock
+    return 1;
+}
+
+int Looper::removeFd(int fd) {
+#if DEBUG_CALLBACKS
+    LOGD("%p ~ removeFd - fd=%d", this, fd);
+#endif
+
+    { // acquire lock
+        AutoMutex _l(mLock);
+        ssize_t requestIndex = mRequests.indexOfKey(fd);
+        if (requestIndex < 0) {
+            return 0;
+        }
+
+        int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL);
+        if (epollResult < 0) {
+            LOGE("Error removing epoll events for fd %d, errno=%d", fd, errno);
+            return -1;
+        }
+
+        mRequests.removeItemsAt(requestIndex);
+    } // request lock
+    return 1;
+}
+
+} // namespace android
diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp
deleted file mode 100644
index fe76cd0..0000000
--- a/libs/utils/PollLoop.cpp
+++ /dev/null
@@ -1,377 +0,0 @@
-//
-// Copyright 2010 The Android Open Source Project
-//
-// A select loop implementation.
-//
-#define LOG_TAG "PollLoop"
-
-//#define LOG_NDEBUG 0
-
-// Debugs poll and wake interactions.
-#define DEBUG_POLL_AND_WAKE 0
-
-// Debugs callback registration and invocation.
-#define DEBUG_CALLBACKS 0
-
-#include <cutils/log.h>
-#include <utils/PollLoop.h>
-
-#include <unistd.h>
-#include <fcntl.h>
-
-namespace android {
-
-static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER;
-static bool gHaveTLS = false;
-static pthread_key_t gTLS = 0;
-
-PollLoop::PollLoop(bool allowNonCallbacks) :
-        mAllowNonCallbacks(allowNonCallbacks), mPolling(false),
-        mWaiters(0), mPendingFdsPos(0) {
-    openWakePipe();
-}
-
-PollLoop::~PollLoop() {
-    closeWakePipe();
-}
-
-void PollLoop::threadDestructor(void *st) {
-    PollLoop* const self = static_cast<PollLoop*>(st);
-    if (self != NULL) {
-        self->decStrong((void*)threadDestructor);
-    }
-}
-
-void PollLoop::setForThread(const sp<PollLoop>& pollLoop) {
-    sp<PollLoop> old = getForThread();
-    
-    if (pollLoop != NULL) {
-        pollLoop->incStrong((void*)threadDestructor);
-    }
-    
-    pthread_setspecific(gTLS, pollLoop.get());
-    
-    if (old != NULL) {
-        old->decStrong((void*)threadDestructor);
-    }
-}
-    
-sp<PollLoop> PollLoop::getForThread() {
-    if (!gHaveTLS) {
-        pthread_mutex_lock(&gTLSMutex);
-        if (pthread_key_create(&gTLS, threadDestructor) != 0) {
-            pthread_mutex_unlock(&gTLSMutex);
-            return NULL;
-        }
-        gHaveTLS = true;
-        pthread_mutex_unlock(&gTLSMutex);
-    }
-    
-    return (PollLoop*)pthread_getspecific(gTLS);
-}
-
-void PollLoop::openWakePipe() {
-    int wakeFds[2];
-    int result = pipe(wakeFds);
-    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);
-
-    mWakeReadPipeFd = wakeFds[0];
-    mWakeWritePipeFd = wakeFds[1];
-
-    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
-    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",
-            errno);
-
-    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
-    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
-            errno);
-
-    // Add the wake pipe to the head of the request list with a null callback.
-    struct pollfd requestedFd;
-    requestedFd.fd = mWakeReadPipeFd;
-    requestedFd.events = POLLIN;
-    mRequestedFds.insertAt(requestedFd, 0);
-
-    RequestedCallback requestedCallback;
-    requestedCallback.callback = NULL;
-    requestedCallback.looperCallback = NULL;
-    requestedCallback.ident = 0;
-    requestedCallback.data = NULL;
-    mRequestedCallbacks.insertAt(requestedCallback, 0);
-}
-
-void PollLoop::closeWakePipe() {
-    close(mWakeReadPipeFd);
-    close(mWakeWritePipeFd);
-
-    // Note: We don't need to remove the poll structure or callback entry because this
-    //       method is currently only called by the destructor.
-}
-
-int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) {
-    // If there are still pending fds from the last call, dispatch those
-    // first, to avoid an earlier fd from starving later ones.
-    const size_t pendingFdsCount = mPendingFds.size();
-    if (mPendingFdsPos < pendingFdsCount) {
-        const PendingCallback& pending = mPendingFds.itemAt(mPendingFdsPos);
-        mPendingFdsPos++;
-        if (outEvents != NULL) *outEvents = pending.events;
-        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);
-    }
-    mPolling = true;
-    mLock.unlock();
-
-    // Poll.
-    int32_t result;
-    size_t requestedCount = mRequestedFds.size();
-
-#if DEBUG_POLL_AND_WAKE
-    LOGD("%p ~ pollOnce - waiting on %d fds", this, requestedCount);
-    for (size_t i = 0; i < requestedCount; i++) {
-        LOGD("  fd %d - events %d", mRequestedFds[i].fd, mRequestedFds[i].events);
-    }
-#endif
-
-    int respondedCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis);
-
-    if (respondedCount == 0) {
-        // Timeout
-#if DEBUG_POLL_AND_WAKE
-        LOGD("%p ~ pollOnce - timeout", this);
-#endif
-        result = POLL_TIMEOUT;
-        goto Done;
-    }
-
-    if (respondedCount < 0) {
-        // Error
-#if DEBUG_POLL_AND_WAKE
-        LOGD("%p ~ pollOnce - error, errno=%d", this, errno);
-#endif
-        if (errno != EINTR) {
-            LOGW("Poll failed with an unexpected error, errno=%d", errno);
-        }
-        result = POLL_ERROR;
-        goto Done;
-    }
-
-#if DEBUG_POLL_AND_WAKE
-    LOGD("%p ~ pollOnce - handling responses from %d fds", this, respondedCount);
-    for (size_t i = 0; i < requestedCount; i++) {
-        LOGD("  fd %d - events %d, revents %d", mRequestedFds[i].fd, mRequestedFds[i].events,
-                mRequestedFds[i].revents);
-    }
-#endif
-
-    // Process the poll results.
-    mPendingCallbacks.clear();
-    mPendingFds.clear();
-    mPendingFdsPos = 0;
-    if (outEvents != NULL) *outEvents = 0;
-    if (outData != NULL) *outData = NULL;
-    
-    result = POLL_CALLBACK;
-    for (size_t i = 0; i < requestedCount; i++) {
-        const struct pollfd& requestedFd = mRequestedFds.itemAt(i);
-
-        short revents = requestedFd.revents;
-        if (revents) {
-            const RequestedCallback& requestedCallback = mRequestedCallbacks.itemAt(i);
-            PendingCallback pending;
-            pending.fd = requestedFd.fd;
-            pending.ident = requestedCallback.ident;
-            pending.events = revents;
-            pending.callback = requestedCallback.callback;
-            pending.looperCallback = requestedCallback.looperCallback;
-            pending.data = requestedCallback.data;
-
-            if (pending.callback || pending.looperCallback) {
-                mPendingCallbacks.push(pending);
-            } else if (pending.fd != mWakeReadPipeFd) {
-                if (result == POLL_CALLBACK) {
-                    result = pending.ident;
-                    if (outEvents != NULL) *outEvents = pending.events;
-                    if (outData != NULL) *outData = pending.data;
-                } else {
-                    mPendingFds.push(pending);
-                }
-            } else {
-#if DEBUG_POLL_AND_WAKE
-                LOGD("%p ~ pollOnce - awoken", this);
-#endif
-                char buffer[16];
-                ssize_t nRead;
-                do {
-                    nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
-                } while (nRead == sizeof(buffer));
-            }
-
-            respondedCount -= 1;
-            if (respondedCount == 0) {
-                break;
-            }
-        }
-    }
-
-Done:
-    // Set mPolling to false and wake up the wakeAndLock() waiters.
-    mLock.lock();
-    mPolling = false;
-    if (mWaiters != 0) {
-        mAwake.broadcast();
-    }
-    mLock.unlock();
-
-    if (result == POLL_CALLBACK || result >= 0) {
-        size_t pendingCount = mPendingCallbacks.size();
-        for (size_t i = 0; i < pendingCount; i++) {
-            const PendingCallback& pendingCallback = mPendingCallbacks.itemAt(i);
-#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
-            LOGD("%p ~ pollOnce - invoking callback for fd %d", this, pendingCallback.fd);
-#endif
-
-            bool keep = true;
-            if (pendingCallback.callback != NULL) {
-                keep = pendingCallback.callback(pendingCallback.fd, pendingCallback.events,
-                        pendingCallback.data);
-            } else {
-                keep = pendingCallback.looperCallback(pendingCallback.fd, pendingCallback.events,
-                        pendingCallback.data) != 0;
-            }
-            if (! keep) {
-                removeCallback(pendingCallback.fd);
-            }
-        }
-    }
-
-#if DEBUG_POLL_AND_WAKE
-    LOGD("%p ~ pollOnce - done", this);
-#endif
-    return result;
-}
-
-void PollLoop::wake() {
-#if DEBUG_POLL_AND_WAKE
-    LOGD("%p ~ wake", this);
-#endif
-
-    ssize_t nWrite = write(mWakeWritePipeFd, "W", 1);
-    if (nWrite != 1) {
-        if (errno != EAGAIN) {
-            LOGW("Could not write wake signal, errno=%d", errno);
-        }
-    }
-}
-
-bool PollLoop::getAllowNonCallbacks() const {
-    return mAllowNonCallbacks;
-}
-
-void PollLoop::setCallback(int fd, int ident, int events, Callback callback, void* data) {
-    setCallbackCommon(fd, ident, events, callback, NULL, data);
-}
-
-void PollLoop::setCallback(int fd, int events, Callback callback, void* data) {
-    setCallbackCommon(fd, POLL_CALLBACK, events, callback, NULL, data);
-}
-
-void PollLoop::setLooperCallback(int fd, int ident, int events, ALooper_callbackFunc* callback,
-        void* data) {
-    setCallbackCommon(fd, ident, events, NULL, callback, data);
-}
-
-void PollLoop::setCallbackCommon(int fd, int ident, int events, Callback callback,
-        ALooper_callbackFunc* looperCallback, void* data) {
-
-#if DEBUG_CALLBACKS
-    LOGD("%p ~ setCallback - fd=%d, events=%d", this, fd, events);
-#endif
-
-    if (! events) {
-        LOGE("Invalid attempt to set a callback with no selected poll events.");
-        removeCallback(fd);
-        return;
-    }
-
-    if (! callback && ! looperCallback && ! mAllowNonCallbacks) {
-        LOGE("Invalid attempt to set NULL callback but not allowed.");
-        removeCallback(fd);
-        return;
-    }
-    
-    wakeAndLock();
-
-    struct pollfd requestedFd;
-    requestedFd.fd = fd;
-    requestedFd.events = events;
-
-    RequestedCallback requestedCallback;
-    requestedCallback.callback = callback;
-    requestedCallback.looperCallback = looperCallback;
-    requestedCallback.ident = ident;
-    requestedCallback.data = data;
-
-    ssize_t index = getRequestIndexLocked(fd);
-    if (index < 0) {
-        mRequestedFds.push(requestedFd);
-        mRequestedCallbacks.push(requestedCallback);
-    } else {
-        mRequestedFds.replaceAt(requestedFd, size_t(index));
-        mRequestedCallbacks.replaceAt(requestedCallback, size_t(index));
-    }
-
-    mLock.unlock();
-}
-
-bool PollLoop::removeCallback(int fd) {
-#if DEBUG_CALLBACKS
-    LOGD("%p ~ removeCallback - fd=%d", this, fd);
-#endif
-
-    wakeAndLock();
-
-    ssize_t index = getRequestIndexLocked(fd);
-    if (index >= 0) {
-        mRequestedFds.removeAt(size_t(index));
-        mRequestedCallbacks.removeAt(size_t(index));
-    }
-
-    mLock.unlock();
-    return index >= 0;
-}
-
-ssize_t PollLoop::getRequestIndexLocked(int fd) {
-    size_t requestCount = mRequestedFds.size();
-
-    for (size_t i = 0; i < requestCount; i++) {
-        if (mRequestedFds.itemAt(i).fd == fd) {
-            return i;
-        }
-    }
-
-    return -1;
-}
-
-void PollLoop::wakeAndLock() {
-    mLock.lock();
-
-    mWaiters += 1;
-    while (mPolling) {
-        wake();
-        mAwake.wait(mLock);
-    }
-
-    mWaiters -= 1;
-    if (mWaiters == 0) {
-        mResume.signal();
-    }
-}
-
-} // namespace android
diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk
index 725de9c..00077ee 100644
--- a/libs/utils/tests/Android.mk
+++ b/libs/utils/tests/Android.mk
@@ -7,7 +7,7 @@
 # Build the unit tests.
 test_src_files := \
 	ObbFile_test.cpp \
-	PollLoop_test.cpp \
+	Looper_test.cpp \
 	String8_test.cpp
 
 shared_libraries := \
diff --git a/libs/utils/tests/Looper_test.cpp b/libs/utils/tests/Looper_test.cpp
new file mode 100644
index 0000000..afc92f8
--- /dev/null
+++ b/libs/utils/tests/Looper_test.cpp
@@ -0,0 +1,433 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+
+#include <utils/Looper.h>
+#include <utils/Timers.h>
+#include <utils/StopWatch.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "TestHelpers.h"
+
+// # of milliseconds to fudge stopwatch measurements
+#define TIMING_TOLERANCE_MS 25
+
+namespace android {
+
+class DelayedWake : public DelayedTask {
+    sp<Looper> mLooper;
+
+public:
+    DelayedWake(int delayMillis, const sp<Looper> looper) :
+        DelayedTask(delayMillis), mLooper(looper) {
+    }
+
+protected:
+    virtual void doTask() {
+        mLooper->wake();
+    }
+};
+
+class DelayedWriteSignal : public DelayedTask {
+    Pipe* mPipe;
+
+public:
+    DelayedWriteSignal(int delayMillis, Pipe* pipe) :
+        DelayedTask(delayMillis), mPipe(pipe) {
+    }
+
+protected:
+    virtual void doTask() {
+        mPipe->writeSignal();
+    }
+};
+
+class CallbackHandler {
+public:
+    void setCallback(const sp<Looper>& looper, int fd, int events) {
+        looper->addFd(fd, 0, events, staticHandler, this);
+    }
+
+protected:
+    virtual ~CallbackHandler() { }
+
+    virtual int handler(int fd, int events) = 0;
+
+private:
+    static int staticHandler(int fd, int events, void* data) {
+        return static_cast<CallbackHandler*>(data)->handler(fd, events);
+    }
+};
+
+class StubCallbackHandler : public CallbackHandler {
+public:
+    int nextResult;
+    int callbackCount;
+
+    int fd;
+    int events;
+
+    StubCallbackHandler(int nextResult) : nextResult(nextResult),
+            callbackCount(0), fd(-1), events(-1) {
+    }
+
+protected:
+    virtual int handler(int fd, int events) {
+        callbackCount += 1;
+        this->fd = fd;
+        this->events = events;
+        return nextResult;
+    }
+};
+
+class LooperTest : public testing::Test {
+protected:
+    sp<Looper> mLooper;
+
+    virtual void SetUp() {
+        mLooper = new Looper(true);
+    }
+
+    virtual void TearDown() {
+        mLooper.clear();
+    }
+};
+
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeout) {
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. equal timeout";
+    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+            << "pollOnce result should be ALOOPER_POLL_TIMEOUT";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturns) {
+    mLooper->wake();
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(1000);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because wake() was called before waiting";
+    EXPECT_EQ(ALOOPER_POLL_WAKE, result)
+            << "pollOnce result should be ALOOPER_POLL_CALLBACK because loop was awoken";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturns) {
+    sp<DelayedWake> delayedWake = new DelayedWake(100, mLooper);
+    delayedWake->run();
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(1000);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. equal wake delay";
+    EXPECT_EQ(ALOOPER_POLL_WAKE, result)
+            << "pollOnce result should be ALOOPER_POLL_CALLBACK because loop was awoken";
+}
+
+TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturns) {
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(0);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should be approx. zero";
+    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+            << "pollOnce result should be ALOOPER_POLL_TIMEOUT";
+}
+
+TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturns) {
+    Pipe pipe;
+    StubCallbackHandler handler(true);
+
+    handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(0);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should be approx. zero";
+    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+            << "pollOnce result should be ALOOPER_POLL_TIMEOUT";
+    EXPECT_EQ(0, handler.callbackCount)
+            << "callback should not have been invoked because FD was not signalled";
+}
+
+TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCallbackAndReturns) {
+    Pipe pipe;
+    StubCallbackHandler handler(true);
+
+    ASSERT_EQ(OK, pipe.writeSignal());
+    handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(0);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should be approx. zero";
+    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+            << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled";
+    EXPECT_EQ(1, handler.callbackCount)
+            << "callback should be invoked exactly once";
+    EXPECT_EQ(pipe.receiveFd, handler.fd)
+            << "callback should have received pipe fd as parameter";
+    EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events)
+            << "callback should have received ALOOPER_EVENT_INPUT as events";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturns) {
+    Pipe pipe;
+    StubCallbackHandler handler(true);
+
+    handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. equal timeout";
+    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+            << "pollOnce result should be ALOOPER_POLL_TIMEOUT";
+    EXPECT_EQ(0, handler.callbackCount)
+            << "callback should not have been invoked because FD was not signalled";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_ImmediatelyInvokesCallbackAndReturns) {
+    Pipe pipe;
+    StubCallbackHandler handler(true);
+
+    pipe.writeSignal();
+    handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    ASSERT_EQ(OK, pipe.readSignal())
+            << "signal should actually have been written";
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should be approx. zero";
+    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+            << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled";
+    EXPECT_EQ(1, handler.callbackCount)
+            << "callback should be invoked exactly once";
+    EXPECT_EQ(pipe.receiveFd, handler.fd)
+            << "callback should have received pipe fd as parameter";
+    EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events)
+            << "callback should have received ALOOPER_EVENT_INPUT as events";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturns) {
+    Pipe pipe;
+    StubCallbackHandler handler(true);
+    sp<DelayedWriteSignal> delayedWriteSignal = new DelayedWriteSignal(100, & pipe);
+
+    handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+    delayedWriteSignal->run();
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(1000);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    ASSERT_EQ(OK, pipe.readSignal())
+            << "signal should actually have been written";
+    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. equal signal delay";
+    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+            << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled";
+    EXPECT_EQ(1, handler.callbackCount)
+            << "callback should be invoked exactly once";
+    EXPECT_EQ(pipe.receiveFd, handler.fd)
+            << "callback should have received pipe fd as parameter";
+    EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events)
+            << "callback should have received ALOOPER_EVENT_INPUT as events";
+}
+
+TEST_F(LooperTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) {
+    Pipe pipe;
+    StubCallbackHandler handler(true);
+
+    handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+    pipe.writeSignal(); // would cause FD to be considered signalled
+    mLooper->removeFd(pipe.receiveFd);
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    ASSERT_EQ(OK, pipe.readSignal())
+            << "signal should actually have been written";
+    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. equal timeout because FD was no longer registered";
+    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+            << "pollOnce result should be ALOOPER_POLL_TIMEOUT";
+    EXPECT_EQ(0, handler.callbackCount)
+            << "callback should not be invoked";
+}
+
+TEST_F(LooperTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedAgainLater) {
+    Pipe pipe;
+    StubCallbackHandler handler(false);
+
+    handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+
+    // First loop: Callback is registered and FD is signalled.
+    pipe.writeSignal();
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(0);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    ASSERT_EQ(OK, pipe.readSignal())
+            << "signal should actually have been written";
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. equal zero because FD was already signalled";
+    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+            << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled";
+    EXPECT_EQ(1, handler.callbackCount)
+            << "callback should be invoked";
+
+    // Second loop: Callback is no longer registered and FD is signalled.
+    pipe.writeSignal();
+
+    stopWatch.reset();
+    result = mLooper->pollOnce(0);
+    elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    ASSERT_EQ(OK, pipe.readSignal())
+            << "signal should actually have been written";
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. equal zero because timeout was zero";
+    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+            << "pollOnce result should be ALOOPER_POLL_TIMEOUT";
+    EXPECT_EQ(1, handler.callbackCount)
+            << "callback should not be invoked this time";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonCallbackFdIsSignalled_ReturnsIdent) {
+    const int expectedIdent = 5;
+    void* expectedData = this;
+
+    Pipe pipe;
+
+    pipe.writeSignal();
+    mLooper->addFd(pipe.receiveFd, expectedIdent, ALOOPER_EVENT_INPUT, NULL, expectedData);
+
+    StopWatch stopWatch("pollOnce");
+    int fd;
+    int events;
+    void* data;
+    int result = mLooper->pollOnce(100, &fd, &events, &data);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    ASSERT_EQ(OK, pipe.readSignal())
+            << "signal should actually have been written";
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should be approx. zero";
+    EXPECT_EQ(expectedIdent, result)
+            << "pollOnce result should be the ident of the FD that was signalled";
+    EXPECT_EQ(pipe.receiveFd, fd)
+            << "pollOnce should have returned the received pipe fd";
+    EXPECT_EQ(ALOOPER_EVENT_INPUT, events)
+            << "pollOnce should have returned ALOOPER_EVENT_INPUT as events";
+    EXPECT_EQ(expectedData, data)
+            << "pollOnce should have returned the data";
+}
+
+TEST_F(LooperTest, AddFd_WhenCallbackAdded_ReturnsOne) {
+    Pipe pipe;
+    int result = mLooper->addFd(pipe.receiveFd, 0, ALOOPER_EVENT_INPUT, NULL, NULL);
+
+    EXPECT_EQ(1, result)
+            << "addFd should return 1 because FD was added";
+}
+
+TEST_F(LooperTest, AddFd_WhenEventsIsZero_ReturnsError) {
+    Pipe pipe;
+    int result = mLooper->addFd(pipe.receiveFd, 0, 0, NULL, NULL);
+
+    EXPECT_EQ(-1, result)
+            << "addFd should return -1 because arguments were invalid";
+}
+
+TEST_F(LooperTest, AddFd_WhenIdentIsNegativeAndCallbackIsNull_ReturnsError) {
+    Pipe pipe;
+    int result = mLooper->addFd(pipe.receiveFd, -1, ALOOPER_EVENT_INPUT, NULL, NULL);
+
+    EXPECT_EQ(-1, result)
+            << "addFd should return -1 because arguments were invalid";
+}
+
+TEST_F(LooperTest, AddFd_WhenNoCallbackAndAllowNonCallbacksIsFalse_ReturnsError) {
+    Pipe pipe;
+    sp<Looper> looper = new Looper(false /*allowNonCallbacks*/);
+    int result = looper->addFd(pipe.receiveFd, 0, 0, NULL, NULL);
+
+    EXPECT_EQ(-1, result)
+            << "addFd should return -1 because arguments were invalid";
+}
+
+TEST_F(LooperTest, RemoveFd_WhenCallbackNotAdded_ReturnsZero) {
+    int result = mLooper->removeFd(1);
+
+    EXPECT_EQ(0, result)
+            << "removeFd should return 0 because FD not registered";
+}
+
+TEST_F(LooperTest, RemoveFd_WhenCallbackAddedThenRemovedTwice_ReturnsOnceFirstTimeAndReturnsZeroSecondTime) {
+    Pipe pipe;
+    StubCallbackHandler handler(false);
+    handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+
+    // First time.
+    int result = mLooper->removeFd(pipe.receiveFd);
+
+    EXPECT_EQ(1, result)
+            << "removeFd should return 1 first time because FD was registered";
+
+    // Second time.
+    result = mLooper->removeFd(pipe.receiveFd);
+
+    EXPECT_EQ(0, result)
+            << "removeFd should return 0 second time because FD was no longer registered";
+}
+
+TEST_F(LooperTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInvoked) {
+    Pipe pipe;
+    StubCallbackHandler handler1(true);
+    StubCallbackHandler handler2(true);
+
+    handler1.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+    handler2.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); // replace it
+    pipe.writeSignal(); // would cause FD to be considered signalled
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    ASSERT_EQ(OK, pipe.readSignal())
+            << "signal should actually have been written";
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because FD was already signalled";
+    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+            << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled";
+    EXPECT_EQ(0, handler1.callbackCount)
+            << "original handler callback should not be invoked because it was replaced";
+    EXPECT_EQ(1, handler2.callbackCount)
+            << "replacement handler callback should be invoked";
+}
+
+
+} // namespace android
diff --git a/libs/utils/tests/PollLoop_test.cpp b/libs/utils/tests/PollLoop_test.cpp
deleted file mode 100644
index 02f1808..0000000
--- a/libs/utils/tests/PollLoop_test.cpp
+++ /dev/null
@@ -1,370 +0,0 @@
-//
-// Copyright 2010 The Android Open Source Project
-//
-
-#include <utils/PollLoop.h>
-#include <utils/Timers.h>
-#include <utils/StopWatch.h>
-#include <gtest/gtest.h>
-#include <unistd.h>
-#include <time.h>
-
-#include "TestHelpers.h"
-
-// # of milliseconds to fudge stopwatch measurements
-#define TIMING_TOLERANCE_MS 25
-
-namespace android {
-
-class DelayedWake : public DelayedTask {
-    sp<PollLoop> mPollLoop;
-
-public:
-    DelayedWake(int delayMillis, const sp<PollLoop> pollLoop) :
-        DelayedTask(delayMillis), mPollLoop(pollLoop) {
-    }
-
-protected:
-    virtual void doTask() {
-        mPollLoop->wake();
-    }
-};
-
-class DelayedWriteSignal : public DelayedTask {
-    Pipe* mPipe;
-
-public:
-    DelayedWriteSignal(int delayMillis, Pipe* pipe) :
-        DelayedTask(delayMillis), mPipe(pipe) {
-    }
-
-protected:
-    virtual void doTask() {
-        mPipe->writeSignal();
-    }
-};
-
-class CallbackHandler {
-public:
-    void setCallback(const sp<PollLoop>& pollLoop, int fd, int events) {
-        pollLoop->setCallback(fd, events, staticHandler, this);
-    }
-
-protected:
-    virtual ~CallbackHandler() { }
-
-    virtual bool handler(int fd, int events) = 0;
-
-private:
-    static bool staticHandler(int fd, int events, void* data) {
-        return static_cast<CallbackHandler*>(data)->handler(fd, events);
-    }
-};
-
-class StubCallbackHandler : public CallbackHandler {
-public:
-    bool nextResult;
-    int callbackCount;
-
-    int fd;
-    int events;
-
-    StubCallbackHandler(bool nextResult) : nextResult(nextResult),
-            callbackCount(0), fd(-1), events(-1) {
-    }
-
-protected:
-    virtual bool handler(int fd, int events) {
-        callbackCount += 1;
-        this->fd = fd;
-        this->events = events;
-        return nextResult;
-    }
-};
-
-class PollLoopTest : public testing::Test {
-protected:
-    sp<PollLoop> mPollLoop;
-
-    virtual void SetUp() {
-        mPollLoop = new PollLoop(false);
-    }
-
-    virtual void TearDown() {
-        mPollLoop.clear();
-    }
-};
-
-
-TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeoutAndReturnsFalse) {
-    StopWatch stopWatch("pollOnce");
-    int32_t result = mPollLoop->pollOnce(100);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. equal timeout";
-    EXPECT_EQ(result, PollLoop::POLL_TIMEOUT)
-            << "pollOnce result should be POLL_TIMEOUT";
-}
-
-TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturnsTrue) {
-    mPollLoop->wake();
-
-    StopWatch stopWatch("pollOnce");
-    int32_t result = mPollLoop->pollOnce(1000);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. zero because wake() was called before waiting";
-    EXPECT_EQ(result, PollLoop::POLL_CALLBACK)
-            << "pollOnce result should be POLL_CALLBACK because loop was awoken";
-}
-
-TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturnsTrue) {
-    sp<DelayedWake> delayedWake = new DelayedWake(100, mPollLoop);
-    delayedWake->run();
-
-    StopWatch stopWatch("pollOnce");
-    int32_t result = mPollLoop->pollOnce(1000);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. equal wake delay";
-    EXPECT_EQ(result, PollLoop::POLL_CALLBACK)
-            << "pollOnce result should be POLL_CALLBACK because loop was awoken";
-}
-
-TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturnsFalse) {
-    StopWatch stopWatch("pollOnce");
-    int32_t result = mPollLoop->pollOnce(0);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should be approx. zero";
-    EXPECT_EQ(result, PollLoop::POLL_TIMEOUT)
-            << "pollOnce result should be POLL_TIMEOUT";
-}
-
-TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturnsFalse) {
-    Pipe pipe;
-    StubCallbackHandler handler(true);
-
-    handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
-
-    StopWatch stopWatch("pollOnce");
-    int32_t result = mPollLoop->pollOnce(0);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should be approx. zero";
-    EXPECT_EQ(result, PollLoop::POLL_TIMEOUT)
-            << "pollOnce result should be POLL_TIMEOUT";
-    EXPECT_EQ(0, handler.callbackCount)
-            << "callback should not have been invoked because FD was not signalled";
-}
-
-TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCallbackAndReturnsTrue) {
-    Pipe pipe;
-    StubCallbackHandler handler(true);
-
-    ASSERT_EQ(OK, pipe.writeSignal());
-    handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
-
-    StopWatch stopWatch("pollOnce");
-    int32_t result = mPollLoop->pollOnce(0);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should be approx. zero";
-    EXPECT_EQ(result, PollLoop::POLL_CALLBACK)
-            << "pollOnce result should be POLL_CALLBACK because FD was signalled";
-    EXPECT_EQ(1, handler.callbackCount)
-            << "callback should be invoked exactly once";
-    EXPECT_EQ(pipe.receiveFd, handler.fd)
-            << "callback should have received pipe fd as parameter";
-    EXPECT_EQ(POLL_IN, handler.events)
-            << "callback should have received POLL_IN as events";
-}
-
-TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturnsFalse) {
-    Pipe pipe;
-    StubCallbackHandler handler(true);
-
-    handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
-
-    StopWatch stopWatch("pollOnce");
-    int32_t result = mPollLoop->pollOnce(100);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. equal timeout";
-    EXPECT_EQ(result, PollLoop::POLL_TIMEOUT)
-            << "pollOnce result should be POLL_TIMEOUT";
-    EXPECT_EQ(0, handler.callbackCount)
-            << "callback should not have been invoked because FD was not signalled";
-}
-
-TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_ImmediatelyInvokesCallbackAndReturnsTrue) {
-    Pipe pipe;
-    StubCallbackHandler handler(true);
-
-    pipe.writeSignal();
-    handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
-
-    StopWatch stopWatch("pollOnce");
-    int32_t result = mPollLoop->pollOnce(100);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    ASSERT_EQ(OK, pipe.readSignal())
-            << "signal should actually have been written";
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should be approx. zero";
-    EXPECT_EQ(result, PollLoop::POLL_CALLBACK)
-            << "pollOnce result should be POLL_CALLBACK because FD was signalled";
-    EXPECT_EQ(1, handler.callbackCount)
-            << "callback should be invoked exactly once";
-    EXPECT_EQ(pipe.receiveFd, handler.fd)
-            << "callback should have received pipe fd as parameter";
-    EXPECT_EQ(POLL_IN, handler.events)
-            << "callback should have received POLL_IN as events";
-}
-
-TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturnsTrue) {
-    Pipe pipe;
-    StubCallbackHandler handler(true);
-    sp<DelayedWriteSignal> delayedWriteSignal = new DelayedWriteSignal(100, & pipe);
-
-    handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
-    delayedWriteSignal->run();
-
-    StopWatch stopWatch("pollOnce");
-    int32_t result = mPollLoop->pollOnce(1000);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    ASSERT_EQ(OK, pipe.readSignal())
-            << "signal should actually have been written";
-    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. equal signal delay";
-    EXPECT_EQ(result, PollLoop::POLL_CALLBACK)
-            << "pollOnce result should be POLL_CALLBACK because FD was signalled";
-    EXPECT_EQ(1, handler.callbackCount)
-            << "callback should be invoked exactly once";
-    EXPECT_EQ(pipe.receiveFd, handler.fd)
-            << "callback should have received pipe fd as parameter";
-    EXPECT_EQ(POLL_IN, handler.events)
-            << "callback should have received POLL_IN as events";
-}
-
-TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) {
-    Pipe pipe;
-    StubCallbackHandler handler(true);
-
-    handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
-    pipe.writeSignal(); // would cause FD to be considered signalled
-    mPollLoop->removeCallback(pipe.receiveFd);
-
-    StopWatch stopWatch("pollOnce");
-    int32_t result = mPollLoop->pollOnce(100);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    ASSERT_EQ(OK, pipe.readSignal())
-            << "signal should actually have been written";
-    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. equal timeout because FD was no longer registered";
-    EXPECT_EQ(result, PollLoop::POLL_TIMEOUT)
-            << "pollOnce result should be POLL_TIMEOUT";
-    EXPECT_EQ(0, handler.callbackCount)
-            << "callback should not be invoked";
-}
-
-TEST_F(PollLoopTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedAgainLater) {
-    Pipe pipe;
-    StubCallbackHandler handler(false);
-
-    handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
-
-    // First loop: Callback is registered and FD is signalled.
-    pipe.writeSignal();
-
-    StopWatch stopWatch("pollOnce");
-    int32_t result = mPollLoop->pollOnce(0);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    ASSERT_EQ(OK, pipe.readSignal())
-            << "signal should actually have been written";
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. equal zero because FD was already signalled";
-    EXPECT_EQ(result, PollLoop::POLL_CALLBACK)
-            << "pollOnce result should be POLL_CALLBACK because FD was signalled";
-    EXPECT_EQ(1, handler.callbackCount)
-            << "callback should be invoked";
-
-    // Second loop: Callback is no longer registered and FD is signalled.
-    pipe.writeSignal();
-
-    stopWatch.reset();
-    result = mPollLoop->pollOnce(0);
-    elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    ASSERT_EQ(OK, pipe.readSignal())
-            << "signal should actually have been written";
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. equal zero because timeout was zero";
-    EXPECT_EQ(result, PollLoop::POLL_TIMEOUT)
-            << "pollOnce result should be POLL_TIMEOUT";
-    EXPECT_EQ(1, handler.callbackCount)
-            << "callback should not be invoked this time";
-}
-
-TEST_F(PollLoopTest, RemoveCallback_WhenCallbackNotAdded_ReturnsFalse) {
-    bool result = mPollLoop->removeCallback(1);
-
-    EXPECT_FALSE(result)
-            << "removeCallback should return false because FD not registered";
-}
-
-TEST_F(PollLoopTest, RemoveCallback_WhenCallbackAddedThenRemovedTwice_ReturnsTrueFirstTimeAndReturnsFalseSecondTime) {
-    Pipe pipe;
-    StubCallbackHandler handler(false);
-    handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
-
-    // First time.
-    bool result = mPollLoop->removeCallback(pipe.receiveFd);
-
-    EXPECT_TRUE(result)
-            << "removeCallback should return true first time because FD was registered";
-
-    // Second time.
-    result = mPollLoop->removeCallback(pipe.receiveFd);
-
-    EXPECT_FALSE(result)
-            << "removeCallback should return false second time because FD was no longer registered";
-}
-
-TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInvoked) {
-    Pipe pipe;
-    StubCallbackHandler handler1(true);
-    StubCallbackHandler handler2(true);
-
-    handler1.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
-    handler2.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); // replace it
-    pipe.writeSignal(); // would cause FD to be considered signalled
-
-    StopWatch stopWatch("pollOnce");
-    int32_t result = mPollLoop->pollOnce(100);
-    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
-
-    ASSERT_EQ(OK, pipe.readSignal())
-            << "signal should actually have been written";
-    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
-            << "elapsed time should approx. zero because FD was already signalled";
-    EXPECT_EQ(result, PollLoop::POLL_CALLBACK)
-            << "pollOnce result should be POLL_CALLBACK because FD was signalled";
-    EXPECT_EQ(0, handler1.callbackCount)
-            << "original handler callback should not be invoked because it was replaced";
-    EXPECT_EQ(1, handler2.callbackCount)
-            << "replacement handler callback should be invoked";
-}
-
-
-} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index 2eac0a8..0515110 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -296,6 +296,10 @@
     return mNativeWindow->compositionComplete();
 }
 
+int DisplayHardware::getCurrentBufferIndex() const {
+    return mNativeWindow->getCurrentBufferIndex();
+}
+
 void DisplayHardware::flip(const Region& dirty) const
 {
     checkGLErrors();
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
index 66bf521..2d7900c 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -87,6 +87,9 @@
         return Rect(mWidth, mHeight);
     }
 
+    // only for debugging
+    int getCurrentBufferIndex() const;
+
 private:
     void init(uint32_t displayIndex) __attribute__((noinline));
     void fini() __attribute__((noinline));
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 637ae48..f199ca9 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -38,6 +38,7 @@
 #include <utils/StopWatch.h>
 
 #include <ui/GraphicBufferAllocator.h>
+#include <ui/GraphicLog.h>
 #include <ui/PixelFormat.h>
 
 #include <pixelflinger/pixelflinger.h>
@@ -371,15 +372,25 @@
     const DisplayHardware& hw(graphicPlane(0).displayHardware());
     if (LIKELY(hw.canDraw() && !isFrozen())) {
         // repaint the framebuffer (if needed)
+
+        const int index = hw.getCurrentBufferIndex();
+        GraphicLog& logger(GraphicLog::getInstance());
+
+        logger.log(GraphicLog::SF_REPAINT, index);
         handleRepaint();
 
         // inform the h/w that we're done compositing
+        logger.log(GraphicLog::SF_COMPOSITION_COMPLETE, index);
         hw.compositionComplete();
 
         // release the clients before we flip ('cause flip might block)
+        logger.log(GraphicLog::SF_UNLOCK_CLIENTS, index);
         unlockClients();
 
+        logger.log(GraphicLog::SF_SWAP_BUFFERS, index);
         postFramebuffer();
+
+        logger.log(GraphicLog::SF_REPAINT_DONE, index);
     } else {
         // pretend we did the post
         unlockClients();
@@ -1470,8 +1481,7 @@
         int n;
         switch (code) {
             case 1000: // SHOW_CPU, NOT SUPPORTED ANYMORE
-                return NO_ERROR;
-            case 1001:  // SHOW_FPS, NOT SUPPORTED ANYMORE
+            case 1001: // SHOW_FPS, NOT SUPPORTED ANYMORE
                 return NO_ERROR;
             case 1002:  // SHOW_UPDATES
                 n = data.readInt32();
@@ -1492,6 +1502,11 @@
                 setTransactionFlags(eTransactionNeeded|eTraversalNeeded);
                 return NO_ERROR;
             }
+            case 1006:{ // enable/disable GraphicLog
+                int enabled = data.readInt32();
+                GraphicLog::getInstance().setEnabled(enabled);
+                return NO_ERROR;
+            }
             case 1007: // set mFreezeCount
                 mFreezeCount = data.readInt32();
                 mFreezeDisplayTime = 0;