diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 130fad851aa73f0cfd177262be4b1f473a4f68bf..625575d538a2a47ee20745856197673787825589 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -365,6 +365,11 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
                     mGestureRecognitionUpdateTime);
         }
 
+        @Override
+        public void cancelUpdateBatchInputTimer(final PointerTracker tracker) {
+            removeMessages(MSG_UPDATE_BATCH_INPUT, tracker);
+        }
+
         @Override
         public void cancelAllUpdateBatchInputTimers() {
             removeMessages(MSG_UPDATE_BATCH_INPUT);
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 1ee87bebbf3e74905c8a63675d2ba21caaedfccc..62e674ad55728c8dc1716369a95e9b0038e5a178 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -98,6 +98,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
         public boolean isInDoubleTapTimeout();
         public void cancelKeyTimers();
         public void startUpdateBatchInputTimer(PointerTracker tracker);
+        public void cancelUpdateBatchInputTimer(PointerTracker tracker);
         public void cancelAllUpdateBatchInputTimers();
 
         public static class Adapter implements TimerProxy {
@@ -124,6 +125,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
             @Override
             public void startUpdateBatchInputTimer(PointerTracker tracker) {}
             @Override
+            public void cancelUpdateBatchInputTimer(PointerTracker tracker) {}
+            @Override
             public void cancelAllUpdateBatchInputTimers() {}
         }
     }
@@ -940,6 +943,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
         if (DEBUG_MOVE_EVENT) {
             printTouchEvent("onMoveEvent:", x, y, eventTime);
         }
+        mTimerProxy.cancelUpdateBatchInputTimer(this);
         if (mIsTrackingCanceled) {
             return;
         }
@@ -1124,6 +1128,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
             printTouchEvent("onUpEvent  :", x, y, eventTime);
         }
 
+        mTimerProxy.cancelUpdateBatchInputTimer(this);
         if (!sInGesture) {
             if (mCurrentKey != null && mCurrentKey.isModifier()) {
                 // Before processing an up event of modifier key, all pointers already being
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java
index a43e94a753dcc301c05b6750a03130696c978956..adf223602eb385793d51d2a3aecd5fa0486c8168 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java
@@ -233,6 +233,10 @@ public class GestureStroke {
         if (lastIndex >= 0) {
             final int x = mXCoordinates.get(lastIndex);
             final int y = mYCoordinates.get(lastIndex);
+            if (DEBUG) {
+                Log.d(TAG, String.format("[%d] duplicateLastPointWith: %d,%d|%d", mPointerId,
+                        x, y, time));
+            }
             // TODO: Have appendMajorPoint()
             appendPoint(x, y, time);
             updateIncrementalRecognitionSize(x, y, time);
@@ -251,6 +255,16 @@ public class GestureStroke {
     }
 
     private void appendPoint(final int x, final int y, final int time) {
+        final int lastIndex = mEventTimes.getLength() - 1;
+        // The point that is created by {@link duplicateLastPointWith(int)} may have later event
+        // time than the next {@link MotionEvent}. To maintain the monotonicity of the event time,
+        // drop the successive point here.
+        if (lastIndex >= 0 && mEventTimes.get(lastIndex) > time) {
+            Log.w(TAG, String.format("[%d] drop stale event: %d,%d|%d last: %d,%d|%d", mPointerId,
+                    x, y, time, mXCoordinates.get(lastIndex), mYCoordinates.get(lastIndex),
+                    mEventTimes.get(lastIndex)));
+            return;
+        }
         mEventTimes.add(time);
         mXCoordinates.add(x);
         mYCoordinates.add(y);