diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 78c65e0c780ae13cba93335289472d2c2ea2be79..b3f7e674d4a1285321711cd4672d13f125c17ee6 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -825,7 +825,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         // we know for sure the cursor moved while we were composing and we should reset
         // the state.
         final boolean noComposingSpan = composingSpanStart == -1 && composingSpanEnd == -1;
-        if (!mExpectingUpdateSelection) {
+        if (!mExpectingUpdateSelection
+                && !mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart)) {
             // TAKE CARE: there is a race condition when we enter this test even when the user
             // did not explicitly move the cursor. This happens when typing fast, where two keys
             // turn this flag on in succession and both onUpdateSelection() calls arrive after
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 37e1dbb6903ce136e9a9e6c3d3e4d93e6fefc9b1..ce7049f4f5ebd091038d18b1ffcd05d9c3a1c7c7 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -629,4 +629,34 @@ public class RichInputConnection {
         commitText(" " + textBeforeCursor.subSequence(0, 1), 1);
         return true;
     }
+
+    /**
+     * Heuristic to determine if this is an expected update of the cursor.
+     *
+     * Sometimes updates to the cursor position are late because of their asynchronous nature.
+     * This method tries to determine if this update is one, based on the values of the cursor
+     * position in the update, and the currently expected position of the cursor according to
+     * LatinIME's internal accounting. If this is not a belated expected update, then it should
+     * mean that the user moved the cursor explicitly.
+     * This is quite robust, but of course it's not perfect. In particular, it will fail in the
+     * case we get an update A, the user types in N characters so as to move the cursor to A+N but
+     * we don't get those, and then the user places the cursor between A and A+N, and we get only
+     * this update and not the ones in-between. This is almost impossible to achieve even trying
+     * very very hard.
+     *
+     * @param oldSelStart The value of the old cursor position in the update.
+     * @param newSelStart The value of the new cursor position in the update.
+     * @return whether this is a belated expected update or not.
+     */
+    public boolean isBelatedExpectedUpdate(final int oldSelStart, final int newSelStart) {
+        // If this is an update that arrives at our expected position, it's a belated update.
+        if (newSelStart == mCurrentCursorPosition) return true;
+        // If this is an update that moves the cursor from our expected position, it must be
+        // an explicit move.
+        if (oldSelStart == mCurrentCursorPosition) return false;
+        // The following returns true if newSelStart is between oldSelStart and
+        // mCurrentCursorPosition. We assume that if the updated position is between the old
+        // position and the expected position, then it must be a belated update.
+        return (newSelStart - oldSelStart) * (mCurrentCursorPosition - newSelStart) >= 0;
+    }
 }