Optimize EventHub to process events in big chunks.

When 10 fingers are down, reduces the CPU time spent by the InputReader
thread from ~30% to ~5% on Stingray.

Change-Id: Icdf7c91cd5d9039ac3beb38ba9021a05e7fabc80
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index b90571b..65bc1b9 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -127,9 +127,11 @@
         mError(NO_INIT), mBuiltInKeyboardId(-1), mNextDeviceId(1),
         mOpeningDevices(0), mClosingDevices(0),
         mOpened(false), mNeedToSendFinishedDeviceScan(false),
-        mInputBufferIndex(0), mInputBufferCount(0), mInputFdIndex(0) {
+        mInputFdIndex(1) {
     acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
+
     memset(mSwitches, 0, sizeof(mSwitches));
+    mNumCpus = sysconf(_SC_NPROCESSORS_ONLN);
 }
 
 EventHub::~EventHub(void) {
@@ -445,17 +447,10 @@
     return NULL;
 }
 
-bool EventHub::getEvent(int timeoutMillis, RawEvent* outEvent) {
-    outEvent->deviceId = 0;
-    outEvent->type = 0;
-    outEvent->scanCode = 0;
-    outEvent->keyCode = 0;
-    outEvent->flags = 0;
-    outEvent->value = 0;
-    outEvent->when = 0;
-
-    // Note that we only allow one caller to getEvent(), so don't need
+size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
+    // Note that we only allow one caller to getEvents(), so don't need
     // to do locking here...  only when adding/removing devices.
+    assert(bufferSize >= 1);
 
     if (!mOpened) {
         mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
@@ -463,99 +458,62 @@
         mNeedToSendFinishedDeviceScan = true;
     }
 
+    struct input_event readBuffer[bufferSize];
+
+    RawEvent* event = buffer;
+    size_t capacity = bufferSize;
     for (;;) {
+        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+
         // Report any devices that had last been added/removed.
-        if (mClosingDevices != NULL) {
+        while (mClosingDevices) {
             Device* device = mClosingDevices;
             LOGV("Reporting device closed: id=%d, name=%s\n",
                  device->id, device->path.string());
             mClosingDevices = device->next;
-            if (device->id == mBuiltInKeyboardId) {
-                outEvent->deviceId = 0;
-            } else {
-                outEvent->deviceId = device->id;
-            }
-            outEvent->type = DEVICE_REMOVED;
-            outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
+            event->when = now;
+            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
+            event->type = DEVICE_REMOVED;
+            event += 1;
             delete device;
             mNeedToSendFinishedDeviceScan = true;
-            return true;
+            if (--capacity == 0) {
+                break;
+            }
         }
 
-        if (mOpeningDevices != NULL) {
+        while (mOpeningDevices != NULL) {
             Device* device = mOpeningDevices;
             LOGV("Reporting device opened: id=%d, name=%s\n",
                  device->id, device->path.string());
             mOpeningDevices = device->next;
-            if (device->id == mBuiltInKeyboardId) {
-                outEvent->deviceId = 0;
-            } else {
-                outEvent->deviceId = device->id;
-            }
-            outEvent->type = DEVICE_ADDED;
-            outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
+            event->when = now;
+            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
+            event->type = DEVICE_ADDED;
+            event += 1;
             mNeedToSendFinishedDeviceScan = true;
-            return true;
+            if (--capacity == 0) {
+                break;
+            }
         }
 
         if (mNeedToSendFinishedDeviceScan) {
             mNeedToSendFinishedDeviceScan = false;
-            outEvent->type = FINISHED_DEVICE_SCAN;
-            outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
-            return true;
+            event->when = now;
+            event->type = FINISHED_DEVICE_SCAN;
+            event += 1;
+            if (--capacity == 0) {
+                break;
+            }
         }
 
         // Grab the next input event.
+        // mInputFdIndex is initially 1 because index 0 is used for inotify.
         bool deviceWasRemoved = false;
-        for (;;) {
-            // Consume buffered input events, if any.
-            if (mInputBufferIndex < mInputBufferCount) {
-                const struct input_event& iev = mInputBufferData[mInputBufferIndex++];
-                const Device* device = mDevices[mInputFdIndex];
-
-                LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", device->path.string(),
-                     (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value);
-                if (device->id == mBuiltInKeyboardId) {
-                    outEvent->deviceId = 0;
-                } else {
-                    outEvent->deviceId = device->id;
-                }
-                outEvent->type = iev.type;
-                outEvent->scanCode = iev.code;
-                outEvent->flags = 0;
-                if (iev.type == EV_KEY) {
-                    outEvent->keyCode = AKEYCODE_UNKNOWN;
-                    if (device->keyMap.haveKeyLayout()) {
-                        status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code,
-                                &outEvent->keyCode, &outEvent->flags);
-                        LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
-                                iev.code, outEvent->keyCode, outEvent->flags, err);
-                    }
-                } else {
-                    outEvent->keyCode = iev.code;
-                }
-                outEvent->value = iev.value;
-
-                // Use an event timestamp in the same timebase as
-                // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis()
-                // as expected by the rest of the system.
-                outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
-                return true;
-            }
-
-            // Finish reading all events from devices identified in previous poll().
-            // This code assumes that mInputDeviceIndex is initially 0 and that the
-            // revents member of pollfd is initialized to 0 when the device is first added.
-            // Since mFds[0] is used for inotify, we process regular events starting at index 1.
-            mInputFdIndex += 1;
-            if (mInputFdIndex >= mFds.size()) {
-                break;
-            }
-
+        while (mInputFdIndex < mFds.size()) {
             const struct pollfd& pfd = mFds[mInputFdIndex];
             if (pfd.revents & POLLIN) {
-                int32_t readSize = read(pfd.fd, mInputBufferData,
-                        sizeof(struct input_event) * INPUT_BUFFER_SIZE);
+                int32_t readSize = read(pfd.fd, readBuffer, sizeof(struct input_event) * capacity);
                 if (readSize < 0) {
                     if (errno == ENODEV) {
                         deviceWasRemoved = true;
@@ -566,11 +524,43 @@
                     }
                 } else if ((readSize % sizeof(struct input_event)) != 0) {
                     LOGE("could not get event (wrong size: %d)", readSize);
+                } else if (readSize == 0) { // eof
+                    deviceWasRemoved = true;
+                    break;
                 } else {
-                    mInputBufferCount = size_t(readSize) / sizeof(struct input_event);
-                    mInputBufferIndex = 0;
+                    const Device* device = mDevices[mInputFdIndex];
+                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
+
+                    size_t count = size_t(readSize) / sizeof(struct input_event);
+                    for (size_t i = 0; i < count; i++) {
+                        const struct input_event& iev = readBuffer[i];
+                        LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d",
+                                device->path.string(),
+                                (int) iev.time.tv_sec, (int) iev.time.tv_usec,
+                                iev.type, iev.code, iev.value);
+
+                        event->when = now;
+                        event->deviceId = deviceId;
+                        event->type = iev.type;
+                        event->scanCode = iev.code;
+                        event->value = iev.value;
+                        event->keyCode = AKEYCODE_UNKNOWN;
+                        event->flags = 0;
+                        if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) {
+                            status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code,
+                                        &event->keyCode, &event->flags);
+                            LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
+                                    iev.code, event->keyCode, event->flags, err);
+                        }
+                        event += 1;
+                    }
+                    capacity -= count;
+                    if (capacity == 0) {
+                        break;
+                    }
                 }
             }
+            mInputFdIndex += 1;
         }
 
         // Handle the case where a device has been removed but INotify has not yet noticed.
@@ -586,10 +576,16 @@
         if(mFds[0].revents & POLLIN) {
             readNotify(mFds[0].fd);
             mFds.editItemAt(0).revents = 0;
+            mInputFdIndex = mFds.size();
             continue; // report added or removed devices immediately
         }
 #endif
 
+        // Return now if we have collected any events, otherwise poll.
+        if (event != buffer) {
+            break;
+        }
+
         // Poll for events.  Mind the wake lock dance!
         // We hold a wake lock at all times except during poll().  This works due to some
         // subtle choreography.  When a device driver has pending (unread) events, it acquires
@@ -608,19 +604,36 @@
         acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
 
         if (pollResult == 0) {
-            // Timed out.
-            return false;
+            break; // timed out
         }
         if (pollResult < 0) {
+            // Sleep after errors to avoid locking up the system.
+            // Hopefully the error is transient.
             if (errno != EINTR) {
                 LOGW("poll failed (errno=%d)\n", errno);
                 usleep(100000);
             }
+        } else {
+            // On an SMP system, it is possible for the framework to read input events
+            // faster than the kernel input device driver can produce a complete packet.
+            // Because poll() wakes up as soon as the first input event becomes available,
+            // the framework will often end up reading one event at a time until the
+            // packet is complete.  Instead of one call to read() returning 71 events,
+            // it could take 71 calls to read() each returning 1 event.
+            //
+            // Sleep for a short period of time after waking up from the poll() to give
+            // the kernel time to finish writing the entire packet of input events.
+            if (mNumCpus > 1) {
+                usleep(250);
+            }
         }
 
         // Prepare to process all of the FDs we just polled.
-        mInputFdIndex = 0;
+        mInputFdIndex = 1;
     }
+
+    // All done, return the number of events we read.
+    return event - buffer;
 }
 
 /*
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 1d287ac..4d26a95 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -157,6 +157,8 @@
         // Sent when all added/removed devices from the most recent scan have been reported.
         // This event is always sent at least once.
         FINISHED_DEVICE_SCAN = 0x30000000,
+
+        FIRST_SYNTHETIC_EVENT = DEVICE_ADDED,
     };
 
     virtual uint32_t getDeviceClasses(int32_t deviceId) const = 0;
@@ -181,7 +183,7 @@
     virtual void addExcludedDevice(const char* deviceName) = 0;
 
     /*
-     * Wait for the next event to become available and return it.
+     * Wait for events to become available and returns them.
      * After returning, the EventHub holds onto a wake lock until the next call to getEvent.
      * This ensures that the device will not go to sleep while the event is being processed.
      * If the device needs to remain awake longer than that, then the caller is responsible
@@ -190,9 +192,9 @@
      * The timeout is advisory only.  If the device is asleep, it will not wake just to
      * service the timeout.
      *
-     * Returns true if an event was obtained, false if the timeout expired.
+     * Returns the number of events obtained, or 0 if the timeout expired.
      */
-    virtual bool getEvent(int timeoutMillis, RawEvent* outEvent) = 0;
+    virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) = 0;
 
     /*
      * Query current input state.
@@ -249,7 +251,7 @@
     virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
             const int32_t* keyCodes, uint8_t* outFlags) const;
 
-    virtual bool getEvent(int timeoutMillis, RawEvent* outEvent);
+    virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize);
 
     virtual bool hasLed(int32_t deviceId, int32_t led) const;
     virtual void setLedState(int32_t deviceId, int32_t led, bool on);
@@ -336,11 +338,11 @@
     // device ids that report particular switches.
     int32_t mSwitches[SW_MAX + 1];
 
-    static const int INPUT_BUFFER_SIZE = 64;
-    struct input_event mInputBufferData[INPUT_BUFFER_SIZE];
-    size_t mInputBufferIndex;
-    size_t mInputBufferCount;
+    // The index of the next file descriptor that needs to be read.
     size_t mInputFdIndex;
+
+    // Set to the number of CPUs.
+    int32_t mNumCpus;
 };
 
 }; // namespace android
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 182bd50..94753bf 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -250,15 +250,11 @@
         timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
     }
 
-    RawEvent rawEvent;
-    if (mEventHub->getEvent(timeoutMillis, &rawEvent)) {
-#if DEBUG_RAW_EVENTS
-        LOGD("Input event: device=%d type=0x%04x scancode=0x%04x keycode=0x%04x value=0x%04x",
-                rawEvent.deviceId, rawEvent.type, rawEvent.scanCode, rawEvent.keyCode,
-                rawEvent.value);
-#endif
-        process(&rawEvent);
-    } else {
+    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
+    if (count) {
+        processEvents(mEventBuffer, count);
+    }
+    if (!count || timeoutMillis == 0) {
         nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
 #if DEBUG_RAW_EVENTS
         LOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
@@ -268,23 +264,41 @@
     }
 }
 
-void InputReader::process(const RawEvent* rawEvent) {
-    switch (rawEvent->type) {
-    case EventHubInterface::DEVICE_ADDED:
-        addDevice(rawEvent->deviceId);
-        break;
-
-    case EventHubInterface::DEVICE_REMOVED:
-        removeDevice(rawEvent->deviceId);
-        break;
-
-    case EventHubInterface::FINISHED_DEVICE_SCAN:
-        handleConfigurationChanged(rawEvent->when);
-        break;
-
-    default:
-        consumeEvent(rawEvent);
-        break;
+void InputReader::processEvents(const RawEvent* rawEvents, size_t count) {
+    for (const RawEvent* rawEvent = rawEvents; count;) {
+        int32_t type = rawEvent->type;
+        size_t batchSize = 1;
+        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
+            int32_t deviceId = rawEvent->deviceId;
+            while (batchSize < count) {
+                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
+                        || rawEvent[batchSize].deviceId != deviceId) {
+                    break;
+                }
+                batchSize += 1;
+            }
+#if DEBUG_RAW_EVENTS
+            LOGD("BatchSize: %d Count: %d", batchSize, count);
+#endif
+            processEventsForDevice(deviceId, rawEvent, batchSize);
+        } else {
+            switch (rawEvent->type) {
+            case EventHubInterface::DEVICE_ADDED:
+                addDevice(rawEvent->deviceId);
+                break;
+            case EventHubInterface::DEVICE_REMOVED:
+                removeDevice(rawEvent->deviceId);
+                break;
+            case EventHubInterface::FINISHED_DEVICE_SCAN:
+                handleConfigurationChanged(rawEvent->when);
+                break;
+            default:
+                assert(false); // can't happen
+                break;
+            }
+        }
+        count -= batchSize;
+        rawEvent += batchSize;
     }
 }
 
@@ -405,9 +419,8 @@
     return device;
 }
 
-void InputReader::consumeEvent(const RawEvent* rawEvent) {
-    int32_t deviceId = rawEvent->deviceId;
-
+void InputReader::processEventsForDevice(int32_t deviceId,
+        const RawEvent* rawEvents, size_t count) {
     { // acquire device registry reader lock
         RWLock::AutoRLock _rl(mDeviceRegistryLock);
 
@@ -423,7 +436,7 @@
             return;
         }
 
-        device->process(rawEvent);
+        device->process(rawEvents, count);
     } // release device registry reader lock
 }
 
@@ -785,11 +798,25 @@
     }
 }
 
-void InputDevice::process(const RawEvent* rawEvent) {
+void InputDevice::process(const RawEvent* rawEvents, size_t count) {
+    // Process all of the events in order for each mapper.
+    // We cannot simply ask each mapper to process them in bulk because mappers may
+    // have side-effects that must be interleaved.  For example, joystick movement events and
+    // gamepad button presses are handled by different mappers but they should be dispatched
+    // in the order received.
     size_t numMappers = mMappers.size();
-    for (size_t i = 0; i < numMappers; i++) {
-        InputMapper* mapper = mMappers[i];
-        mapper->process(rawEvent);
+    for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
+#if DEBUG_RAW_EVENTS
+        LOGD("Input event: device=%d type=0x%04x scancode=0x%04x "
+                "keycode=0x%04x value=0x%04x flags=0x%08x",
+                rawEvent->deviceId, rawEvent->type, rawEvent->scanCode, rawEvent->keyCode,
+                rawEvent->value, rawEvent->flags);
+#endif
+
+        for (size_t i = 0; i < numMappers; i++) {
+            InputMapper* mapper = mMappers[i];
+            mapper->process(rawEvent);
+        }
     }
 }
 
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index fdb4cfc..cf9b13d 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -216,6 +216,10 @@
     virtual InputDispatcherInterface* getDispatcher() { return mDispatcher.get(); }
     virtual EventHubInterface* getEventHub() { return mEventHub.get(); }
 
+    // The event queue.
+    static const int EVENT_BUFFER_SIZE = 256;
+    RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
+
     // This reader/writer lock guards the list of input devices.
     // The writer lock must be held whenever the list of input devices is modified
     //   and then promptly released.
@@ -228,16 +232,15 @@
     KeyedVector<int32_t, InputDevice*> mDevices;
 
     // low-level input event decoding and device management
-    void process(const RawEvent* rawEvent);
+    void processEvents(const RawEvent* rawEvents, size_t count);
 
     void addDevice(int32_t deviceId);
     void removeDevice(int32_t deviceId);
-    void configureExcludedDevices();
-
-    void consumeEvent(const RawEvent* rawEvent);
+    void processEventsForDevice(int32_t deviceId, const RawEvent* rawEvents, size_t count);
     void timeoutExpired(nsecs_t when);
 
     void handleConfigurationChanged(nsecs_t when);
+    void configureExcludedDevices();
 
     // state management for all devices
     Mutex mStateLock;
@@ -251,12 +254,12 @@
     InputConfiguration mInputConfiguration;
     void updateInputConfiguration();
 
-    nsecs_t mDisableVirtualKeysTimeout;
+    nsecs_t mDisableVirtualKeysTimeout; // only accessed by reader thread
     virtual void disableVirtualKeysUntil(nsecs_t time);
     virtual bool shouldDropVirtualKey(nsecs_t now,
             InputDevice* device, int32_t keyCode, int32_t scanCode);
 
-    nsecs_t mNextTimeout;
+    nsecs_t mNextTimeout; // only accessed by reader thread
     virtual void requestTimeoutAtTime(nsecs_t when);
 
     // state queries
@@ -301,7 +304,7 @@
     void addMapper(InputMapper* mapper);
     void configure();
     void reset();
-    void process(const RawEvent* rawEvent);
+    void process(const RawEvent* rawEvents, size_t count);
     void timeoutExpired(nsecs_t when);
 
     void getDeviceInfo(InputDeviceInfo* outDeviceInfo);
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 120951d..b9174bf 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -610,14 +610,14 @@
         mExcludedDevices.add(String8(deviceName));
     }
 
-    virtual bool getEvent(int timeoutMillis, RawEvent* outEvent) {
+    virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
         if (mEvents.empty()) {
-            return false;
+            return 0;
         }
 
-        *outEvent = *mEvents.begin();
+        *buffer = *mEvents.begin();
         mEvents.erase(mEvents.begin());
-        return true;
+        return 1;
     }
 
     virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const {
@@ -1433,7 +1433,7 @@
 
     // Event handling.
     RawEvent event;
-    mDevice->process(&event);
+    mDevice->process(&event, 1);
 
     ASSERT_NO_FATAL_FAILURE(mapper1->assertProcessWasCalled());
     ASSERT_NO_FATAL_FAILURE(mapper2->assertProcessWasCalled());