Synthesize events for pressure and button changes.

Even when there isn't movement on the touchscreen we should produce
events for pressure and button state changes generated by external
stylii.

Change-Id: I9fd7ba85902d5d6bfb28d5e5ff5d8f340a94c2bf
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 8709692..c6389a9 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -73,6 +73,13 @@
 // external stylus.
 static const nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72);
 
+// Maximum amount of time to wait on touch data before pushing out new pressure data.
+static const nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20);
+
+// Artificial latency on synthetic events created from stylus data without corresponding touch
+// data.
+static const nsecs_t STYLUS_DATA_LATENCY = ms2ns(10);
+
 // --- Static Functions ---
 
 template<typename T>
@@ -2827,7 +2834,7 @@
             toString(mExternalStylusConnected));
     dump.appendFormat(INDENT4 "External Stylus ID: %" PRId64 "\n", mExternalStylusId);
     dump.appendFormat(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n",
-            mExternalStylusDataTimeout);
+            mExternalStylusFusionTimeout);
     dump.append(INDENT3 "External Stylus State:\n");
     dumpStylusState(dump, mExternalStylusState);
 
@@ -3836,10 +3843,15 @@
 void TouchInputMapper::resetExternalStylus() {
     mExternalStylusState.clear();
     mExternalStylusId = -1;
-    mExternalStylusDataTimeout = LLONG_MAX;
+    mExternalStylusFusionTimeout = LLONG_MAX;
     mExternalStylusDataPending = false;
 }
 
+void TouchInputMapper::clearStylusDataPendingFlags() {
+    mExternalStylusDataPending = false;
+    mExternalStylusFusionTimeout = LLONG_MAX;
+}
+
 void TouchInputMapper::process(const RawEvent* rawEvent) {
     mCursorButtonAccumulator.process(rawEvent);
     mCursorScrollAccumulator.process(rawEvent);
@@ -3915,19 +3927,30 @@
         }
 
         // All ready to go.
-        mExternalStylusDataPending = false;
+        clearStylusDataPendingFlags();
         mCurrentRawState.copyFrom(next);
+        if (mCurrentRawState.when < mLastRawState.when) {
+            mCurrentRawState.when = mLastRawState.when;
+        }
         cookAndDispatch(mCurrentRawState.when);
     }
     if (count != 0) {
         mRawStatesPending.removeItemsAt(0, count);
     }
 
-    // TODO: If we still have pending stylus data that hasn't been mapped to a touch yet
-    // then set a timeout to synthesize a new touch event with the new data.  Otherwise
-    // we will lose button and pressure information while the touch is stationary.
     if (mExternalStylusDataPending) {
-        ALOGD("STYLUS TODO: Wiggle wiggle wiggle.");
+        if (timeout) {
+            nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY;
+            clearStylusDataPendingFlags();
+            mCurrentRawState.copyFrom(mLastRawState);
+#if DEBUG_STYLUS_FUSION
+            ALOGD("Timeout expired, synthesizing event with new stylus data");
+#endif
+            cookAndDispatch(when);
+        } else if (mExternalStylusFusionTimeout == LLONG_MAX) {
+            mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT;
+            getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
+        }
     }
 }
 
@@ -4086,15 +4109,14 @@
 #endif
             resetExternalStylus();
         } else {
-            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-            if (mExternalStylusDataTimeout == LLONG_MAX) {
-                mExternalStylusDataTimeout = now + EXTERNAL_STYLUS_DATA_TIMEOUT;
+            if (mExternalStylusFusionTimeout == LLONG_MAX) {
+                mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT;
             }
 #if DEBUG_STYLUS_FUSION
             ALOGD("No stylus data but stylus is connected, requesting timeout "
-                    "(%" PRId64 "ms)", mExternalStylusDataTimeout);
+                    "(%" PRId64 "ms)", mExternalStylusFusionTimeout);
 #endif
-            getContext()->requestTimeoutAtTime(mExternalStylusDataTimeout);
+            getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
             return true;
         }
     }
@@ -4117,22 +4139,20 @@
             dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/);
         }
     } else if (mDeviceMode == DEVICE_MODE_DIRECT) {
-        if (mExternalStylusDataTimeout < when) {
-            mExternalStylusDataTimeout = LLONG_MAX;
+        if (mExternalStylusFusionTimeout < when) {
             processRawTouches(true /*timeout*/);
-        } else if (mExternalStylusDataTimeout != LLONG_MAX) {
-            getContext()->requestTimeoutAtTime(mExternalStylusDataTimeout);
+        } else if (mExternalStylusFusionTimeout != LLONG_MAX) {
+            getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
         }
     }
 }
 
 void TouchInputMapper::updateExternalStylusState(const StylusState& state) {
     mExternalStylusState.copyFrom(state);
-    if (mExternalStylusId != -1 || mExternalStylusDataTimeout != LLONG_MAX) {
+    if (mExternalStylusId != -1 || mExternalStylusFusionTimeout != LLONG_MAX) {
         // We're either in the middle of a fused stream of data or we're waiting on data before
         // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus
         // data.
-        mExternalStylusDataTimeout = LLONG_MAX;
         mExternalStylusDataPending = true;
         processRawTouches(false /*timeout*/);
     }
@@ -4310,7 +4330,7 @@
         // Dispatch move events if any of the remaining pointers moved from their old locations.
         // Although applications receive new locations as part of individual pointer up
         // events, they do not generally handle them except when presented in a move event.
-        if (moveNeeded) {
+        if (moveNeeded && !moveIdBits.isEmpty()) {
             ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
             dispatchMotion(when, policyFlags, mSource,
                     AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState, 0,
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index 451a20f..aadc37f 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -1456,7 +1456,7 @@
     // State provided by an external stylus
     StylusState mExternalStylusState;
     int64_t mExternalStylusId;
-    nsecs_t mExternalStylusDataTimeout;
+    nsecs_t mExternalStylusFusionTimeout;
     bool mExternalStylusDataPending;
 
     // True if we sent a HOVER_ENTER event.
@@ -1781,6 +1781,7 @@
     VelocityControl mWheelYVelocityControl;
 
     void resetExternalStylus();
+    void clearStylusDataPendingFlags();
 
     void sync(nsecs_t when);