diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index eee631e865b46d8f2de6a3364b36a081dddb4788..03c11c0c874432545488df8281c1046ddbb9f5cc 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -73,6 +73,7 @@ import com.android.inputmethod.keyboard.KeyboardActionListener;
 import com.android.inputmethod.keyboard.KeyboardId;
 import com.android.inputmethod.keyboard.KeyboardSwitcher;
 import com.android.inputmethod.keyboard.MainKeyboardView;
+import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.define.ProductionFlag;
 import com.android.inputmethod.latin.personalization.PersonalizationDictionary;
@@ -1759,9 +1760,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
                     // Batch input has ended or canceled while the message was being delivered.
                     return;
                 }
-                final SuggestedWords suggestedWords = getSuggestedWordsGestureLocked(batchPointers);
-                mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip(
-                        suggestedWords, false /* dismissGestureFloatingPreviewText */);
+
+                getSuggestedWordsGestureLocked(batchPointers, new OnGetSuggestedWordsCallback() {
+                    @Override
+                    public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
+                        mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip(
+                                suggestedWords, false /* dismissGestureFloatingPreviewText */);
+                    }});
             }
         }
 
@@ -1784,29 +1789,39 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         }
 
         // Run in the UI thread.
-        public SuggestedWords onEndBatchInput(final InputPointers batchPointers) {
-            synchronized (mLock) {
-                mInBatchInput = false;
-                final SuggestedWords suggestedWords = getSuggestedWordsGestureLocked(batchPointers);
-                mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip(
-                        suggestedWords, true /* dismissGestureFloatingPreviewText */);
-                return suggestedWords;
+        public void onEndBatchInput(final InputPointers batchPointers) {
+            synchronized(mLock) {
+                getSuggestedWordsGestureLocked(batchPointers, new OnGetSuggestedWordsCallback() {
+                    @Override
+                    public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
+                        mInBatchInput = false;
+                        mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip(suggestedWords,
+                                true /* dismissGestureFloatingPreviewText */);
+                        mLatinIme.onEndBatchInputAsyncInternal(suggestedWords);
+                    }
+                });
             }
         }
 
         // {@link LatinIME#getSuggestedWords(int)} method calls with same session id have to
         // be synchronized.
-        private SuggestedWords getSuggestedWordsGestureLocked(final InputPointers batchPointers) {
+        private void getSuggestedWordsGestureLocked(final InputPointers batchPointers,
+                final OnGetSuggestedWordsCallback callback) {
             mLatinIme.mWordComposer.setBatchInputPointers(batchPointers);
-            final SuggestedWords suggestedWords =
-                    mLatinIme.getSuggestedWordsOrOlderSuggestions(Suggest.SESSION_GESTURE);
-            final int suggestionCount = suggestedWords.size();
-            if (suggestionCount <= 1) {
-                final String mostProbableSuggestion = (suggestionCount == 0) ? null
-                        : suggestedWords.getWord(0);
-                return mLatinIme.getOlderSuggestions(mostProbableSuggestion);
-            }
-            return suggestedWords;
+            mLatinIme.getSuggestedWordsOrOlderSuggestions(Suggest.SESSION_GESTURE,
+                    new OnGetSuggestedWordsCallback() {
+                @Override
+                public void onGetSuggestedWords(SuggestedWords suggestedWords) {
+                    final int suggestionCount = suggestedWords.size();
+                    if (suggestionCount <= 1) {
+                        final String mostProbableSuggestion = (suggestionCount == 0) ? null
+                                : suggestedWords.getWord(0);
+                        callback.onGetSuggestedWords(
+                                mLatinIme.getOlderSuggestions(mostProbableSuggestion));
+                    }
+                    callback.onGetSuggestedWords(suggestedWords);
+                }
+            });
         }
     }
 
@@ -1831,10 +1846,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         BatchInputUpdater.getInstance().onUpdateBatchInput(batchPointers);
     }
 
-    @Override
-    public void onEndBatchInput(final InputPointers batchPointers) {
-        final SuggestedWords suggestedWords = BatchInputUpdater.getInstance().onEndBatchInput(
-                batchPointers);
+    public void onEndBatchInputAsyncInternal(final SuggestedWords suggestedWords) {
         final String batchInputText = suggestedWords.isEmpty()
                 ? null : suggestedWords.getWord(0);
         if (TextUtils.isEmpty(batchInputText)) {
@@ -1856,6 +1868,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         mKeyboardSwitcher.updateShiftState();
     }
 
+    @Override
+    public void onEndBatchInput(final InputPointers batchPointers) {
+        BatchInputUpdater.getInstance().onEndBatchInput(batchPointers);
+    }
+
     private String specificTldProcessingOnTextInput(final String text) {
         if (text.length() <= 1 || text.charAt(0) != Constants.CODE_PERIOD
                 || !Character.isLetter(text.charAt(1))) {
@@ -2326,17 +2343,23 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
             return;
         }
 
-        final SuggestedWords suggestedWords =
-                getSuggestedWordsOrOlderSuggestions(Suggest.SESSION_TYPING);
         final String typedWord = mWordComposer.getTypedWord();
-        showSuggestionStrip(suggestedWords, typedWord);
+        getSuggestedWordsOrOlderSuggestions(Suggest.SESSION_TYPING,
+                new OnGetSuggestedWordsCallback() {
+            @Override
+            public void onGetSuggestedWords(SuggestedWords suggestedWords) {
+                showSuggestionStrip(suggestedWords, typedWord);
+            }
+        });
     }
 
-    private SuggestedWords getSuggestedWords(final int sessionId) {
+    private void getSuggestedWords(final int sessionId,
+            final OnGetSuggestedWordsCallback callback) {
         final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
         final Suggest suggest = mSuggest;
         if (keyboard == null || suggest == null) {
-            return SuggestedWords.EMPTY;
+            callback.onGetSuggestedWords(SuggestedWords.EMPTY);
+            return;
         }
         // Get the word on which we should search the bigrams. If we are composing a word, it's
         // whatever is *before* the half-committed word in the buffer, hence 2; if we aren't, we
@@ -2353,14 +2376,20 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
             prevWord = LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? null
                     : mLastComposedWord.mCommittedWord;
         }
-        return suggest.getSuggestedWords(mWordComposer, prevWord, keyboard.getProximityInfo(),
-                currentSettings.mBlockPotentiallyOffensive,
-                currentSettings.mCorrectionEnabled, additionalFeaturesOptions, sessionId);
+        suggest.getSuggestedWords(mWordComposer, prevWord, keyboard.getProximityInfo(),
+                currentSettings.mBlockPotentiallyOffensive, currentSettings.mCorrectionEnabled,
+                additionalFeaturesOptions, sessionId, callback);
     }
 
-    private SuggestedWords getSuggestedWordsOrOlderSuggestions(final int sessionId) {
-        return maybeRetrieveOlderSuggestions(mWordComposer.getTypedWord(),
-                getSuggestedWords(sessionId));
+    private void getSuggestedWordsOrOlderSuggestions(final int sessionId,
+            final OnGetSuggestedWordsCallback callback) {
+        getSuggestedWords(sessionId, new OnGetSuggestedWordsCallback() {
+            @Override
+            public void onGetSuggestedWords(SuggestedWords suggestedWords) {
+                callback.onGetSuggestedWords(maybeRetrieveOlderSuggestions(
+                        mWordComposer.getTypedWord(), suggestedWords));
+            }
+        });
     }
 
     private SuggestedWords maybeRetrieveOlderSuggestions(final String typedWord,
@@ -2658,39 +2687,49 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         mConnection.setComposingRegion(
                 mLastSelectionStart - numberOfCharsInWordBeforeCursor,
                 mLastSelectionEnd + range.getNumberOfCharsInWordAfterCursor());
-        final SuggestedWords suggestedWords;
         if (suggestions.isEmpty()) {
             // We come here if there weren't any suggestion spans on this word. We will try to
             // compute suggestions for it instead.
-            final SuggestedWords suggestedWordsIncludingTypedWord =
-                    getSuggestedWords(Suggest.SESSION_TYPING);
-            if (suggestedWordsIncludingTypedWord.size() > 1) {
-                // We were able to compute new suggestions for this word.
-                // Remove the typed word, since we don't want to display it in this case.
-                // The #getSuggestedWordsExcludingTypedWord() method sets willAutoCorrect to false.
-                suggestedWords =
-                        suggestedWordsIncludingTypedWord.getSuggestedWordsExcludingTypedWord();
-            } else {
-                // No saved suggestions, and we were unable to compute any good one either.
-                // Rather than displaying an empty suggestion strip, we'll display the original
-                // word alone in the middle.
-                // Since there is only one word, willAutoCorrect is false.
-                suggestedWords = suggestedWordsIncludingTypedWord;
-            }
+            getSuggestedWords(Suggest.SESSION_TYPING, new OnGetSuggestedWordsCallback() {
+                @Override
+                public void onGetSuggestedWords(SuggestedWords suggestedWordsIncludingTypedWord) {
+                    final SuggestedWords suggestedWords;
+                    if (suggestedWordsIncludingTypedWord.size() > 1) {
+                        // We were able to compute new suggestions for this word.
+                        // Remove the typed word, since we don't want to display it in this case.
+                        // The #getSuggestedWordsExcludingTypedWord() method sets willAutoCorrect to
+                        // false.
+                        suggestedWords = suggestedWordsIncludingTypedWord
+                                .getSuggestedWordsExcludingTypedWord();
+                    } else {
+                        // No saved suggestions, and we were unable to compute any good one either.
+                        // Rather than displaying an empty suggestion strip, we'll display the
+                        // original word alone in the middle.
+                        // Since there is only one word, willAutoCorrect is false.
+                        suggestedWords = suggestedWordsIncludingTypedWord;
+                    }
+                    unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(suggestedWords,
+                            typedWord);
+                }});
         } else {
             // We found suggestion spans in the word. We'll create the SuggestedWords out of
             // them, and make willAutoCorrect false.
-            suggestedWords = new SuggestedWords(suggestions,
+            final SuggestedWords suggestedWords = new SuggestedWords(suggestions,
                     true /* typedWordValid */, false /* willAutoCorrect */,
                     false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */,
                     false /* isPrediction */);
+            unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(suggestedWords, typedWord);
         }
+    }
 
+    public void unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(
+            final SuggestedWords suggestedWords, final String typedWord) {
         // Note that it's very important here that suggestedWords.mWillAutoCorrect is false.
-        // We never want to auto-correct on a resumed suggestion. Please refer to the three
-        // places above where suggestedWords is affected. We also need to reset
-        // mIsAutoCorrectionIndicatorOn to avoid showSuggestionStrip touching the text to adapt it.
-        // TODO: remove mIsAutoCorrectionIndicator on (see comment on definition)
+        // We never want to auto-correct on a resumed suggestion. Please refer to the three places
+        // above in restartSuggestionsOnWordTouchedByCursor() where suggestedWords is affected.
+        // We also need to unset mIsAutoCorrectionIndicatorOn to avoid showSuggestionStrip touching
+        // the text to adapt it.
+        // TODO: remove mIsAutoCorrectionIndicatorOn (see comment on definition)
         mIsAutoCorrectionIndicatorOn = false;
         showSuggestionStrip(suggestedWords, typedWord);
     }
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 826387a05f671afa7fba8ff8301ad24a69fa312e..18ba15872ad4f28eadb635df6250436aef1d5c63 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -211,26 +211,31 @@ public final class Suggest {
         mAutoCorrectionThreshold = threshold;
     }
 
-    public SuggestedWords getSuggestedWords(final WordComposer wordComposer,
+    public interface OnGetSuggestedWordsCallback {
+        public void onGetSuggestedWords(final SuggestedWords suggestedWords);
+    }
+
+    public void getSuggestedWords(final WordComposer wordComposer,
             final String prevWordForBigram, final ProximityInfo proximityInfo,
             final boolean blockOffensiveWords, final boolean isCorrectionEnabled,
-            final int[] additionalFeaturesOptions, final int sessionId) {
+            final int[] additionalFeaturesOptions, final int sessionId,
+            final OnGetSuggestedWordsCallback callback) {
         LatinImeLogger.onStartSuggestion(prevWordForBigram);
         if (wordComposer.isBatchMode()) {
-            return getSuggestedWordsForBatchInput(
-                    wordComposer, prevWordForBigram, proximityInfo, blockOffensiveWords,
-                    additionalFeaturesOptions, sessionId);
+            getSuggestedWordsForBatchInput(wordComposer, prevWordForBigram, proximityInfo,
+                    blockOffensiveWords, additionalFeaturesOptions, sessionId, callback);
         } else {
-            return getSuggestedWordsForTypingInput(wordComposer, prevWordForBigram, proximityInfo,
-                    blockOffensiveWords, isCorrectionEnabled, additionalFeaturesOptions);
+            getSuggestedWordsForTypingInput(wordComposer, prevWordForBigram, proximityInfo,
+                    blockOffensiveWords, isCorrectionEnabled, additionalFeaturesOptions, callback);
         }
     }
 
-    // Retrieves suggestions for the typing input.
-    private SuggestedWords getSuggestedWordsForTypingInput(final WordComposer wordComposer,
+    // Retrieves suggestions for the typing input
+    // and calls the callback function with the suggestions.
+    private void getSuggestedWordsForTypingInput(final WordComposer wordComposer,
             final String prevWordForBigram, final ProximityInfo proximityInfo,
             final boolean blockOffensiveWords, final boolean isCorrectionEnabled,
-            final int[] additionalFeaturesOptions) {
+            final int[] additionalFeaturesOptions, final OnGetSuggestedWordsCallback callback) {
         final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount();
         final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
                 MAX_SUGGESTIONS);
@@ -253,8 +258,8 @@ public final class Suggest {
 
         for (final String key : mDictionaries.keySet()) {
             final Dictionary dictionary = mDictionaries.get(key);
-            suggestionsSet.addAll(dictionary.getSuggestions(
-                    wordComposerForLookup, prevWordForBigram, proximityInfo, blockOffensiveWords,
+            suggestionsSet.addAll(dictionary.getSuggestions(wordComposerForLookup,
+                    prevWordForBigram, proximityInfo, blockOffensiveWords,
                     additionalFeaturesOptions));
         }
 
@@ -332,7 +337,7 @@ public final class Suggest {
             suggestionsList = suggestionsContainer;
         }
 
-        return new SuggestedWords(suggestionsList,
+        callback.onGetSuggestedWords(new SuggestedWords(suggestionsList,
                 // TODO: this first argument is lying. If this is a whitelisted word which is an
                 // actual word, it says typedWordValid = false, which looks wrong. We should either
                 // rename the attribute or change the value.
@@ -340,14 +345,15 @@ public final class Suggest {
                 hasAutoCorrection, /* willAutoCorrect */
                 false /* isPunctuationSuggestions */,
                 false /* isObsoleteSuggestions */,
-                !wordComposer.isComposingWord() /* isPrediction */);
+                !wordComposer.isComposingWord() /* isPrediction */));
     }
 
-    // Retrieves suggestions for the batch input.
-    private SuggestedWords getSuggestedWordsForBatchInput(final WordComposer wordComposer,
+    // Retrieves suggestions for the batch input
+    // and calls the callback function with the suggestions.
+    private void getSuggestedWordsForBatchInput(final WordComposer wordComposer,
             final String prevWordForBigram, final ProximityInfo proximityInfo,
             final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
-            final int sessionId) {
+            final int sessionId, final OnGetSuggestedWordsCallback callback) {
         final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
                 MAX_SUGGESTIONS);
 
@@ -401,12 +407,12 @@ public final class Suggest {
 
         // In the batch input mode, the most relevant suggested word should act as a "typed word"
         // (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false).
-        return new SuggestedWords(suggestionsContainer,
+        callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer,
                 true /* typedWordValid */,
                 false /* willAutoCorrect */,
                 false /* isPunctuationSuggestions */,
                 false /* isObsoleteSuggestions */,
-                false /* isPrediction */);
+                false /* isPrediction */));
     }
 
     private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo(