diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java
index 4ea7fb888479fc6cd327b2353b23e43574632e49..ee9125a079b101aef397c7670ee4e7e488c1ed53 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java
@@ -19,6 +19,7 @@ package com.android.inputmethod.compat;
 import android.os.Build;
 import android.view.inputmethod.InputMethodSubtype;
 
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.Constants;
 
 import java.lang.reflect.Constructor;
@@ -64,7 +65,12 @@ public final class InputMethodSubtypeCompatUtils {
     }
 
     public static boolean isAsciiCapable(final InputMethodSubtype subtype) {
-        return (Boolean)CompatUtils.invoke(subtype, false, METHOD_isAsciiCapable)
+        return isAsciiCapableWithAPI(subtype)
                 || subtype.containsExtraValueKey(Constants.Subtype.ExtraValue.ASCII_CAPABLE);
     }
+
+    @UsedForTesting
+    public static boolean isAsciiCapableWithAPI(final InputMethodSubtype subtype) {
+        return (Boolean)CompatUtils.invoke(subtype, false, METHOD_isAsciiCapable);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java b/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java
index 31fa867742e0104a3edf37a9d07faacaf60d2cec..ad411f9ee901c19becdc0f7fbc231396b556c384 100644
--- a/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java
+++ b/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java
@@ -150,8 +150,9 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment {
             // TODO: Should filter out already existing combinations of locale and layout.
             for (final String layout : SubtypeLocaleUtils.getPredefinedKeyboardLayoutSet()) {
                 // This is a dummy subtype with NO_LANGUAGE, only for display.
-                final InputMethodSubtype subtype = AdditionalSubtypeUtils.createAdditionalSubtype(
-                        SubtypeLocaleUtils.NO_LANGUAGE, layout, null);
+                final InputMethodSubtype subtype =
+                        AdditionalSubtypeUtils.createDummyAdditionalSubtype(
+                                SubtypeLocaleUtils.NO_LANGUAGE, layout);
                 add(new KeyboardLayoutSetItem(subtype));
             }
         }
@@ -286,8 +287,9 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment {
                         (SubtypeLocaleItem) mSubtypeLocaleSpinner.getSelectedItem();
                 final KeyboardLayoutSetItem layout =
                         (KeyboardLayoutSetItem) mKeyboardLayoutSetSpinner.getSelectedItem();
-                final InputMethodSubtype subtype = AdditionalSubtypeUtils.createAdditionalSubtype(
-                        locale.first, layout.first, Constants.Subtype.ExtraValue.ASCII_CAPABLE);
+                final InputMethodSubtype subtype =
+                        AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                                locale.first, layout.first);
                 setSubtype(subtype);
                 notifyChanged();
                 if (isEditing) {
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index b57eab31be47c54965441bfd6d8e2948114306b8..90c8f618f8c296d9b2c64f184aa04123949fcb66 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -374,8 +374,8 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
     public DictAndKeyboard createDictAndKeyboard(final Locale locale) {
         final int script = ScriptUtils.getScriptFromSpellCheckerLocale(locale);
         final String keyboardLayoutName = getKeyboardLayoutNameForScript(script);
-        final InputMethodSubtype subtype = AdditionalSubtypeUtils.createAdditionalSubtype(
-                locale.toString(), keyboardLayoutName, null);
+        final InputMethodSubtype subtype = AdditionalSubtypeUtils.createDummyAdditionalSubtype(
+                locale.toString(), keyboardLayoutName);
         final KeyboardLayoutSet keyboardLayoutSet = createKeyboardSetForSpellChecker(subtype);
 
         final DictionaryCollection dictionaryCollection =
diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
index 3ca7c7e1c3734cdb07bd185bce0ccfcc98a671b2..db7f2a56c1a8a8276addbb11ea3605fd935eab1c 100644
--- a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.latin.utils;
 
 import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE;
+import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.ASCII_CAPABLE;
 import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
 import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.IS_ADDITIONAL_SUBTYPE;
 import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET;
@@ -56,25 +57,33 @@ public final class AdditionalSubtypeUtils {
     private static final int LENGTH_WITH_EXTRA_VALUE = (INDEX_OF_EXTRA_VALUE + 1);
     private static final String PREF_SUBTYPE_SEPARATOR = ";";
 
-    public static InputMethodSubtype createAdditionalSubtype(final String localeString,
-            final String keyboardLayoutSetName, final String extraValue) {
-        final String layoutExtraValue = KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName;
-        final String layoutDisplayNameExtraValue;
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
-                && SubtypeLocaleUtils.isExceptionalLocale(localeString)) {
-            final String layoutDisplayName = SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(
-                    keyboardLayoutSetName);
-            layoutDisplayNameExtraValue = StringUtils.appendToCommaSplittableTextIfNotExists(
-                    UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME + "=" + layoutDisplayName, extraValue);
-        } else {
-            layoutDisplayNameExtraValue = extraValue;
-        }
-        final String additionalSubtypeExtraValue =
-                StringUtils.appendToCommaSplittableTextIfNotExists(
-                        IS_ADDITIONAL_SUBTYPE, layoutDisplayNameExtraValue);
+    private static InputMethodSubtype createAdditionalSubtypeInternal(
+            final String localeString, final String keyboardLayoutSetName,
+            final boolean isAsciiCapable, final boolean isEmojiCapable) {
         final int nameId = SubtypeLocaleUtils.getSubtypeNameId(localeString, keyboardLayoutSetName);
-        return buildInputMethodSubtype(
-                nameId, localeString, layoutExtraValue, additionalSubtypeExtraValue);
+        final String platformVersionDependentExtraValues = getPlatformVersionDependentExtraValue(
+                localeString, keyboardLayoutSetName, isAsciiCapable, isEmojiCapable);
+        final int platformVersionIndependentSubtypeId =
+                getPlatformVersionIndependentSubtypeId(localeString, keyboardLayoutSetName);
+        // NOTE: In KitKat and later, InputMethodSubtypeBuilder#setIsAsciiCapable is also available.
+        // TODO: Use InputMethodSubtypeBuilder#setIsAsciiCapable when appropriate.
+        return InputMethodSubtypeCompatUtils.newInputMethodSubtype(nameId,
+                R.drawable.ic_ime_switcher_dark, localeString, KEYBOARD_MODE,
+                platformVersionDependentExtraValues,
+                false /* isAuxiliary */, false /* overrideImplicitlyEnabledSubtype */,
+                platformVersionIndependentSubtypeId);
+    }
+
+    public static InputMethodSubtype createDummyAdditionalSubtype(
+            final String localeString, final String keyboardLayoutSetName) {
+        return createAdditionalSubtypeInternal(localeString, keyboardLayoutSetName,
+                false /* isAsciiCapable */, false /* isEmojiCapable */);
+    }
+
+    public static InputMethodSubtype createAsciiEmojiCapableAdditionalSubtype(
+            final String localeString, final String keyboardLayoutSetName) {
+        return createAdditionalSubtypeInternal(localeString, keyboardLayoutSetName,
+                true /* isAsciiCapable */, true /* isEmojiCapable */);
     }
 
     public static String getPrefSubtype(final InputMethodSubtype subtype) {
@@ -106,10 +115,10 @@ public final class AdditionalSubtypeUtils {
             }
             final String localeString = elems[INDEX_OF_LOCALE];
             final String keyboardLayoutSetName = elems[INDEX_OF_KEYBOARD_LAYOUT];
-            final String extraValue = (elems.length == LENGTH_WITH_EXTRA_VALUE)
-                    ? elems[INDEX_OF_EXTRA_VALUE] : null;
-            final InputMethodSubtype subtype = createAdditionalSubtype(
-                    localeString, keyboardLayoutSetName, extraValue);
+            // Here we assume that all the additional subtypes have AsciiCapable and EmojiCapable.
+            // This is actually what the setting dialog for additional subtype is doing.
+            final InputMethodSubtype subtype = createAsciiEmojiCapableAdditionalSubtype(
+                    localeString, keyboardLayoutSetName);
             if (subtype.getNameResId() == SubtypeLocaleUtils.UNKNOWN_KEYBOARD_LAYOUT) {
                 // Skip unknown keyboard layout subtype. This may happen when predefined keyboard
                 // layout has been removed.
@@ -148,35 +157,80 @@ public final class AdditionalSubtypeUtils {
         return sb.toString();
     }
 
-    private static InputMethodSubtype buildInputMethodSubtype(final int nameId,
-            final String localeString, final String layoutExtraValue,
-            final String additionalSubtypeExtraValue) {
-        // To preserve additional subtype settings and user's selection across OS updates, subtype
-        // id shouldn't be changed. New attributes, such as emojiCapable, are carefully excluded
-        // from the calculation of subtype id.
-        final String compatibleExtraValue = StringUtils.joinCommaSplittableText(
-                layoutExtraValue, additionalSubtypeExtraValue);
-        final int compatibleSubtypeId = getInputMethodSubtypeId(localeString, compatibleExtraValue);
-        final String extraValue;
-        // Color Emoji is supported from KitKat.
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
-            extraValue = StringUtils.appendToCommaSplittableTextIfNotExists(
-                    EMOJI_CAPABLE, compatibleExtraValue);
-        } else {
-            extraValue = compatibleExtraValue;
+    /**
+     * Returns the extra value that is optimized for the running OS.
+     * <p>
+     * Historically the extra value has been used as the last resort to annotate various kinds of
+     * attributes. Some of these attributes are valid only on some platform versions. Thus we cannot
+     * assume that the extra values stored in a persistent storage are always valid. We need to
+     * regenerate the extra value on the fly instead.
+     * </p>
+     * @param localeString the locale string (e.g., "en_US").
+     * @param keyboardLayoutSetName the keyboard layout set name (e.g., "dvorak").
+     * @param isAsciiCapable true when ASCII characters are supported with this layout.
+     * @param isEmojiCapable true when Unicode Emoji characters are supported with this layout.
+     * @return extra value that is optimized for the running OS.
+     * @see #getPlatformVersionIndependentSubtypeId(String, String)
+     */
+    private static String getPlatformVersionDependentExtraValue(final String localeString,
+            final String keyboardLayoutSetName, final boolean isAsciiCapable,
+            final boolean isEmojiCapable) {
+        final ArrayList<String> extraValueItems = new ArrayList<>();
+        extraValueItems.add(KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName);
+        if (isAsciiCapable) {
+            extraValueItems.add(ASCII_CAPABLE);
         }
-        return InputMethodSubtypeCompatUtils.newInputMethodSubtype(nameId,
-                R.drawable.ic_ime_switcher_dark, localeString, KEYBOARD_MODE, extraValue,
-                false, false, compatibleSubtypeId);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN &&
+                SubtypeLocaleUtils.isExceptionalLocale(localeString)) {
+            extraValueItems.add(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME + "=" +
+                    SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(keyboardLayoutSetName));
+        }
+        if (isEmojiCapable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            extraValueItems.add(EMOJI_CAPABLE);
+        }
+        extraValueItems.add(IS_ADDITIONAL_SUBTYPE);
+        return TextUtils.join(",", extraValueItems);
     }
 
-    private static int getInputMethodSubtypeId(final String localeString, final String extraValue) {
-        // From the compatibility point of view, the calculation of subtype id has been copied from
-        // {@link InputMethodSubtype} of JellyBean MR2.
+    /**
+     * Returns the subtype ID that is supposed to be compatible between different version of OSes.
+     * <p>
+     * From the compatibility point of view, it is important to keep subtype id predictable and
+     * stable between different OSes. For this purpose, the calculation code in this method is
+     * carefully chosen and then fixed. Treat the following code as no more or less than a
+     * hash function. Each component to be hashed can be different from the corresponding value
+     * that is used to instantiate {@link InputMethodSubtype} actually.
+     * For example, you don't need to update <code>compatibilityExtraValueItems</code> in this
+     * method even when we need to add some new extra values for the actual instance of
+     * {@link InputMethodSubtype}.
+     * </p>
+     * @param localeString the locale string (e.g., "en_US").
+     * @param keyboardLayoutSetName the keyboard layout set name (e.g., "dvorak").
+     * @return a platform-version independent subtype ID.
+     * @see #getPlatformVersionDependentExtraValue(String, String, boolean, boolean)
+     */
+    private static int getPlatformVersionIndependentSubtypeId(final String localeString,
+            final String keyboardLayoutSetName) {
+        // For compatibility reasons, we concatenate the extra values in the following order.
+        // - KeyboardLayoutSet
+        // - AsciiCapable
+        // - UntranslatableReplacementStringInSubtypeName
+        // - EmojiCapable
+        // - isAdditionalSubtype
+        final ArrayList<String> compatibilityExtraValueItems = new ArrayList<>();
+        compatibilityExtraValueItems.add(KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName);
+        compatibilityExtraValueItems.add(ASCII_CAPABLE);
+        if (SubtypeLocaleUtils.isExceptionalLocale(localeString)) {
+            compatibilityExtraValueItems.add(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME + "=" +
+                    SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(keyboardLayoutSetName));
+        }
+        compatibilityExtraValueItems.add(EMOJI_CAPABLE);
+        compatibilityExtraValueItems.add(IS_ADDITIONAL_SUBTYPE);
+        final String compatibilityExtraValues = TextUtils.join(",", compatibilityExtraValueItems);
         return Arrays.hashCode(new Object[] {
                 localeString,
                 KEYBOARD_MODE,
-                extraValue,
+                compatibilityExtraValues,
                 false /* isAuxiliary */,
                 false /* overrideImplicitlyEnabledSubtype */ });
     }
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
index ab7d1b28df13eb272c51684439c2de7bc2bf630c..cf884bfeaba36ef54df552f9b5d87b005a9d35f7 100644
--- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
+++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java
@@ -104,8 +104,8 @@ public abstract class KeyboardLayoutSetTestsBase extends AndroidTestCase {
             final Locale subtypeLocale = SubtypeLocaleUtils.getSubtypeLocale(subtype);
             if (locale.equals(subtypeLocale)) {
                 // Create additional subtype.
-                return AdditionalSubtypeUtils.createAdditionalSubtype(
-                        locale.toString(), keyboardLayout, null /* extraValue */);
+                return AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                        locale.toString(), keyboardLayout);
             }
         }
         throw new RuntimeException(
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java b/tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java
index 0be1e374ca346c0afbcae64347390be38c672147..6ea27588eb25162e72667c3af02f1821c21323ff 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java
@@ -67,10 +67,10 @@ public class LanguageOnSpacebarHelperTests extends AndroidTestCase {
                 Locale.CANADA_FRENCH.toString(), "qwerty");
         FR_CH_SWISS = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
                 "fr_CH", "swiss");
-        FR_CH_QWERTZ = AdditionalSubtypeUtils.createAdditionalSubtype(
-                "fr_CH", "qwertz", null);
-        FR_CH_QWERTY = AdditionalSubtypeUtils.createAdditionalSubtype(
-                "fr_CH", "qwerty", null);
+        FR_CH_QWERTZ = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                "fr_CH", "qwertz");
+        FR_CH_QWERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                "fr_CH", "qwerty");
         ZZ_QWERTY = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
                 SubtypeLocaleUtils.NO_LANGUAGE, "qwerty");
     }
diff --git a/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..91c9c377510db2199a9345e19c8ebaeae264747c
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtilsTests.java
@@ -0,0 +1,175 @@
+/*
+ * 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 android.content.Context;
+import android.os.Build;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
+
+import java.util.Locale;
+
+import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE;
+import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.ASCII_CAPABLE;
+import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
+import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.IS_ADDITIONAL_SUBTYPE;
+import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET;
+import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue
+        .UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME;
+
+@SmallTest
+public class AdditionalSubtypeUtilsTests extends AndroidTestCase {
+
+    /**
+     * Predictable subtype ID for en_US dvorak layout. This is actually a hash code calculated as
+     * follows.
+     * <code>
+     * final boolean isAuxiliary = false;
+     * final boolean overrideImplicitlyEnabledSubtype = false;
+     * final int SUBTYPE_ID_EN_US_DVORAK = Arrays.hashCode(new Object[] {
+     *         "en_US",
+     *         "keyboard",
+     *         "KeyboardLayoutSet=dvorak"
+     *                 + ",AsciiCapable"
+     *                 + ",UntranslatableReplacementStringInSubtypeName=Dvorak"
+     *                 + ",EmojiCapable"
+     *                 + ",isAdditionalSubtype",
+     *         isAuxiliary,
+     *         overrideImplicitlyEnabledSubtype });
+     * </code>
+     */
+    private static int SUBTYPE_ID_EN_US_DVORAK = 0xb3c0cc56;
+    private static String EXTRA_VALUE_EN_US_DVORAK_ICS =
+            "KeyboardLayoutSet=dvorak" +
+            ",AsciiCapable" +
+            ",isAdditionalSubtype";
+    private static String EXTRA_VALUE_EN_US_DVORAK_JELLY_BEAN =
+            "KeyboardLayoutSet=dvorak" +
+            ",AsciiCapable" +
+            ",UntranslatableReplacementStringInSubtypeName=Dvorak" +
+            ",isAdditionalSubtype";
+    private static String EXTRA_VALUE_EN_US_DVORAK_KITKAT =
+            "KeyboardLayoutSet=dvorak" +
+            ",AsciiCapable" +
+            ",UntranslatableReplacementStringInSubtypeName=Dvorak" +
+            ",EmojiCapable" +
+            ",isAdditionalSubtype";
+
+    /**
+     * Predictable subtype ID for azerty layout. This is actually a hash code calculated as follows.
+     * <code>
+     * final boolean isAuxiliary = false;
+     * final boolean overrideImplicitlyEnabledSubtype = false;
+     * final int SUBTYPE_ID_ZZ_AZERTY = Arrays.hashCode(new Object[] {
+     *         "zz",
+     *         "keyboard",
+     *         "KeyboardLayoutSet=azerty"
+     *                 + ",AsciiCapable"
+     *                 + ",EmojiCapable"
+     *                 + ",isAdditionalSubtype",
+     *         isAuxiliary,
+     *         overrideImplicitlyEnabledSubtype });
+     * </code>
+     */
+    private static int SUBTYPE_ID_ZZ_AZERTY = 0x5b6be697;
+    private static String EXTRA_VALUE_ZZ_AZERTY_ICS =
+            "KeyboardLayoutSet=azerty" +
+            ",AsciiCapable" +
+            ",isAdditionalSubtype";
+    private static String EXTRA_VALUE_ZZ_AZERTY_KITKAT =
+            "KeyboardLayoutSet=azerty" +
+            ",AsciiCapable" +
+            ",EmojiCapable" +
+            ",isAdditionalSubtype";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        final Context context = getContext();
+        SubtypeLocaleUtils.init(context);
+    }
+
+    private static void assertEnUsDvorak(InputMethodSubtype subtype) {
+        assertEquals("en_US", subtype.getLocale());
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            assertEquals(EXTRA_VALUE_EN_US_DVORAK_KITKAT, subtype.getExtraValue());
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            assertEquals(EXTRA_VALUE_EN_US_DVORAK_JELLY_BEAN, subtype.getExtraValue());
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            assertEquals(EXTRA_VALUE_EN_US_DVORAK_ICS, subtype.getExtraValue());
+        }
+        assertTrue(subtype.containsExtraValueKey(ASCII_CAPABLE));
+        assertTrue(InputMethodSubtypeCompatUtils.isAsciiCapable(subtype));
+        // TODO: Enable following test
+        // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+        //    assertTrue(InputMethodSubtypeCompatUtils.isAsciiCapableWithAPI(subtype));
+        // }
+        assertTrue(subtype.containsExtraValueKey(EMOJI_CAPABLE));
+        assertTrue(subtype.containsExtraValueKey(IS_ADDITIONAL_SUBTYPE));
+        assertEquals("dvorak", subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET));
+        assertEquals("Dvorak", subtype.getExtraValueOf(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME));
+        assertEquals(KEYBOARD_MODE, subtype.getMode());
+        assertEquals(SUBTYPE_ID_EN_US_DVORAK, subtype.hashCode());
+    }
+
+    private static void assertAzerty(InputMethodSubtype subtype) {
+        assertEquals("zz", subtype.getLocale());
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            assertEquals(EXTRA_VALUE_ZZ_AZERTY_KITKAT, subtype.getExtraValue());
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            assertEquals(EXTRA_VALUE_ZZ_AZERTY_ICS, subtype.getExtraValue());
+        }
+        assertTrue(subtype.containsExtraValueKey(ASCII_CAPABLE));
+        assertTrue(InputMethodSubtypeCompatUtils.isAsciiCapable(subtype));
+        // TODO: Enable following test
+        // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+        //    assertTrue(InputMethodSubtypeCompatUtils.isAsciiCapableWithAPI(subtype));
+        // }
+        assertTrue(subtype.containsExtraValueKey(EMOJI_CAPABLE));
+        assertTrue(subtype.containsExtraValueKey(IS_ADDITIONAL_SUBTYPE));
+        assertEquals("azerty", subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET));
+        assertFalse(subtype.containsExtraValueKey(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME));
+        assertEquals(KEYBOARD_MODE, subtype.getMode());
+        assertEquals(SUBTYPE_ID_ZZ_AZERTY, subtype.hashCode());
+    }
+
+    public void testRestorable() {
+        final InputMethodSubtype EN_UK_DVORAK =
+                AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                        Locale.US.toString(), "dvorak");
+        final InputMethodSubtype ZZ_AZERTY =
+                AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                        SubtypeLocaleUtils.NO_LANGUAGE, "azerty");
+        assertEnUsDvorak(EN_UK_DVORAK);
+        assertAzerty(ZZ_AZERTY);
+
+        // Make sure the subtype can be stored and restored in a deterministic manner.
+        final InputMethodSubtype[] subtypes = { EN_UK_DVORAK, ZZ_AZERTY };
+        final String prefSubtype = AdditionalSubtypeUtils.createPrefSubtypes(subtypes);
+        final InputMethodSubtype[] restoredSubtypes =
+                AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtype);
+        assertEquals(2, restoredSubtypes.length);
+        final InputMethodSubtype restored_EN_UK_DVORAK = restoredSubtypes[0];
+        final InputMethodSubtype restored_ZZ_AZERTY = restoredSubtypes[1];
+
+        assertEnUsDvorak(restored_EN_UK_DVORAK);
+        assertAzerty(restored_ZZ_AZERTY);
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java
index 4156de78b984e795db294ef4de358955ebcb4c5b..fdde3425130c373f070fa78cde3a959cf30d19df 100644
--- a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java
@@ -87,20 +87,20 @@ public class SpacebarLanguagetUtilsTests extends AndroidTestCase {
                 "de_CH", "swiss");
         ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
                 SubtypeLocaleUtils.NO_LANGUAGE, "qwerty");
-        DE_QWERTY = AdditionalSubtypeUtils.createAdditionalSubtype(
-                Locale.GERMAN.toString(), "qwerty", null);
-        FR_QWERTZ = AdditionalSubtypeUtils.createAdditionalSubtype(
-                Locale.FRENCH.toString(), "qwertz", null);
-        EN_US_AZERTY = AdditionalSubtypeUtils.createAdditionalSubtype(
-                Locale.US.toString(), "azerty", null);
-        EN_UK_DVORAK = AdditionalSubtypeUtils.createAdditionalSubtype(
-                Locale.UK.toString(), "dvorak", null);
-        ES_US_COLEMAK = AdditionalSubtypeUtils.createAdditionalSubtype(
-                "es_US", "colemak", null);
-        ZZ_AZERTY = AdditionalSubtypeUtils.createAdditionalSubtype(
-                SubtypeLocaleUtils.NO_LANGUAGE, "azerty", null);
-        ZZ_PC = AdditionalSubtypeUtils.createAdditionalSubtype(
-                SubtypeLocaleUtils.NO_LANGUAGE, "pcqwerty", null);
+        DE_QWERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                Locale.GERMAN.toString(), "qwerty");
+        FR_QWERTZ = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                Locale.FRENCH.toString(), "qwertz");
+        EN_US_AZERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                Locale.US.toString(), "azerty");
+        EN_UK_DVORAK = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                Locale.UK.toString(), "dvorak");
+        ES_US_COLEMAK = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                "es_US", "colemak");
+        ZZ_AZERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                SubtypeLocaleUtils.NO_LANGUAGE, "azerty");
+        ZZ_PC = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                SubtypeLocaleUtils.NO_LANGUAGE, "pcqwerty");
     }
 
     public void testAllFullDisplayNameForSpacebar() {
diff --git a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
index 8e409ab9916a1b4e9c495e7b01ad229fd4cbabf2..ce3df7dd624d06129f66ebd57c6797b7e1767817 100644
--- a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
@@ -87,20 +87,20 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase {
                 "de_CH", "swiss");
         ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
                 SubtypeLocaleUtils.NO_LANGUAGE, "qwerty");
-        DE_QWERTY = AdditionalSubtypeUtils.createAdditionalSubtype(
-                Locale.GERMAN.toString(), "qwerty", null);
-        FR_QWERTZ = AdditionalSubtypeUtils.createAdditionalSubtype(
-                Locale.FRENCH.toString(), "qwertz", null);
-        EN_US_AZERTY = AdditionalSubtypeUtils.createAdditionalSubtype(
-                Locale.US.toString(), "azerty", null);
-        EN_UK_DVORAK = AdditionalSubtypeUtils.createAdditionalSubtype(
-                Locale.UK.toString(), "dvorak", null);
-        ES_US_COLEMAK = AdditionalSubtypeUtils.createAdditionalSubtype(
-                "es_US", "colemak", null);
-        ZZ_AZERTY = AdditionalSubtypeUtils.createAdditionalSubtype(
-                SubtypeLocaleUtils.NO_LANGUAGE, "azerty", null);
-        ZZ_PC = AdditionalSubtypeUtils.createAdditionalSubtype(
-                SubtypeLocaleUtils.NO_LANGUAGE, "pcqwerty", null);
+        DE_QWERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                Locale.GERMAN.toString(), "qwerty");
+        FR_QWERTZ = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                Locale.FRENCH.toString(), "qwertz");
+        EN_US_AZERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                Locale.US.toString(), "azerty");
+        EN_UK_DVORAK = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                Locale.UK.toString(), "dvorak");
+        ES_US_COLEMAK = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                "es_US", "colemak");
+        ZZ_AZERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                SubtypeLocaleUtils.NO_LANGUAGE, "azerty");
+        ZZ_PC = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
+                SubtypeLocaleUtils.NO_LANGUAGE, "pcqwerty");
     }
 
     public void testAllFullDisplayName() {