Add support for mouse hover and scroll wheel.

Dispatch ACTION_HOVER_MOVE and ACTION_SCROLL through the View
hierarchy as onGenericTouchEvent.  Pointer events dispatched
this way are delivered to the view under the pointer.  Non-pointer
events continue to be delivered to the focused view.

Added scroll wheel support to AbsListView, ScrollView,
HorizontalScrollView and WebView.  Shift+VSCROLL is translated
to HSCROLL as appropriate.

Added logging of new pointer events in PointerLocationView.

Fixed a problem in EventHub when a USB device is removed that
resulted in a long stream of ENODEV errors being logged until INotify
noticed the device was gone.

Note that the new events are not supported by wallpapers at this time
because the wallpaper engine only delivers touch events.

Make all mouse buttons behave identically.  (Effectively we only
support one button.)

Change-Id: I9ab445ffb63c813fcb07db6693987b02475f3756
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index b31381a..25db25e 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -508,6 +508,7 @@
         }
 
         // Grab the next input event.
+        bool deviceWasRemoved = false;
         for (;;) {
             // Consume buffered input events, if any.
             if (mInputBufferIndex < mInputBufferCount) {
@@ -558,6 +559,10 @@
                 int32_t readSize = read(pfd.fd, mInputBufferData,
                         sizeof(struct input_event) * INPUT_BUFFER_SIZE);
                 if (readSize < 0) {
+                    if (errno == ENODEV) {
+                        deviceWasRemoved = true;
+                        break;
+                    }
                     if (errno != EAGAIN && errno != EINTR) {
                         LOGW("could not get event (errno=%d)", errno);
                     }
@@ -570,6 +575,13 @@
             }
         }
 
+        // Handle the case where a device has been removed but INotify has not yet noticed.
+        if (deviceWasRemoved) {
+            AutoMutex _l(mLock);
+            closeDeviceAtIndexLocked(mInputFdIndex);
+            continue; // report added or removed devices immediately
+        }
+
 #if HAVE_INOTIFY
         // readNotify() will modify mFDs and mFDCount, so this must be done after
         // processing all other events.
@@ -580,8 +592,6 @@
         }
 #endif
 
-        mInputFdIndex = 0;
-
         // 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
@@ -602,6 +612,9 @@
                 usleep(100000);
             }
         }
+
+        // Prepare to process all of the FDs we just polled.
+        mInputFdIndex = 0;
     }
 }
 
@@ -1008,37 +1021,42 @@
     for (size_t i = FIRST_ACTUAL_DEVICE_INDEX; i < mDevices.size(); i++) {
         Device* device = mDevices[i];
         if (device->path == devicePath) {
-            LOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x\n",
-                 device->path.string(), device->identifier.name.string(), device->id,
-                 device->fd, device->classes);
-
-            for (int j=0; j<EV_SW; j++) {
-                if (mSwitches[j] == device->id) {
-                    mSwitches[j] = 0;
-                }
-            }
-
-            if (device->id == mBuiltInKeyboardId) {
-                LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
-                        device->path.string(), mBuiltInKeyboardId);
-                mBuiltInKeyboardId = -1;
-                clearKeyboardProperties(device, true);
-            }
-            clearKeyboardProperties(device, false);
-
-            mFds.removeAt(i);
-            mDevices.removeAt(i);
-            device->close();
-
-            device->next = mClosingDevices;
-            mClosingDevices = device;
-            return 0;
+            return closeDeviceAtIndexLocked(i);
         }
     }
-    LOGE("remove device: %s not found\n", devicePath);
+    LOGV("Remove device: %s not found, device may already have been removed.", devicePath);
     return -1;
 }
 
+int EventHub::closeDeviceAtIndexLocked(int index) {
+    Device* device = mDevices[index];
+    LOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x\n",
+         device->path.string(), device->identifier.name.string(), device->id,
+         device->fd, device->classes);
+
+    for (int j=0; j<EV_SW; j++) {
+        if (mSwitches[j] == device->id) {
+            mSwitches[j] = 0;
+        }
+    }
+
+    if (device->id == mBuiltInKeyboardId) {
+        LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
+                device->path.string(), mBuiltInKeyboardId);
+        mBuiltInKeyboardId = -1;
+        clearKeyboardProperties(device, true);
+    }
+    clearKeyboardProperties(device, false);
+
+    mFds.removeAt(index);
+    mDevices.removeAt(index);
+    device->close();
+
+    device->next = mClosingDevices;
+    mClosingDevices = device;
+    return 0;
+}
+
 int EventHub::readNotify(int nfd) {
 #ifdef HAVE_INOTIFY
     int res;
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 23bb344..f7936d2 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -261,6 +261,7 @@
 
     int openDevice(const char *devicePath);
     int closeDevice(const char *devicePath);
+    int closeDeviceAtIndexLocked(int index);
     int scanDir(const char *dirname);
     int readNotify(int nfd);
 
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 2e3f0bd..c064a9c 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -116,6 +116,7 @@
     case AMOTION_EVENT_ACTION_MOVE:
     case AMOTION_EVENT_ACTION_OUTSIDE:
     case AMOTION_EVENT_ACTION_HOVER_MOVE:
+    case AMOTION_EVENT_ACTION_SCROLL:
         return true;
     case AMOTION_EVENT_ACTION_POINTER_DOWN:
     case AMOTION_EVENT_ACTION_POINTER_UP: {
@@ -480,8 +481,7 @@
         // If the application takes too long to catch up then we drop all events preceding
         // the touch into the other window.
         MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
-        if ((motionEntry->action == AMOTION_EVENT_ACTION_DOWN
-                || motionEntry->action == AMOTION_EVENT_ACTION_HOVER_MOVE)
+        if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN
                 && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
                 && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
                 && mInputTargetWaitApplication != NULL) {
@@ -1180,7 +1180,8 @@
             && (mTouchState.deviceId != entry->deviceId
                     || mTouchState.source != entry->source);
     if (maskedAction == AMOTION_EVENT_ACTION_DOWN
-            || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+            || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
+            || maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
         bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
         if (wrongDevice && !down) {
             mTempTouchState.copyFrom(mTouchState);
@@ -1205,8 +1206,9 @@
 
     if (maskedAction == AMOTION_EVENT_ACTION_DOWN
             || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)
-            || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
-        /* Case 1: New splittable pointer going down. */
+            || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
+            || maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
+        /* Case 1: New splittable pointer going down, or need target for hover or scroll. */
 
         int32_t pointerIndex = getMotionEventActionPointerIndex(action);
         int32_t x = int32_t(entry->firstSample.pointerCoords[pointerIndex].
@@ -1380,8 +1382,10 @@
     // If this is the first pointer going down and the touched window has a wallpaper
     // then also add the touched wallpaper windows so they are locked in for the duration
     // of the touch gesture.
-    if (maskedAction == AMOTION_EVENT_ACTION_DOWN
-            || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+    // We do not collect wallpapers during HOVER_MOVE or SCROLL because the wallpaper
+    // engine only supports touch events.  We would need to add a mechanism similar
+    // to View.onGenericMotionEvent to enable wallpapers to handle these events.
+    if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
         const InputWindow* foregroundWindow = mTempTouchState.getFirstForegroundWindow();
         if (foregroundWindow->hasWallpaper) {
             for (size_t i = 0; i < mWindows.size(); i++) {
@@ -1423,7 +1427,7 @@
                     || maskedAction == AMOTION_EVENT_ACTION_CANCEL
                     || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
                 // All pointers up or canceled.
-                mTempTouchState.reset();
+                mTouchState.reset();
             } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
                 // First pointer went down.
                 if (mTouchState.down) {
@@ -1432,6 +1436,7 @@
                     LOGD("Pointer down received while already down.");
 #endif
                 }
+                mTouchState.copyFrom(mTempTouchState);
             } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
                 // One pointer went up.
                 if (isSplit) {
@@ -1450,10 +1455,13 @@
                         i += 1;
                     }
                 }
+                mTouchState.copyFrom(mTempTouchState);
+            } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
+                // Discard temporary touch state since it was only valid for this action.
+            } else {
+                // Save changes to touch state as-is for all other actions.
+                mTouchState.copyFrom(mTempTouchState);
             }
-
-            // Save changes to touch state.
-            mTouchState.copyFrom(mTempTouchState);
         }
     } else {
 #if DEBUG_FOCUS
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index a963c72..a865d9f 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -1197,7 +1197,14 @@
     switch (rawEvent->type) {
     case EV_KEY:
         switch (rawEvent->scanCode) {
-        case BTN_MOUSE:
+        case BTN_LEFT:
+        case BTN_RIGHT:
+        case BTN_MIDDLE:
+        case BTN_SIDE:
+        case BTN_EXTRA:
+        case BTN_FORWARD:
+        case BTN_BACK:
+        case BTN_TASK:
             mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE;
             mAccumulator.btnMouse = rawEvent->value != 0;
             // Sync now since BTN_MOUSE is not necessarily followed by SYN_REPORT and
@@ -1247,6 +1254,7 @@
     int motionEventAction;
     PointerCoords pointerCoords;
     nsecs_t downTime;
+    float vscroll, hscroll;
     { // acquire lock
         AutoMutex _l(mLock);
 
@@ -1331,10 +1339,14 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, mLocked.down ? 1.0f : 0.0f);
 
         if (mHaveVWheel && (fields & Accumulator::FIELD_REL_WHEEL)) {
-            pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, mAccumulator.relWheel);
+            vscroll = mAccumulator.relWheel;
+        } else {
+            vscroll = 0;
         }
         if (mHaveHWheel && (fields & Accumulator::FIELD_REL_HWHEEL)) {
-            pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, mAccumulator.relHWheel);
+            hscroll = mAccumulator.relHWheel;
+        } else {
+            hscroll = 0;
         }
     } // release lock
 
@@ -1345,6 +1357,15 @@
             1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime);
 
     mAccumulator.clear();
+
+    if (vscroll != 0 || hscroll != 0) {
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
+
+        getDispatcher()->notifyMotion(when, getDeviceId(), mSources, 0,
+                AMOTION_EVENT_ACTION_SCROLL, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime);
+    }
 }
 
 int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {