Merge "Correctly read the header of APK-embedded dicts"
diff --git a/dictionaries/cs_wordlist.combined.gz b/dictionaries/cs_wordlist.combined.gz
index 7829d65..94ba863 100644
--- a/dictionaries/cs_wordlist.combined.gz
+++ b/dictionaries/cs_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/da_wordlist.combined.gz b/dictionaries/da_wordlist.combined.gz
index e714019..b4baf62 100644
--- a/dictionaries/da_wordlist.combined.gz
+++ b/dictionaries/da_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/de_wordlist.combined.gz b/dictionaries/de_wordlist.combined.gz
index 6a4bd44..400718d 100644
--- a/dictionaries/de_wordlist.combined.gz
+++ b/dictionaries/de_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/el_wordlist.combined.gz b/dictionaries/el_wordlist.combined.gz
index 74effa3..599734c 100644
--- a/dictionaries/el_wordlist.combined.gz
+++ b/dictionaries/el_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_GB_wordlist.combined.gz b/dictionaries/en_GB_wordlist.combined.gz
index ff69f64..22685d1 100644
--- a/dictionaries/en_GB_wordlist.combined.gz
+++ b/dictionaries/en_GB_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_US_wordlist.combined.gz b/dictionaries/en_US_wordlist.combined.gz
index 2c80bc4..92a3761 100644
--- a/dictionaries/en_US_wordlist.combined.gz
+++ b/dictionaries/en_US_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_emoji.combined.gz b/dictionaries/en_emoji.combined.gz
index 0fc009d..4d9cf1b 100644
--- a/dictionaries/en_emoji.combined.gz
+++ b/dictionaries/en_emoji.combined.gz
Binary files differ
diff --git a/dictionaries/en_wordlist.combined.gz b/dictionaries/en_wordlist.combined.gz
index 06fb64f..f8b71c8 100644
--- a/dictionaries/en_wordlist.combined.gz
+++ b/dictionaries/en_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/es_wordlist.combined.gz b/dictionaries/es_wordlist.combined.gz
index 0a48b6d..56617db 100644
--- a/dictionaries/es_wordlist.combined.gz
+++ b/dictionaries/es_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/fi_wordlist.combined.gz b/dictionaries/fi_wordlist.combined.gz
index eefbfe5..b7332ad 100644
--- a/dictionaries/fi_wordlist.combined.gz
+++ b/dictionaries/fi_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/fr_emoji.combined.gz b/dictionaries/fr_emoji.combined.gz
index f292977..5c9c7a0 100644
--- a/dictionaries/fr_emoji.combined.gz
+++ b/dictionaries/fr_emoji.combined.gz
Binary files differ
diff --git a/dictionaries/fr_wordlist.combined.gz b/dictionaries/fr_wordlist.combined.gz
index b568838..697f367 100644
--- a/dictionaries/fr_wordlist.combined.gz
+++ b/dictionaries/fr_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/hr_wordlist.combined.gz b/dictionaries/hr_wordlist.combined.gz
index 864f676..9a2086f 100644
--- a/dictionaries/hr_wordlist.combined.gz
+++ b/dictionaries/hr_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/it_wordlist.combined.gz b/dictionaries/it_wordlist.combined.gz
index ac977e3..5a5cbdc 100644
--- a/dictionaries/it_wordlist.combined.gz
+++ b/dictionaries/it_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/iw_wordlist.combined.gz b/dictionaries/iw_wordlist.combined.gz
index 36b0478..13eab9f 100644
--- a/dictionaries/iw_wordlist.combined.gz
+++ b/dictionaries/iw_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/lt_wordlist.combined.gz b/dictionaries/lt_wordlist.combined.gz
index 029722d..961266b 100644
--- a/dictionaries/lt_wordlist.combined.gz
+++ b/dictionaries/lt_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/lv_wordlist.combined.gz b/dictionaries/lv_wordlist.combined.gz
index 41e1c28..ae906a9 100644
--- a/dictionaries/lv_wordlist.combined.gz
+++ b/dictionaries/lv_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/nb_wordlist.combined.gz b/dictionaries/nb_wordlist.combined.gz
index b699912..1c0f2cf 100644
--- a/dictionaries/nb_wordlist.combined.gz
+++ b/dictionaries/nb_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/nl_wordlist.combined.gz b/dictionaries/nl_wordlist.combined.gz
index 89c2388..37ba8ab 100644
--- a/dictionaries/nl_wordlist.combined.gz
+++ b/dictionaries/nl_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/pl_wordlist.combined.gz b/dictionaries/pl_wordlist.combined.gz
index 2b53f69..ba71a55 100644
--- a/dictionaries/pl_wordlist.combined.gz
+++ b/dictionaries/pl_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/pt_BR_wordlist.combined.gz b/dictionaries/pt_BR_wordlist.combined.gz
index 2d22447..02df1c1 100644
--- a/dictionaries/pt_BR_wordlist.combined.gz
+++ b/dictionaries/pt_BR_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/pt_PT_wordlist.combined.gz b/dictionaries/pt_PT_wordlist.combined.gz
index 1504165..bcd50ab 100644
--- a/dictionaries/pt_PT_wordlist.combined.gz
+++ b/dictionaries/pt_PT_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/ru_wordlist.combined.gz b/dictionaries/ru_wordlist.combined.gz
index 8e654e9..acde9bc 100644
--- a/dictionaries/ru_wordlist.combined.gz
+++ b/dictionaries/ru_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/sl_wordlist.combined.gz b/dictionaries/sl_wordlist.combined.gz
index 55e1bb1..a7240fe 100644
--- a/dictionaries/sl_wordlist.combined.gz
+++ b/dictionaries/sl_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/sr_wordlist.combined.gz b/dictionaries/sr_wordlist.combined.gz
index 8488a08..30ce996 100644
--- a/dictionaries/sr_wordlist.combined.gz
+++ b/dictionaries/sr_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/sv_wordlist.combined.gz b/dictionaries/sv_wordlist.combined.gz
index 6342520..b6ebab3 100644
--- a/dictionaries/sv_wordlist.combined.gz
+++ b/dictionaries/sv_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/tr_wordlist.combined.gz b/dictionaries/tr_wordlist.combined.gz
index 0251778..306cea1 100644
--- a/dictionaries/tr_wordlist.combined.gz
+++ b/dictionaries/tr_wordlist.combined.gz
Binary files differ
diff --git a/java/res/raw/main_de.dict b/java/res/raw/main_de.dict
index c65698d..3cbf710 100644
--- a/java/res/raw/main_de.dict
+++ b/java/res/raw/main_de.dict
Binary files differ
diff --git a/java/res/raw/main_en.dict b/java/res/raw/main_en.dict
index 57f3fe7..49adc9a 100644
--- a/java/res/raw/main_en.dict
+++ b/java/res/raw/main_en.dict
Binary files differ
diff --git a/java/res/raw/main_es.dict b/java/res/raw/main_es.dict
index 261ab8c..fe24cd6 100644
--- a/java/res/raw/main_es.dict
+++ b/java/res/raw/main_es.dict
Binary files differ
diff --git a/java/res/raw/main_fr.dict b/java/res/raw/main_fr.dict
index c240d96..94d1b96 100644
--- a/java/res/raw/main_fr.dict
+++ b/java/res/raw/main_fr.dict
Binary files differ
diff --git a/java/res/raw/main_it.dict b/java/res/raw/main_it.dict
index 98135ce..ff11b97 100644
--- a/java/res/raw/main_it.dict
+++ b/java/res/raw/main_it.dict
Binary files differ
diff --git a/java/res/raw/main_pt_br.dict b/java/res/raw/main_pt_br.dict
index 21bbe7c..9fa5044 100644
--- a/java/res/raw/main_pt_br.dict
+++ b/java/res/raw/main_pt_br.dict
Binary files differ
diff --git a/java/res/raw/main_ru.dict b/java/res/raw/main_ru.dict
index c2b1e67..0f08f17 100644
--- a/java/res/raw/main_ru.dict
+++ b/java/res/raw/main_ru.dict
Binary files differ
diff --git a/java/res/xml/row_pcqwerty5.xml b/java/res/xml/row_pcqwerty5.xml
index a72f388..f6438ab 100644
--- a/java/res/xml/row_pcqwerty5.xml
+++ b/java/res/xml/row_pcqwerty5.xml
@@ -64,18 +64,8 @@
             latin:keyStyle="defaultEnterKeyStyle"
             latin:keySpec="!icon/enter_key|!code/key_enter"
             latin:keyWidth="15.384%p" />
-        <switch>
-            <case
-                latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted"
-            >
-                <Spacer />
-            </case>
-            <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" -->
-            <default>
-                <Key
-                    latin:keyStyle="emojiKeyStyle"
-                    latin:keyWidth="fillRight" />
-            </default>
-        </switch>
+        <Key
+            latin:keyStyle="emojiKeyStyle"
+            latin:keyWidth="fillRight" />
     </Row>
 </merge>
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
index 4c53b52..7fff46a 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
@@ -25,7 +25,6 @@
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.Rect;
-import android.graphics.Typeface;
 import android.os.Build;
 import android.os.CountDownTimer;
 import android.preference.PreferenceManager;
@@ -49,6 +48,8 @@
 import com.android.inputmethod.keyboard.internal.DynamicGridKeyboard;
 import com.android.inputmethod.keyboard.internal.EmojiLayoutParams;
 import com.android.inputmethod.keyboard.internal.EmojiPageKeyboardView;
+import com.android.inputmethod.keyboard.internal.KeyDrawParams;
+import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
 import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SubtypeSwitcher;
@@ -634,20 +635,23 @@
         // TODO:
     }
 
-    // Hack: These parameters are hacky.
-    public void startEmojiPalettes(final String switchToAlphaLabel, final int switchToAlphaColor,
-            final float switchToAlphaSize, final Typeface switchToAlphaTypeface) {
+    private static void setupAlphabetKey(final TextView alphabetKey, final String label,
+            final KeyDrawParams params) {
+        alphabetKey.setText(label);
+        alphabetKey.setTextColor(params.mTextColor);
+        alphabetKey.setTextSize(TypedValue.COMPLEX_UNIT_PX, params.mLabelSize);
+        alphabetKey.setTypeface(params.mTypeface);
+    }
+
+    public void startEmojiPalettes(final String switchToAlphaLabel,
+            final KeyVisualAttributes keyVisualAttr) {
         if (DEBUG_PAGER) {
             Log.d(TAG, "allocate emoji palettes memory " + mCurrentPagerPosition);
         }
-        mAlphabetKeyLeft.setText(switchToAlphaLabel);
-        mAlphabetKeyLeft.setTextColor(switchToAlphaColor);
-        mAlphabetKeyLeft.setTextSize(TypedValue.COMPLEX_UNIT_PX, switchToAlphaSize);
-        mAlphabetKeyLeft.setTypeface(switchToAlphaTypeface);
-        mAlphabetKeyRight.setText(switchToAlphaLabel);
-        mAlphabetKeyRight.setTextColor(switchToAlphaColor);
-        mAlphabetKeyRight.setTextSize(TypedValue.COMPLEX_UNIT_PX, switchToAlphaSize);
-        mAlphabetKeyRight.setTypeface(switchToAlphaTypeface);
+        final KeyDrawParams params = new KeyDrawParams();
+        params.updateParams(mEmojiLayoutParams.getActionBarHeight(), keyVisualAttr);
+        setupAlphabetKey(mAlphabetKeyLeft, switchToAlphaLabel, params);
+        setupAlphabetKey(mAlphabetKeyRight, switchToAlphaLabel, params);
         mEmojiPager.setAdapter(mEmojiPalettesAdapter);
         mEmojiPager.setCurrentItem(mCurrentPagerPosition);
     }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 6215e27..f975863 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
-import android.graphics.Paint;
 import android.preference.PreferenceManager;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
@@ -31,7 +30,7 @@
 import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
 import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException;
 import com.android.inputmethod.keyboard.internal.KeyboardState;
-import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.keyboard.internal.KeyboardTextsSet;
 import com.android.inputmethod.latin.InputView;
 import com.android.inputmethod.latin.LatinIME;
 import com.android.inputmethod.latin.LatinImeLogger;
@@ -81,8 +80,9 @@
     private KeyboardState mState;
 
     private KeyboardLayoutSet mKeyboardLayoutSet;
+    // TODO: The following {@link KeyboardTextsSet} should be in {@link KeyboardLayoutSet}.
+    private final KeyboardTextsSet mKeyboardTextsSet = new KeyboardTextsSet();
     private SettingsValues mCurrentSettingsValues;
-    private Key mSwitchToAlphaKey;
 
     /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of
      * what user actually typed. */
@@ -163,8 +163,7 @@
         mCurrentSettingsValues = settingsValues;
         try {
             mState.onLoadKeyboard();
-            final Keyboard symbols = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS);
-            mSwitchToAlphaKey = symbols.getKey(Constants.CODE_SWITCH_ALPHA_SYMBOL);
+            mKeyboardTextsSet.setLocale(mSubtypeSwitcher.getCurrentSubtypeLocale());
         } catch (KeyboardLayoutSetException e) {
             Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause());
             LatinImeLogger.logOnException(e.mKeyboardId.toString(), e.getCause());
@@ -290,10 +289,9 @@
     @Override
     public void setEmojiKeyboard() {
         mMainKeyboardFrame.setVisibility(View.GONE);
-        final Paint paint = mKeyboardView.newLabelPaint(mSwitchToAlphaKey);
         mEmojiPalettesView.startEmojiPalettes(
-                mSwitchToAlphaKey.getLabel(), paint.getColor(), paint.getTextSize(),
-                paint.getTypeface());
+                mKeyboardTextsSet.getText(KeyboardTextsSet.SWITCH_TO_ALPHA_KEY_LABEL),
+                mKeyboardView.getKeyVisualAttribute());
         mEmojiPalettesView.setVisibility(View.VISIBLE);
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index dd3ab9c..18e51d3 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -146,6 +146,10 @@
         mPaint.setAntiAlias(true);
     }
 
+    public KeyVisualAttributes getKeyVisualAttribute() {
+        return mKeyVisualAttributes;
+    }
+
     private static void blendAlpha(final Paint paint, final int alpha) {
         final int color = paint.getColor();
         paint.setARGB((paint.getAlpha() * alpha) / Constants.Color.ALPHA_OPAQUE,
diff --git a/java/src/com/android/inputmethod/keyboard/internal/EmojiLayoutParams.java b/java/src/com/android/inputmethod/keyboard/internal/EmojiLayoutParams.java
index 12e0632..d57ea5a 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/EmojiLayoutParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/EmojiLayoutParams.java
@@ -73,9 +73,13 @@
         ll.setLayoutParams(lp);
     }
 
+    public int getActionBarHeight() {
+        return mEmojiActionBarHeight - mBottomPadding;
+    }
+
     public void setActionBarProperties(final LinearLayout ll) {
         final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams();
-        lp.height = mEmojiActionBarHeight - mBottomPadding;
+        lp.height = getActionBarHeight();
         ll.setLayoutParams(lp);
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
index 89221ba..976038c 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
@@ -29,6 +29,8 @@
 
 public final class KeyboardTextsSet {
     public static final String PREFIX_TEXT = "!text/";
+    public static final String SWITCH_TO_ALPHA_KEY_LABEL = "label_to_alpha_key";
+
     private static final char BACKSLASH = Constants.CODE_BACKSLASH;
     private static final int MAX_STRING_REFERENCE_INDIRECTION = 10;
 
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java
index e767e4b..2dba713 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java
@@ -28,6 +28,7 @@
 import com.android.inputmethod.latin.personalization.UserHistoryDictionary;
 import com.android.inputmethod.latin.settings.SettingsValues;
 import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.utils.ExecutorUtils;
 import com.android.inputmethod.latin.utils.LanguageModelParam;
 
 import java.io.File;
@@ -208,8 +209,7 @@
         if (listener != null) {
             listener.onUpdateMainDictionaryAvailability(hasMainDictionary());
         }
-        new Thread("InitializeBinaryDictionary") {
-            @Override
+        ExecutorUtils.getExecutor("InitializeBinaryDictionary").execute(new Runnable() {
             public void run() {
                 final DictionaryCollection newMainDict =
                         DictionaryFactory.createMainDictionaryFromManager(context, locale);
@@ -219,7 +219,7 @@
                 }
                 mLatchForWaitingLoadingMainDictionary.countDown();
             }
-        }.start();
+        });
     }
 
     // The main dictionary could have been loaded asynchronously.  Don't cache the return value
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index f9ab941..c3e33c0 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -29,6 +29,7 @@
 import com.android.inputmethod.latin.utils.AsyncResultHolder;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 import com.android.inputmethod.latin.utils.CombinedFormatUtils;
+import com.android.inputmethod.latin.utils.ExecutorUtils;
 import com.android.inputmethod.latin.utils.FileUtils;
 import com.android.inputmethod.latin.utils.LanguageModelParam;
 import com.android.inputmethod.latin.utils.PrioritizedSerialExecutor;
@@ -80,9 +81,6 @@
     private static final ConcurrentHashMap<String, DictionaryUpdateController>
             sDictNameDictionaryUpdateControllerMap = CollectionUtils.newConcurrentHashMap();
 
-    private static final ConcurrentHashMap<String, PrioritizedSerialExecutor>
-            sDictNameExecutorMap = CollectionUtils.newConcurrentHashMap();
-
     /** The application context. */
     protected final Context mContext;
 
@@ -163,33 +161,6 @@
         return recorder;
     }
 
-    /**
-     * Gets the executor for the given dictionary name.
-     */
-    private static PrioritizedSerialExecutor getExecutor(final String dictName) {
-        PrioritizedSerialExecutor executor = sDictNameExecutorMap.get(dictName);
-        if (executor == null) {
-            synchronized(sDictNameExecutorMap) {
-                executor = new PrioritizedSerialExecutor();
-                sDictNameExecutorMap.put(dictName, executor);
-            }
-        }
-        return executor;
-    }
-
-    /**
-     * Shutdowns all executors and removes all executors from the executor map for testing.
-     */
-    @UsedForTesting
-    public static void shutdownAllExecutors() {
-        synchronized(sDictNameExecutorMap) {
-            for (final PrioritizedSerialExecutor executor : sDictNameExecutorMap.values()) {
-                executor.shutdown();
-                sDictNameExecutorMap.remove(executor);
-            }
-        }
-    }
-
     private static AbstractDictionaryWriter getDictionaryWriter(
             final boolean isDynamicPersonalizationDictionary) {
         if (isDynamicPersonalizationDictionary) {
@@ -243,10 +214,10 @@
      */
     @Override
     public void close() {
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
-                if (mBinaryDictionary!= null) {
+                if (mBinaryDictionary != null) {
                     mBinaryDictionary.close();
                     mBinaryDictionary = null;
                 }
@@ -256,7 +227,7 @@
 
     protected void closeBinaryDictionary() {
         // Ensure that no other threads are accessing the local binary dictionary.
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
                 if (mBinaryDictionary != null) {
@@ -287,7 +258,7 @@
     }
 
     protected void clear() {
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
                 if (mDictionaryWriter == null) {
@@ -331,7 +302,7 @@
      * Check whether GC is needed and run GC if required.
      */
     protected void runGCIfRequired(final boolean mindsBlockByGC) {
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
                 runGCIfRequiredInternalLocked(mindsBlockByGC);
@@ -344,7 +315,7 @@
         if (mBinaryDictionary.needsToRunGC(mindsBlockByGC)) {
             if (setProcessingLargeTaskIfNot()) {
                 // Run GC after currently existing time sensitive operations.
-                getExecutor(mDictName).executePrioritized(new Runnable() {
+                ExecutorUtils.getExecutor(mDictName).executePrioritized(new Runnable() {
                     @Override
                     public void run() {
                         try {
@@ -368,7 +339,7 @@
             Log.w(TAG, "addWordDynamically is called for non-updatable dictionary: " + mDictName);
             return;
         }
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
                 runGCIfRequiredInternalLocked(true /* mindsBlockByGC */);
@@ -388,7 +359,7 @@
                     + mDictName);
             return;
         }
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
                 runGCIfRequiredInternalLocked(true /* mindsBlockByGC */);
@@ -406,7 +377,7 @@
                     + mDictName);
             return;
         }
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
                 runGCIfRequiredInternalLocked(true /* mindsBlockByGC */);
@@ -430,7 +401,7 @@
                     "dictionary: " + mDictName);
             return;
         }
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
                 final boolean locked = setProcessingLargeTaskIfNot();
@@ -461,7 +432,7 @@
         }
         final AsyncResultHolder<ArrayList<SuggestedWordInfo>> holder =
                 new AsyncResultHolder<ArrayList<SuggestedWordInfo>>();
-        getExecutor(mDictName).executePrioritized(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).executePrioritized(new Runnable() {
             @Override
             public void run() {
                 if (mBinaryDictionary == null) {
@@ -500,7 +471,7 @@
             return false;
         }
         final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>();
-        getExecutor(mDictName).executePrioritized(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).executePrioritized(new Runnable() {
             @Override
             public void run() {
                 holder.set(isValidWordLocked(word));
@@ -560,7 +531,7 @@
         // swapping in the new one.
         // TODO: Ensure multi-thread assignment of mBinaryDictionary.
         final BinaryDictionary oldBinaryDictionary = mBinaryDictionary;
-        getExecutor(mDictName).executePrioritized(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).executePrioritized(new Runnable() {
             @Override
             public void run() {
                 mBinaryDictionary = newBinaryDictionary;
@@ -662,7 +633,7 @@
     private final void reloadDictionary() {
         // Ensure that only one thread attempts to read or write to the shared binary dictionary
         // file at the same time.
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
                 try {
@@ -696,7 +667,7 @@
                     // If we just loaded the binary dictionary, then mBinaryDictionary is not
                     // up-to-date yet so it's useless to test it right away. Schedule the check
                     // for right after it's loaded instead.
-                    getExecutor(mDictName).executePrioritized(new Runnable() {
+                    ExecutorUtils.getExecutor(mDictName).executePrioritized(new Runnable() {
                         @Override
                         public void run() {
                             if (mBinaryDictionary != null && !(isValidDictionary()
@@ -736,7 +707,7 @@
             }
         };
         final Runnable oldTask = mUnfinishedFlushingTask.getAndSet(newTask);
-        getExecutor(mDictName).replaceAndExecute(oldTask, newTask);
+        ExecutorUtils.getExecutor(mDictName).replaceAndExecute(oldTask, newTask);
     }
 
     /**
@@ -757,7 +728,7 @@
     @UsedForTesting
     public boolean isInUnderlyingBinaryDictionaryForTests(final String word) {
         final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>();
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
                 if (mDictType == Dictionary.TYPE_USER_HISTORY) {
@@ -771,7 +742,7 @@
     @UsedForTesting
     public void waitAllTasksForTests() {
         final CountDownLatch countDownLatch = new CountDownLatch(1);
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
                 countDownLatch.countDown();
@@ -787,7 +758,7 @@
     @UsedForTesting
     public void dumpAllWordsForDebug() {
         reloadDictionaryIfRequired();
-        getExecutor(mDictName).execute(new Runnable() {
+        ExecutorUtils.getExecutor(mDictName).execute(new Runnable() {
             @Override
             public void run() {
                 Log.d(TAG, "Dump dictionary: " + mDictName);
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 30b20a3..323256d 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -483,12 +483,16 @@
      *
      * @param start the character index where the selection should start.
      * @param end the character index where the selection should end.
-     * @return Returns true on success, false if the input connection is no longer valid either when
-     * setting the selection or when retrieving the text cache at that point.
+     * @return Returns true on success, false on failure: either the input connection is no longer
+     * valid when setting the selection or when retrieving the text cache at that point, or
+     * invalid arguments were passed.
      */
     public boolean setSelection(final int start, final int end) {
         if (DEBUG_BATCH_NESTING) checkBatchEdit();
         if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
+        if (start < 0 || end < 0) {
+            return false;
+        }
         mExpectedSelStart = start;
         mExpectedSelEnd = end;
         if (null != mIC) {
diff --git a/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java b/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java
new file mode 100644
index 0000000..ee9718a
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.utils;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Utilities to manage executors.
+ */
+public class ExecutorUtils {
+    private static final ConcurrentHashMap<String, PrioritizedSerialExecutor>
+            sExecutorMap = CollectionUtils.newConcurrentHashMap();
+    /**
+     * Gets the executor for the given dictionary name.
+     */
+    public static PrioritizedSerialExecutor getExecutor(final String dictName) {
+        PrioritizedSerialExecutor executor = sExecutorMap.get(dictName);
+        if (executor == null) {
+            synchronized(sExecutorMap) {
+                executor = new PrioritizedSerialExecutor();
+                sExecutorMap.put(dictName, executor);
+            }
+        }
+        return executor;
+    }
+
+    /**
+     * Shutdowns all executors and removes all executors from the executor map for testing.
+     */
+    @UsedForTesting
+    public static void shutdownAllExecutors() {
+        synchronized(sExecutorMap) {
+            for (final PrioritizedSerialExecutor executor : sExecutorMap.values()) {
+                executor.shutdown();
+                sExecutorMap.remove(executor);
+            }
+        }
+    }
+}
diff --git a/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp b/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp
index e1b3534..bc4ca8e 100644
--- a/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp
+++ b/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp
@@ -992,7 +992,16 @@
             }
         }
         if (character != NOT_AN_INDEX) {
-            codePointBuf[index] = proximityInfo->getCodePointOf(character);
+            const int codePoint = proximityInfo->getCodePointOf(character);
+            if (codePoint == NOT_A_CODE_POINT) {
+                AKLOGE("Key index(%d) is not found. Cannot construct most probable string",
+                        character);
+                ASSERT(false);
+                // Make the length zero, which means most probable string won't be used.
+                index = 0;
+                break;
+            }
+            codePointBuf[index] = codePoint;
             index++;
         }
         sumLogProbability += minLogProbability;