Merge "Refactor KeyboardAccessibilityDelegate class a bit"
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
index d50dd3e..27896fd 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
@@ -114,7 +114,7 @@
      * @param event The event to check.
      * @return {@true} is the event is a touch exploration event
      */
-    public boolean isTouchExplorationEvent(final MotionEvent event) {
+    public static boolean isTouchExplorationEvent(final MotionEvent event) {
         final int action = event.getAction();
         return action == MotionEvent.ACTION_HOVER_ENTER
                 || action == MotionEvent.ACTION_HOVER_EXIT
diff --git a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java
index d6ae698..a6997e2 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java
@@ -134,7 +134,7 @@
         event.setClassName(key.getClass().getName());
         event.setContentDescription(keyDescription);
         event.setEnabled(true);
-        final AccessibilityRecordCompat record = new AccessibilityRecordCompat(event);
+        final AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event);
         record.setSource(mKeyboardView, virtualViewId);
         return event;
     }
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 97f2629..fd34b98 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -183,9 +183,9 @@
     private static native void getHeaderInfoNative(long dict, int[] outHeaderSize,
             int[] outFormatVersion, ArrayList<int[]> outAttributeKeys,
             ArrayList<int[]> outAttributeValues);
-    private static native void flushNative(long dict, String filePath);
+    private static native boolean flushNative(long dict, String filePath);
     private static native boolean needsToRunGCNative(long dict, boolean mindsBlockByGC);
-    private static native void flushWithGCNative(long dict, String filePath);
+    private static native boolean flushWithGCNative(long dict, String filePath);
     private static native void closeNative(long dict);
     private static native int getFormatVersionNative(long dict);
     private static native int getProbabilityNative(long dict, int[] word);
@@ -203,12 +203,12 @@
             int[] outputSuggestionCount, int[] outputCodePoints, int[] outputScores,
             int[] outputIndices, int[] outputTypes, int[] outputAutoCommitFirstWordConfidence,
             float[] inOutLanguageWeight);
-    private static native void addUnigramWordNative(long dict, int[] word, int probability,
+    private static native boolean addUnigramWordNative(long dict, int[] word, int probability,
             int[] shortcutTarget, int shortcutProbability, boolean isBeginningOfSentence,
             boolean isNotAWord, boolean isBlacklisted, int timestamp);
-    private static native void addBigramWordsNative(long dict, int[] word0,
+    private static native boolean addBigramWordsNative(long dict, int[] word0,
             boolean isBeginningOfSentence, int[] word1, int probability, int timestamp);
-    private static native void removeBigramWordsNative(long dict, int[] word0,
+    private static native boolean removeBigramWordsNative(long dict, int[] word0,
             boolean isBeginningOfSentence, int[] word1);
     private static native int addMultipleDictionaryEntriesNative(long dict,
             LanguageModelParam[] languageModelParams, int startIndex);
@@ -417,45 +417,53 @@
     }
 
     // Add a unigram entry to binary dictionary with unigram attributes in native code.
-    public void addUnigramEntry(final String word, final int probability,
+    public boolean addUnigramEntry(final String word, final int probability,
             final String shortcutTarget, final int shortcutProbability,
             final boolean isBeginningOfSentence, final boolean isNotAWord,
             final boolean isBlacklisted, final int timestamp) {
         if (word == null || (word.isEmpty() && !isBeginningOfSentence)) {
-            return;
+            return false;
         }
         final int[] codePoints = StringUtils.toCodePointArray(word);
         final int[] shortcutTargetCodePoints = (shortcutTarget != null) ?
                 StringUtils.toCodePointArray(shortcutTarget) : null;
-        addUnigramWordNative(mNativeDict, codePoints, probability, shortcutTargetCodePoints,
-                shortcutProbability, isBeginningOfSentence, isNotAWord, isBlacklisted, timestamp);
+        if (!addUnigramWordNative(mNativeDict, codePoints, probability, shortcutTargetCodePoints,
+                shortcutProbability, isBeginningOfSentence, isNotAWord, isBlacklisted, timestamp)) {
+            return false;
+        }
         mHasUpdated = true;
+        return true;
     }
 
     // Add an n-gram entry to the binary dictionary with timestamp in native code.
-    public void addNgramEntry(final PrevWordsInfo prevWordsInfo, final String word,
-            final int probability,
-            final int timestamp) {
+    public boolean addNgramEntry(final PrevWordsInfo prevWordsInfo, final String word,
+            final int probability, final int timestamp) {
         if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) {
-            return;
+            return false;
         }
         final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord);
         final int[] codePoints1 = StringUtils.toCodePointArray(word);
-        addBigramWordsNative(mNativeDict, codePoints0, prevWordsInfo.mIsBeginningOfSentence,
-                codePoints1, probability, timestamp);
+        if (!addBigramWordsNative(mNativeDict, codePoints0, prevWordsInfo.mIsBeginningOfSentence,
+                codePoints1, probability, timestamp)) {
+            return false;
+        }
         mHasUpdated = true;
+        return true;
     }
 
     // Remove an n-gram entry from the binary dictionary in native code.
-    public void removeNgramEntry(final PrevWordsInfo prevWordsInfo, final String word) {
+    public boolean removeNgramEntry(final PrevWordsInfo prevWordsInfo, final String word) {
         if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) {
-            return;
+            return false;
         }
         final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord);
         final int[] codePoints1 = StringUtils.toCodePointArray(word);
-        removeBigramWordsNative(mNativeDict, codePoints0, prevWordsInfo.mIsBeginningOfSentence,
-                codePoints1);
+        if (!removeBigramWordsNative(mNativeDict, codePoints0, prevWordsInfo.mIsBeginningOfSentence,
+                codePoints1)) {
+            return false;
+        }
         mHasUpdated = true;
+        return true;
     }
 
     public void addMultipleDictionaryEntries(final LanguageModelParam[] languageModelParams) {
@@ -485,26 +493,33 @@
     }
 
     // Flush to dict file if the dictionary has been updated.
-    public void flush() {
-        if (!isValidDictionary()) return;
+    public boolean flush() {
+        if (!isValidDictionary()) return false;
         if (mHasUpdated) {
-            flushNative(mNativeDict, mDictFilePath);
+            if (!flushNative(mNativeDict, mDictFilePath)) {
+                return false;
+            }
             reopen();
         }
+        return true;
     }
 
     // Run GC and flush to dict file if the dictionary has been updated.
-    public void flushWithGCIfHasUpdated() {
+    public boolean flushWithGCIfHasUpdated() {
         if (mHasUpdated) {
-            flushWithGC();
+            return flushWithGC();
         }
+        return true;
     }
 
     // Run GC and flush to dict file.
-    public void flushWithGC() {
-        if (!isValidDictionary()) return;
-        flushWithGCNative(mNativeDict, mDictFilePath);
+    public boolean flushWithGC() {
+        if (!isValidDictionary()) return false;
+        if (!flushWithGCNative(mNativeDict, mDictFilePath)) {
+            return false;
+        }
         reopen();
+        return true;
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
index 4cb920f..7fa3d04 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
@@ -408,7 +408,7 @@
         if (userHistoryDictionary == null) {
             return;
         }
-        final int maxFreq = getMaxFrequency(word);
+        final int maxFreq = getFrequency(word);
         if (maxFreq == 0 && blockPotentiallyOffensive) {
             return;
         }
@@ -516,7 +516,7 @@
         return false;
     }
 
-    private int getMaxFrequency(final String word) {
+    public int getFrequency(final String word) {
         if (TextUtils.isEmpty(word)) {
             return Dictionary.NOT_A_PROBABILITY;
         }
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 95ff8c6..b10bae0 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -298,8 +298,10 @@
     protected void addUnigramLocked(final String word, final int frequency,
             final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord,
             final boolean isBlacklisted, final int timestamp) {
-        mBinaryDictionary.addUnigramEntry(word, frequency, shortcutTarget, shortcutFreq,
-                false /* isBeginningOfSentence */, isNotAWord, isBlacklisted, timestamp);
+        if (!mBinaryDictionary.addUnigramEntry(word, frequency, shortcutTarget, shortcutFreq,
+                false /* isBeginningOfSentence */, isNotAWord, isBlacklisted, timestamp)) {
+            Log.e(TAG, "Cannot add unigram entry. word: " + word);
+        }
     }
 
     /**
@@ -322,7 +324,11 @@
 
     protected void addNgramEntryLocked(final PrevWordsInfo prevWordsInfo, final String word,
             final int frequency, final int timestamp) {
-        mBinaryDictionary.addNgramEntry(prevWordsInfo, word, frequency, timestamp);
+        if (!mBinaryDictionary.addNgramEntry(prevWordsInfo, word, frequency, timestamp)) {
+            Log.e(TAG, "Cannot add n-gram entry.");
+            Log.e(TAG, "  PrevWordsInfo: " + prevWordsInfo);
+            Log.e(TAG, "  word: " + word);
+        }
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/latin/PrevWordsInfo.java b/java/src/com/android/inputmethod/latin/PrevWordsInfo.java
index e44239f..42b311c 100644
--- a/java/src/com/android/inputmethod/latin/PrevWordsInfo.java
+++ b/java/src/com/android/inputmethod/latin/PrevWordsInfo.java
@@ -53,4 +53,10 @@
     public boolean isValid() {
         return mPrevWord != null;
     }
+
+    @Override
+    public String toString() {
+        return "PrevWord: " + mPrevWord + ", isBeginningOfSentence: "
+                    + mIsBeginningOfSentence + ".";
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java
index 1c93a91..ac0ab28 100644
--- a/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java
+++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java
@@ -33,6 +33,7 @@
 import com.android.inputmethod.keyboard.KeyboardId;
 import com.android.inputmethod.keyboard.KeyboardLayoutSet;
 import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.Dictionary;
 import com.android.inputmethod.latin.DictionaryFacilitator;
 import com.android.inputmethod.latin.PrevWordsInfo;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
@@ -98,10 +99,12 @@
         }
     }
 
-    private static boolean isDistracter(
+    private boolean isDistracter(
             final SuggestionResults suggestionResults, final String consideredWord) {
+        int perfectMatchProbability = Dictionary.NOT_A_PROBABILITY;
         for (final SuggestedWordInfo suggestedWordInfo : suggestionResults) {
             if (suggestedWordInfo.mWord.equals(consideredWord)) {
+                perfectMatchProbability = mDictionaryFacilitator.getFrequency(consideredWord);
                 continue;
             }
             // Exact match can include case errors, accent errors, digraph conversions.
@@ -121,6 +124,17 @@
                 Log.d(TAG, "isExactMatchWithIntentionalOmission: "
                             + isExactMatchWithIntentionalOmission);
             }
+            if (perfectMatchProbability != Dictionary.NOT_A_PROBABILITY) {
+                final int topNonPerfectProbability = mDictionaryFacilitator.getFrequency(
+                        suggestedWordInfo.mWord);
+                if (DEBUG) {
+                    Log.d(TAG, "perfectMatchProbability: " + perfectMatchProbability);
+                    Log.d(TAG, "topNonPerfectProbability: " + topNonPerfectProbability);
+                }
+                if (perfectMatchProbability > topNonPerfectProbability) {
+                    return false;
+                }
+            }
             return isExactMatch || isExactMatchWithIntentionalOmission;
         }
         return false;
diff --git a/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java b/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java
index 7b678e1..c08697c 100644
--- a/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java
@@ -28,14 +28,14 @@
             new ConcurrentHashMap<>();
 
     /**
-     * Gets the executor for the given dictionary name.
+     * Gets the executor for the given id.
      */
-    public static PrioritizedSerialExecutor getExecutor(final String dictName) {
-        PrioritizedSerialExecutor executor = sExecutorMap.get(dictName);
+    public static PrioritizedSerialExecutor getExecutor(final String id) {
+        PrioritizedSerialExecutor executor = sExecutorMap.get(id);
         if (executor == null) {
             synchronized(sExecutorMap) {
-                executor = new PrioritizedSerialExecutor();
-                sExecutorMap.put(dictName, executor);
+                executor = new PrioritizedSerialExecutor(id);
+                sExecutorMap.put(id, executor);
             }
         }
         return executor;
diff --git a/java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java b/java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java
index 148f3bc..21949ff 100644
--- a/java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java
+++ b/java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java
@@ -21,6 +21,7 @@
 import java.util.Queue;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 
@@ -40,12 +41,26 @@
     // The task which is running now.
     private Runnable mActive;
 
-    public PrioritizedSerialExecutor() {
+    private static class ThreadFactoryWithId implements ThreadFactory {
+        private final String mId;
+
+        public ThreadFactoryWithId(final String id) {
+            mId = id;
+        }
+
+        @Override
+        public Thread newThread(final Runnable r) {
+            return new Thread(r, TAG + " - " + mId);
+        }
+    }
+
+    public PrioritizedSerialExecutor(final String id) {
         mTasks = new ConcurrentLinkedQueue<>();
         mPrioritizedTasks = new ConcurrentLinkedQueue<>();
         mIsShutdown = false;
         mThreadPoolExecutor = new ThreadPoolExecutor(1 /* corePoolSize */, 1 /* maximumPoolSize */,
-                0 /* keepAliveTime */, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1));
+                0 /* keepAliveTime */, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1),
+                new ThreadFactoryWithId(id));
     }
 
     /**
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index e41fe1d..d6a6196 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -95,15 +95,15 @@
     return reinterpret_cast<jlong>(dictionary);
 }
 
-static void latinime_BinaryDictionary_flush(JNIEnv *env, jclass clazz, jlong dict,
+static bool latinime_BinaryDictionary_flush(JNIEnv *env, jclass clazz, jlong dict,
         jstring filePath) {
     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
-    if (!dictionary) return;
+    if (!dictionary) return false;
     const jsize filePathUtf8Length = env->GetStringUTFLength(filePath);
     char filePathChars[filePathUtf8Length + 1];
     env->GetStringUTFRegion(filePath, 0, env->GetStringLength(filePath), filePathChars);
     filePathChars[filePathUtf8Length] = '\0';
-    dictionary->flush(filePathChars);
+    return dictionary->flush(filePathChars);
 }
 
 static bool latinime_BinaryDictionary_needsToRunGC(JNIEnv *env, jclass clazz,
@@ -113,15 +113,15 @@
     return dictionary->needsToRunGC(mindsBlockByGC == JNI_TRUE);
 }
 
-static void latinime_BinaryDictionary_flushWithGC(JNIEnv *env, jclass clazz, jlong dict,
+static bool latinime_BinaryDictionary_flushWithGC(JNIEnv *env, jclass clazz, jlong dict,
         jstring filePath) {
     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
-    if (!dictionary) return;
+    if (!dictionary) return false;
     const jsize filePathUtf8Length = env->GetStringUTFLength(filePath);
     char filePathChars[filePathUtf8Length + 1];
     env->GetStringUTFRegion(filePath, 0, env->GetStringLength(filePath), filePathChars);
     filePathChars[filePathUtf8Length] = '\0';
-    dictionary->flushWithGC(filePathChars);
+    return dictionary->flushWithGC(filePathChars);
 }
 
 static void latinime_BinaryDictionary_close(JNIEnv *env, jclass clazz, jlong dict) {
@@ -324,13 +324,13 @@
             outShortcutProbabilities);
 }
 
-static void latinime_BinaryDictionary_addUnigramWord(JNIEnv *env, jclass clazz, jlong dict,
+static bool latinime_BinaryDictionary_addUnigramWord(JNIEnv *env, jclass clazz, jlong dict,
         jintArray word, jint probability, jintArray shortcutTarget, jint shortcutProbability,
         jboolean isBeginningOfSentence, jboolean isNotAWord, jboolean isBlacklisted,
         jint timestamp) {
     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
     if (!dictionary) {
-        return;
+        return false;
     }
     jsize codePointCount = env->GetArrayLength(word);
     int codePoints[codePointCount];
@@ -344,15 +344,15 @@
     // Use 1 for count to indicate the word has inputted.
     const UnigramProperty unigramProperty(isBeginningOfSentence, isNotAWord,
             isBlacklisted, probability, timestamp, 0 /* level */, 1 /* count */, &shortcuts);
-    dictionary->addUnigramEntry(codePoints, codePointCount, &unigramProperty);
+    return dictionary->addUnigramEntry(codePoints, codePointCount, &unigramProperty);
 }
 
-static void latinime_BinaryDictionary_addBigramWords(JNIEnv *env, jclass clazz, jlong dict,
+static bool latinime_BinaryDictionary_addBigramWords(JNIEnv *env, jclass clazz, jlong dict,
         jintArray word0, jboolean isBeginningOfSentence, jintArray word1, jint probability,
         jint timestamp) {
     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
     if (!dictionary) {
-        return;
+        return false;
     }
     jsize word0Length = env->GetArrayLength(word0);
     int word0CodePoints[word0Length];
@@ -366,14 +366,14 @@
     const BigramProperty bigramProperty(&bigramTargetCodePoints, probability,
             timestamp, 0 /* level */, 1 /* count */);
     const PrevWordsInfo prevWordsInfo(word0CodePoints, word0Length, isBeginningOfSentence);
-    dictionary->addNgramEntry(&prevWordsInfo, &bigramProperty);
+    return dictionary->addNgramEntry(&prevWordsInfo, &bigramProperty);
 }
 
-static void latinime_BinaryDictionary_removeBigramWords(JNIEnv *env, jclass clazz, jlong dict,
+static bool latinime_BinaryDictionary_removeBigramWords(JNIEnv *env, jclass clazz, jlong dict,
         jintArray word0, jboolean isBeginningOfSentence, jintArray word1) {
     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
     if (!dictionary) {
-        return;
+        return false;
     }
     jsize word0Length = env->GetArrayLength(word0);
     int word0CodePoints[word0Length];
@@ -382,7 +382,7 @@
     int word1CodePoints[word1Length];
     env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints);
     const PrevWordsInfo prevWordsInfo(word0CodePoints, word0Length, isBeginningOfSentence);
-    dictionary->removeNgramEntry(&prevWordsInfo, word1CodePoints, word1Length);
+    return dictionary->removeNgramEntry(&prevWordsInfo, word1CodePoints, word1Length);
 }
 
 // Returns how many language model params are processed.
@@ -610,7 +610,7 @@
     },
     {
         const_cast<char *>("flushNative"),
-        const_cast<char *>("(JLjava/lang/String;)V"),
+        const_cast<char *>("(JLjava/lang/String;)Z"),
         reinterpret_cast<void *>(latinime_BinaryDictionary_flush)
     },
     {
@@ -620,7 +620,7 @@
     },
     {
         const_cast<char *>("flushWithGCNative"),
-        const_cast<char *>("(JLjava/lang/String;)V"),
+        const_cast<char *>("(JLjava/lang/String;)Z"),
         reinterpret_cast<void *>(latinime_BinaryDictionary_flushWithGC)
     },
     {
@@ -651,17 +651,17 @@
     },
     {
         const_cast<char *>("addUnigramWordNative"),
-        const_cast<char *>("(J[II[IIZZZI)V"),
+        const_cast<char *>("(J[II[IIZZZI)Z"),
         reinterpret_cast<void *>(latinime_BinaryDictionary_addUnigramWord)
     },
     {
         const_cast<char *>("addBigramWordsNative"),
-        const_cast<char *>("(J[IZ[III)V"),
+        const_cast<char *>("(J[IZ[III)Z"),
         reinterpret_cast<void *>(latinime_BinaryDictionary_addBigramWords)
     },
     {
         const_cast<char *>("removeBigramWordsNative"),
-        const_cast<char *>("(J[IZ[I)V"),
+        const_cast<char *>("(J[IZ[I)Z"),
         reinterpret_cast<void *>(latinime_BinaryDictionary_removeBigramWords)
     },
     {
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp
index bcf7d59..898b44f 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.cpp
+++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp
@@ -80,38 +80,38 @@
     return mBigramDictionary.getBigramProbability(prevWordsInfo, word, length);
 }
 
-void Dictionary::addUnigramEntry(const int *const word, const int length,
+bool Dictionary::addUnigramEntry(const int *const word, const int length,
         const UnigramProperty *const unigramProperty) {
     if (unigramProperty->representsBeginningOfSentence()
             && !mDictionaryStructureWithBufferPolicy->getHeaderStructurePolicy()
                     ->supportsBeginningOfSentence()) {
         AKLOGE("The dictionary doesn't support Beginning-of-Sentence.");
-        return;
+        return false;
     }
     TimeKeeper::setCurrentTime();
-    mDictionaryStructureWithBufferPolicy->addUnigramEntry(word, length, unigramProperty);
+    return mDictionaryStructureWithBufferPolicy->addUnigramEntry(word, length, unigramProperty);
 }
 
-void Dictionary::addNgramEntry(const PrevWordsInfo *const prevWordsInfo,
+bool Dictionary::addNgramEntry(const PrevWordsInfo *const prevWordsInfo,
         const BigramProperty *const bigramProperty) {
     TimeKeeper::setCurrentTime();
-    mDictionaryStructureWithBufferPolicy->addNgramEntry(prevWordsInfo, bigramProperty);
+    return mDictionaryStructureWithBufferPolicy->addNgramEntry(prevWordsInfo, bigramProperty);
 }
 
-void Dictionary::removeNgramEntry(const PrevWordsInfo *const prevWordsInfo,
+bool Dictionary::removeNgramEntry(const PrevWordsInfo *const prevWordsInfo,
         const int *const word, const int length) {
     TimeKeeper::setCurrentTime();
-    mDictionaryStructureWithBufferPolicy->removeNgramEntry(prevWordsInfo, word, length);
+    return mDictionaryStructureWithBufferPolicy->removeNgramEntry(prevWordsInfo, word, length);
 }
 
-void Dictionary::flush(const char *const filePath) {
+bool Dictionary::flush(const char *const filePath) {
     TimeKeeper::setCurrentTime();
-    mDictionaryStructureWithBufferPolicy->flush(filePath);
+    return mDictionaryStructureWithBufferPolicy->flush(filePath);
 }
 
-void Dictionary::flushWithGC(const char *const filePath) {
+bool Dictionary::flushWithGC(const char *const filePath) {
     TimeKeeper::setCurrentTime();
-    mDictionaryStructureWithBufferPolicy->flushWithGC(filePath);
+    return mDictionaryStructureWithBufferPolicy->flushWithGC(filePath);
 }
 
 bool Dictionary::needsToRunGC(const bool mindsBlockByGC) {
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h
index e665f7b..f6d406f 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.h
+++ b/native/jni/src/suggest/core/dictionary/dictionary.h
@@ -76,18 +76,18 @@
     int getBigramProbability(const PrevWordsInfo *const prevWordsInfo,
             const int *word, int length) const;
 
-    void addUnigramEntry(const int *const codePoints, const int codePointCount,
+    bool addUnigramEntry(const int *const codePoints, const int codePointCount,
             const UnigramProperty *const unigramProperty);
 
-    void addNgramEntry(const PrevWordsInfo *const prevWordsInfo,
+    bool addNgramEntry(const PrevWordsInfo *const prevWordsInfo,
             const BigramProperty *const bigramProperty);
 
-    void removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, const int *const word,
+    bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, const int *const word,
             const int length);
 
-    void flush(const char *const filePath);
+    bool flush(const char *const filePath);
 
-    void flushWithGC(const char *const filePath);
+    bool flushWithGC(const char *const filePath);
 
     bool needsToRunGC(const bool mindsBlockByGC);
 
diff --git a/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h b/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h
index 3fd815f..cda8940 100644
--- a/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h
+++ b/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h
@@ -81,9 +81,11 @@
     virtual bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo,
             const int *const word, const int length) = 0;
 
-    virtual void flush(const char *const filePath) = 0;
+    // Returns whether the flush was success or not.
+    virtual bool flush(const char *const filePath) = 0;
 
-    virtual void flushWithGC(const char *const filePath) = 0;
+    // Returns whether the GC and flush were success or not.
+    virtual bool flushWithGC(const char *const filePath) = 0;
 
     virtual bool needsToRunGC(const bool mindsBlockByGC) const = 0;
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v401/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v401/ver4_patricia_trie_policy.cpp
index 557a0b4..0f60a89 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v401/ver4_patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v401/ver4_patricia_trie_policy.cpp
@@ -296,26 +296,30 @@
     }
 }
 
-void Ver4PatriciaTriePolicy::flush(const char *const filePath) {
+bool Ver4PatriciaTriePolicy::flush(const char *const filePath) {
     if (!mBuffers->isUpdatable()) {
         AKLOGI("Warning: flush() is called for non-updatable dictionary. filePath: %s", filePath);
-        return;
+        return false;
     }
     if (!mWritingHelper.writeToDictFile(filePath, mUnigramCount, mBigramCount)) {
         AKLOGE("Cannot flush the dictionary to file.");
         mIsCorrupted = true;
+        return false;
     }
+    return true;
 }
 
-void Ver4PatriciaTriePolicy::flushWithGC(const char *const filePath) {
+bool Ver4PatriciaTriePolicy::flushWithGC(const char *const filePath) {
     if (!mBuffers->isUpdatable()) {
         AKLOGI("Warning: flushWithGC() is called for non-updatable dictionary.");
-        return;
+        return false;
     }
     if (!mWritingHelper.writeToDictFileWithGC(getRootPosition(), filePath)) {
         AKLOGE("Cannot flush the dictionary to file with GC.");
         mIsCorrupted = true;
+        return false;
     }
+    return true;
 }
 
 bool Ver4PatriciaTriePolicy::needsToRunGC(const bool mindsBlockByGC) const {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v401/ver4_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v401/ver4_patricia_trie_policy.h
index 9581388..b064aaf 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v401/ver4_patricia_trie_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v401/ver4_patricia_trie_policy.h
@@ -117,9 +117,9 @@
     bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, const int *const word,
             const int length);
 
-    void flush(const char *const filePath);
+    bool flush(const char *const filePath);
 
-    void flushWithGC(const char *const filePath);
+    bool flushWithGC(const char *const filePath);
 
     bool needsToRunGC(const bool mindsBlockByGC) const;
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h
index 6240d46..88bbfd9 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h
@@ -102,14 +102,16 @@
         return false;
     }
 
-    void flush(const char *const filePath) {
+    bool flush(const char *const filePath) {
         // This method should not be called for non-updatable dictionary.
         AKLOGI("Warning: flush() is called for non-updatable dictionary.");
+        return false;
     }
 
-    void flushWithGC(const char *const filePath) {
+    bool flushWithGC(const char *const filePath) {
         // This method should not be called for non-updatable dictionary.
         AKLOGI("Warning: flushWithGC() is called for non-updatable dictionary.");
+        return false;
     }
 
     bool needsToRunGC(const bool mindsBlockByGC) const {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
index 0247870..09c7b7d 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
@@ -304,26 +304,30 @@
     }
 }
 
-void Ver4PatriciaTriePolicy::flush(const char *const filePath) {
+bool Ver4PatriciaTriePolicy::flush(const char *const filePath) {
     if (!mBuffers->isUpdatable()) {
         AKLOGI("Warning: flush() is called for non-updatable dictionary. filePath: %s", filePath);
-        return;
+        return false;
     }
     if (!mWritingHelper.writeToDictFile(filePath, mUnigramCount, mBigramCount)) {
         AKLOGE("Cannot flush the dictionary to file.");
         mIsCorrupted = true;
+        return false;
     }
+    return true;
 }
 
-void Ver4PatriciaTriePolicy::flushWithGC(const char *const filePath) {
+bool Ver4PatriciaTriePolicy::flushWithGC(const char *const filePath) {
     if (!mBuffers->isUpdatable()) {
         AKLOGI("Warning: flushWithGC() is called for non-updatable dictionary.");
-        return;
+        return false;
     }
     if (!mWritingHelper.writeToDictFileWithGC(getRootPosition(), filePath)) {
         AKLOGE("Cannot flush the dictionary to file with GC.");
         mIsCorrupted = true;
+        return false;
     }
+    return true;
 }
 
 bool Ver4PatriciaTriePolicy::needsToRunGC(const bool mindsBlockByGC) const {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h
index 008f2e4..d198c97 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h
@@ -99,9 +99,9 @@
     bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, const int *const word1,
             const int length1);
 
-    void flush(const char *const filePath);
+    bool flush(const char *const filePath);
 
-    void flushWithGC(const char *const filePath);
+    bool flushWithGC(const char *const filePath);
 
     bool needsToRunGC(const bool mindsBlockByGC) const;
 
diff --git a/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java b/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java
index a4dbfaa..406e9a9 100644
--- a/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java
+++ b/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java
@@ -57,11 +57,36 @@
         assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries(
                 EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs));
 
-        typedWord = "were";
-        // For this test case, we consider "were" is a distracter to "we're".
+        typedWord = "youre";
+        // For this test case, we consider "youre" is a distracter to "you're".
         assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries(
                 EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs));
 
+        typedWord = "Banana";
+        // For this test case, we consider "Banana" is a distracter to "banana".
+        assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries(
+                EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs));
+
+        typedWord = "orange";
+        // For this test case, we consider "orange" is not a distracter to any word in dictionaries.
+        assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries(
+                EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs));
+
+        typedWord = "Orange";
+        // For this test case, we consider "Orange" is a distracter to "orange".
+        assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries(
+                EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs));
+
+        typedWord = "café";
+        // For this test case, we consider "café" is a distracter to "cafe".
+        assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries(
+                EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs));
+
+        typedWord = "cafe";
+        // For this test case, we consider "café" is not a distracter to any word in dictionaries.
+        assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries(
+                EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs));
+
         typedWord = "ill";
         // For this test case, we consider "ill" is not a distracter to any word in dictionaries.
         assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries(
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
index 0528e34..8f32e53 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
@@ -75,33 +75,54 @@
         for (final WordProperty wordProperty : dict) {
             // TODO: switch to addMultipleDictionaryEntries when they support shortcuts
             if (null == wordProperty.mShortcutTargets || wordProperty.mShortcutTargets.isEmpty()) {
-                binaryDict.addUnigramEntry(wordProperty.mWord, wordProperty.getProbability(),
+                if (!binaryDict.addUnigramEntry(wordProperty.mWord, wordProperty.getProbability(),
                         null /* shortcutTarget */, 0 /* shortcutProbability */,
                         wordProperty.mIsBeginningOfSentence, wordProperty.mIsNotAWord,
-                        wordProperty.mIsBlacklistEntry, 0 /* timestamp */);
+                        wordProperty.mIsBlacklistEntry, 0 /* timestamp */)) {
+                    MakedictLog.e("Cannot add unigram entry for " + wordProperty.mWord);
+                }
             } else {
                 for (final WeightedString shortcutTarget : wordProperty.mShortcutTargets) {
-                    binaryDict.addUnigramEntry(wordProperty.mWord, wordProperty.getProbability(),
+                    if (!binaryDict.addUnigramEntry(wordProperty.mWord,
+                            wordProperty.getProbability(),
                             shortcutTarget.mWord, shortcutTarget.getProbability(),
                             wordProperty.mIsBeginningOfSentence, wordProperty.mIsNotAWord,
-                            wordProperty.mIsBlacklistEntry, 0 /* timestamp */);
+                            wordProperty.mIsBlacklistEntry, 0 /* timestamp */)) {
+                        MakedictLog.e("Cannot add unigram entry for " + wordProperty.mWord
+                                + ", shortcutTarget: " + shortcutTarget.mWord);
+                        return;
+                    }
                 }
             }
             if (binaryDict.needsToRunGC(true /* mindsBlockByGC */)) {
-                binaryDict.flushWithGC();
+                if (!binaryDict.flushWithGC()) {
+                    MakedictLog.e("Cannot flush dict with GC.");
+                    return;
+                }
             }
         }
         for (final WordProperty word0Property : dict) {
             if (null == word0Property.mBigrams) continue;
             for (final WeightedString word1 : word0Property.mBigrams) {
-                binaryDict.addNgramEntry(new PrevWordsInfo(word0Property.mWord), word1.mWord,
-                        word1.getProbability(), 0 /* timestamp */);
+                final PrevWordsInfo prevWordsInfo = new PrevWordsInfo(word0Property.mWord);
+                if (!binaryDict.addNgramEntry(prevWordsInfo, word1.mWord,
+                        word1.getProbability(), 0 /* timestamp */)) {
+                    MakedictLog.e("Cannot add n-gram entry for "
+                            + prevWordsInfo + " -> " + word1.mWord);
+                    return;
+                }
                 if (binaryDict.needsToRunGC(true /* mindsBlockByGC */)) {
-                    binaryDict.flushWithGC();
+                    if (!binaryDict.flushWithGC()) {
+                        MakedictLog.e("Cannot flush dict with GC.");
+                        return;
+                    }
                 }
             }
         }
-        binaryDict.flushWithGC();
+        if (!binaryDict.flushWithGC()) {
+            MakedictLog.e("Cannot flush dict with GC.");
+            return;
+        }
         binaryDict.close();
     }
 
diff --git a/tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java b/tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java
index e075548..8b78816 100644
--- a/tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java
@@ -30,11 +30,12 @@
 public class PrioritizedSerialExecutorTests extends AndroidTestCase {
     private static final String TAG = PrioritizedSerialExecutorTests.class.getSimpleName();
 
+    private static final String TEST_EXECUTOR_ID = "test";
     private static final int NUM_OF_TASKS = 10;
     private static final int DELAY_FOR_WAITING_TASKS_MILLISECONDS = 500;
 
     public void testExecute() {
-        final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor();
+        final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor(TEST_EXECUTOR_ID);
         final AtomicInteger v = new AtomicInteger(0);
         for (int i = 0; i < NUM_OF_TASKS; ++i) {
             executor.execute(new Runnable() {
@@ -54,7 +55,7 @@
     }
 
     public void testExecutePrioritized() {
-        final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor();
+        final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor(TEST_EXECUTOR_ID);
         final AtomicInteger v = new AtomicInteger(0);
         for (int i = 0; i < NUM_OF_TASKS; ++i) {
             executor.executePrioritized(new Runnable() {
@@ -74,7 +75,7 @@
     }
 
     public void testExecuteCombined() {
-        final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor();
+        final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor(TEST_EXECUTOR_ID);
         final AtomicInteger v = new AtomicInteger(0);
         for (int i = 0; i < NUM_OF_TASKS; ++i) {
             executor.execute(new Runnable() {