Merge "UpdateHandler to support v202 dicts."
diff --git a/common/src/com/android/inputmethod/latin/common/LocaleUtils.java b/common/src/com/android/inputmethod/latin/common/LocaleUtils.java
index 14b3d22..7f2333b 100644
--- a/common/src/com/android/inputmethod/latin/common/LocaleUtils.java
+++ b/common/src/com/android/inputmethod/latin/common/LocaleUtils.java
@@ -17,8 +17,12 @@
 package com.android.inputmethod.latin.common;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Locale;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
 /**
  * A class to help with handling Locales in string form.
  *
@@ -160,26 +164,49 @@
 
     /**
      * Creates a locale from a string specification.
+     * @param localeString a string specification of a locale, in a format of "ll_cc_variant" where
+     * "ll" is a language code, "cc" is a country code.
      */
-    public static Locale constructLocaleFromString(final String localeStr) {
-        if (localeStr == null)
+    @Nullable
+    public static Locale constructLocaleFromString(@Nullable final String localeString) {
+        if (localeString == null) {
             return null;
-        synchronized (sLocaleCache) {
-            if (sLocaleCache.containsKey(localeStr))
-                return sLocaleCache.get(localeStr);
-            Locale retval = null;
-            String[] localeParams = localeStr.split("_", 3);
-            if (localeParams.length == 1) {
-                retval = new Locale(localeParams[0]);
-            } else if (localeParams.length == 2) {
-                retval = new Locale(localeParams[0], localeParams[1]);
-            } else if (localeParams.length == 3) {
-                retval = new Locale(localeParams[0], localeParams[1], localeParams[2]);
-            }
-            if (retval != null) {
-                sLocaleCache.put(localeStr, retval);
-            }
-            return retval;
         }
+        synchronized (sLocaleCache) {
+            if (sLocaleCache.containsKey(localeString)) {
+                return sLocaleCache.get(localeString);
+            }
+            final String[] elements = localeString.split("_", 3);
+            final Locale locale;
+            if (elements.length == 1) {
+                locale = new Locale(elements[0] /* language */);
+            } else if (elements.length == 2) {
+                locale = new Locale(elements[0] /* language */, elements[1] /* country */);
+            } else { // localeParams.length == 3
+                locale = new Locale(elements[0] /* language */, elements[1] /* country */,
+                        elements[2] /* variant */);
+            }
+            sLocaleCache.put(localeString, locale);
+            return locale;
+        }
+    }
+
+    // TODO: Get this information from the framework instead of maintaining here by ourselves.
+    private static final HashSet<String> sRtlLanguageCodes = new HashSet<>();
+    static {
+        // List of known Right-To-Left language codes.
+        sRtlLanguageCodes.add("ar"); // Arabic
+        sRtlLanguageCodes.add("fa"); // Persian
+        sRtlLanguageCodes.add("iw"); // Hebrew
+        sRtlLanguageCodes.add("ku"); // Kurdish
+        sRtlLanguageCodes.add("ps"); // Pashto
+        sRtlLanguageCodes.add("sd"); // Sindhi
+        sRtlLanguageCodes.add("ug"); // Uyghur
+        sRtlLanguageCodes.add("ur"); // Urdu
+        sRtlLanguageCodes.add("yi"); // Yiddish
+    }
+
+    public static boolean isRtlLanguage(@Nonnull final Locale locale) {
+        return sRtlLanguageCodes.contains(locale.getLanguage());
     }
 }
diff --git a/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java b/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java
index f806256..4367568 100644
--- a/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java
+++ b/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java
@@ -48,4 +48,10 @@
      * When {@code true}, personal dictionary sync feature is ready to be enabled.
      */
     public static final boolean ENABLE_PERSONAL_DICTIONARY_SYNC = ENABLE_ACCOUNT_SIGN_IN && false;
+
+    /**
+     * When {@code true}, the IME maintains per account {@link UserHistoryDictionary}.
+     */
+    public static final boolean ENABLE_PER_ACCOUNT_USER_HISTORY_DICTIONARY =
+            ENABLE_ACCOUNT_SIGN_IN && false;
 }
diff --git a/java/res/values-eu-rES/strings-emoji-descriptions.xml b/java/res/values-eu-rES/strings-emoji-descriptions.xml
index 2faec96..c774ae1 100644
--- a/java/res/values-eu-rES/strings-emoji-descriptions.xml
+++ b/java/res/values-eu-rES/strings-emoji-descriptions.xml
@@ -846,6 +846,6 @@
     <string name="spoken_emoji_1F6C1" msgid="2845056048320031158">"Bainuontzia"</string>
     <string name="spoken_emoji_1F6C2" msgid="8117262514698011877">"Pasaporte-kontrola"</string>
     <string name="spoken_emoji_1F6C3" msgid="1176342001834630675">"Aduana"</string>
-    <string name="spoken_emoji_1F6C4" msgid="1477622834179978886">"Maleta-erreklamazioa"</string>
+    <string name="spoken_emoji_1F6C4" msgid="1477622834179978886">"Ekipaje-erreklamazioa"</string>
     <string name="spoken_emoji_1F6C5" msgid="2495834050856617451">"Ahaztutako maletak"</string>
 </resources>
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index 51f37fd..b105138 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -294,7 +294,7 @@
                     : subtype;
             mParams.mSubtype = keyboardSubtype;
             mParams.mKeyboardLayoutSetName = KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX
-                    + SubtypeLocaleUtils.getKeyboardLayoutSetName(keyboardSubtype);
+                    + keyboardSubtype.getKeyboardLayoutSetName();
             return this;
         }
 
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index c739bf3..51f89c1 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -36,7 +36,6 @@
 import com.android.inputmethod.latin.common.Constants;
 import com.android.inputmethod.latin.common.StringUtils;
 import com.android.inputmethod.latin.utils.ResourceUtils;
-import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
 import com.android.inputmethod.latin.utils.XmlParseUtils;
 import com.android.inputmethod.latin.utils.XmlParseUtils.ParseException;
 
@@ -648,7 +647,7 @@
         try {
             final boolean keyboardLayoutSetMatched = matchString(caseAttr,
                     R.styleable.Keyboard_Case_keyboardLayoutSet,
-                    SubtypeLocaleUtils.getKeyboardLayoutSetName(id.mSubtype));
+                    id.mSubtype.getKeyboardLayoutSetName());
             final boolean keyboardLayoutSetElementMatched = matchTypedValue(caseAttr,
                     R.styleable.Keyboard_Case_keyboardLayoutSetElement, id.mElementId,
                     KeyboardId.elementIdToName(id.mElementId));
diff --git a/java/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelper.java b/java/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelper.java
index 2a70ef5..8ed8010 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelper.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelper.java
@@ -52,7 +52,7 @@
             return FORMAT_TYPE_MULTIPLE;
         }
         final String keyboardLanguage = locales[0].getLanguage();
-        final String keyboardLayout = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
+        final String keyboardLayout = subtype.getKeyboardLayoutSetName();
         int sameLanguageAndLayoutCount = 0;
         for (final InputMethodSubtype ims : mEnabledSubtypes) {
             final String language = SubtypeLocaleUtils.getSubtypeLocale(ims).getLanguage();
diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java b/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java
index 03f6d60..ea8d4a2 100644
--- a/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java
+++ b/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java
@@ -135,13 +135,18 @@
 
     public boolean isRtlSubtype() {
         // The subtype is considered RTL if the language of the main subtype is RTL.
-        return SubtypeLocaleUtils.isRtlLanguage(mLocales[0]);
+        return LocaleUtils.isRtlLanguage(mLocales[0]);
     }
 
     // TODO: remove this method
     @Nonnull
     public InputMethodSubtype getRawSubtype() { return mSubtype; }
 
+    @Nonnull
+    public String getKeyboardLayoutSetName() {
+        return SubtypeLocaleUtils.getKeyboardLayoutSetName(mSubtype);
+    }
+
     // Dummy no language QWERTY subtype. See {@link R.xml.method}.
     private static final int SUBTYPE_ID_OF_DUMMY_NO_LANGUAGE_SUBTYPE = 0xdde0bfd3;
     private static final String EXTRA_VALUE_OF_DUMMY_NO_LANGUAGE_SUBTYPE =
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
index ac2fc07..8c5eb0a 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
@@ -28,32 +28,45 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * Helps handle and manage personalized dictionaries such as {@link UserHistoryDictionary} and
+ * {@link PersonalizationDictionary}.
+ */
 public class PersonalizationHelper {
     private static final String TAG = PersonalizationHelper.class.getSimpleName();
     private static final boolean DEBUG = false;
+
     private static final ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>>
             sLangUserHistoryDictCache = new ConcurrentHashMap<>();
     private static final ConcurrentHashMap<String, SoftReference<PersonalizationDictionary>>
             sLangPersonalizationDictCache = new ConcurrentHashMap<>();
 
+    @Nonnull
     public static UserHistoryDictionary getUserHistoryDictionary(
-            final Context context, final Locale locale) {
-        final String localeStr = locale.toString();
+            final Context context, final Locale locale, @Nullable final String accountName) {
+        String lookupStr = locale.toString();
+        if (accountName != null) {
+            lookupStr += "." + accountName;
+        }
         synchronized (sLangUserHistoryDictCache) {
-            if (sLangUserHistoryDictCache.containsKey(localeStr)) {
+            if (sLangUserHistoryDictCache.containsKey(lookupStr)) {
                 final SoftReference<UserHistoryDictionary> ref =
-                        sLangUserHistoryDictCache.get(localeStr);
+                        sLangUserHistoryDictCache.get(lookupStr);
                 final UserHistoryDictionary dict = ref == null ? null : ref.get();
                 if (dict != null) {
                     if (DEBUG) {
-                        Log.w(TAG, "Use cached UserHistoryDictionary for " + locale);
+                        Log.d(TAG, "Use cached UserHistoryDictionary for " + locale +
+                                " & account" + accountName);
                     }
                     dict.reloadDictionaryIfRequired();
                     return dict;
                 }
             }
             final UserHistoryDictionary dict = new UserHistoryDictionary(context, locale);
-            sLangUserHistoryDictCache.put(localeStr, new SoftReference<>(dict));
+            sLangUserHistoryDictCache.put(lookupStr, new SoftReference<>(dict));
             return dict;
         }
     }
diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
index 58782c6..946835c 100644
--- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
@@ -17,30 +17,73 @@
 package com.android.inputmethod.latin.personalization;
 
 import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
 
 import com.android.inputmethod.annotations.ExternallyReferenced;
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.Dictionary;
 import com.android.inputmethod.latin.ExpandableBinaryDictionary;
 import com.android.inputmethod.latin.NgramContext;
 import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.define.ProductionFlags;
+import com.android.inputmethod.latin.settings.LocalSettingsConstants;
 import com.android.inputmethod.latin.utils.DistracterFilter;
 
 import java.io.File;
 import java.util.Locale;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 /**
  * Locally gathers stats about the words user types and various other signals like auto-correction
  * cancellation or manual picks. This allows the keyboard to adapt to the typist over time.
  */
 public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBase {
-    /* package */ static final String NAME = UserHistoryDictionary.class.getSimpleName();
+    static final String NAME = UserHistoryDictionary.class.getSimpleName();
 
     // TODO: Make this constructor private
-    /* package */ UserHistoryDictionary(final Context context, final Locale locale) {
-        super(context, getDictName(NAME, locale, null /* dictFile */), locale,
-                Dictionary.TYPE_USER_HISTORY, null /* dictFile */);
+    UserHistoryDictionary(final Context context, final Locale locale) {
+        super(context,
+                getUserHistoryDictName(
+                        NAME,
+                        locale,
+                        null /* dictFile */,
+                        context),
+                locale,
+                Dictionary.TYPE_USER_HISTORY,
+                null /* dictFile */);
+    }
+
+    /**
+     * @returns the name of the {@link UserHistoryDictionary}.
+     */
+    @UsedForTesting
+    static String getUserHistoryDictName(final String name, final Locale locale,
+            @Nullable final File dictFile, final Context context) {
+        if (!ProductionFlags.ENABLE_PER_ACCOUNT_USER_HISTORY_DICTIONARY) {
+            return getDictName(name, locale, dictFile);
+        }
+        return getUserHistoryDictNamePerAccount(name, locale, dictFile, context);
+    }
+
+    /**
+     * Uses the currently signed in account to determine the dictionary name.
+     */
+    private static String getUserHistoryDictNamePerAccount(final String name, final Locale locale,
+            @Nullable final File dictFile, final Context context) {
+        if (dictFile != null) {
+            return dictFile.getName();
+        }
+        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+        final String account = prefs.getString(LocalSettingsConstants.PREF_ACCOUNT_NAME,
+                null /* default */);
+        String dictName = name + "." + locale.toString();
+        if (account != null) {
+            dictName += "." + account;
+        }
+        return dictName;
     }
 
     // Note: This method is called by {@link DictionaryFacilitator} using Java reflection.
@@ -48,7 +91,14 @@
     @ExternallyReferenced
     public static UserHistoryDictionary getDictionary(final Context context, final Locale locale,
             final File dictFile, final String dictNamePrefix) {
-        return PersonalizationHelper.getUserHistoryDictionary(context, locale);
+        final String account;
+        if (ProductionFlags.ENABLE_PER_ACCOUNT_USER_HISTORY_DICTIONARY) {
+            account = PreferenceManager.getDefaultSharedPreferences(context)
+                    .getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, null /* default */);
+        } else {
+            account = null;
+        }
+        return PersonalizationHelper.getUserHistoryDictionary(context, locale, account);
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java b/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java
index 01398f4..b749aa5 100644
--- a/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java
+++ b/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java
@@ -346,8 +346,10 @@
             super(context, android.R.layout.simple_spinner_item);
             setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
 
+            final String[] predefinedKeyboardLayoutSet = context.getResources().getStringArray(
+                    R.array.predefined_layouts);
             // TODO: Should filter out already existing combinations of locale and layout.
-            for (final String layout : SubtypeLocaleUtils.getPredefinedKeyboardLayoutSet()) {
+            for (final String layout : predefinedKeyboardLayoutSet) {
                 // This is a dummy subtype with NO_LANGUAGE, only for display.
                 final InputMethodSubtype subtype =
                         AdditionalSubtypeUtils.createDummyAdditionalSubtype(
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
index 27a0f62..7991a24 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
@@ -50,10 +50,10 @@
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.SuggestedWords;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.common.LocaleUtils;
 import com.android.inputmethod.latin.settings.Settings;
 import com.android.inputmethod.latin.settings.SettingsValues;
 import com.android.inputmethod.latin.utils.ResourceUtils;
-import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
 import com.android.inputmethod.latin.utils.ViewLayoutUtils;
 
 import java.util.ArrayList;
@@ -570,8 +570,7 @@
             final boolean isRtlLanguage = (ViewCompat.getLayoutDirection(addToDictionaryStrip)
                     == ViewCompat.LAYOUT_DIRECTION_RTL);
             final String arrow = isRtlLanguage ? RIGHTWARDS_ARROW : LEFTWARDS_ARROW;
-            final boolean isRtlSystem = SubtypeLocaleUtils.isRtlLanguage(
-                    res.getConfiguration().locale);
+            final boolean isRtlSystem = LocaleUtils.isRtlLanguage(res.getConfiguration().locale);
             final CharSequence hint = res.getText(R.string.hint_add_to_dictionary);
             hintText = (isRtlLanguage == isRtlSystem) ? (arrow + hint) : (hint + arrow);
             hintWidth = width - wordWidth;
diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
index b36168b..013f024 100644
--- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
@@ -27,11 +27,9 @@
 import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.RichInputMethodSubtype;
 import com.android.inputmethod.latin.common.LocaleUtils;
 import com.android.inputmethod.latin.common.StringUtils;
 
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Locale;
 
@@ -56,7 +54,6 @@
     private static volatile boolean sInitialized = false;
     private static final Object sInitializeLock = new Object();
     private static Resources sResources;
-    private static String[] sPredefinedKeyboardLayoutSet;
     // Keyboard layout to its display name map.
     private static final HashMap<String, String> sKeyboardLayoutToDisplayNameMap = new HashMap<>();
     // Keyboard layout to subtype name resource id map.
@@ -103,7 +100,6 @@
         sResources = res;
 
         final String[] predefinedLayoutSet = res.getStringArray(R.array.predefined_layouts);
-        sPredefinedKeyboardLayoutSet = predefinedLayoutSet;
         final String[] layoutDisplayNames = res.getStringArray(
                 R.array.predefined_layout_display_names);
         for (int i = 0; i < predefinedLayoutSet.length; i++) {
@@ -152,10 +148,6 @@
         }
     }
 
-    public static String[] getPredefinedKeyboardLayoutSet() {
-        return sPredefinedKeyboardLayoutSet;
-    }
-
     public static boolean isExceptionalLocale(final String localeString) {
         return sExceptionalLocaleToNameIdsMap.containsKey(localeString);
     }
@@ -334,10 +326,6 @@
     }
 
     @Nonnull
-    public static String getKeyboardLayoutSetName(@Nonnull final RichInputMethodSubtype subtype) {
-        return getKeyboardLayoutSetName(subtype.getRawSubtype());
-    }
-
     public static String getKeyboardLayoutSetName(final InputMethodSubtype subtype) {
         String keyboardLayoutSet = subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET);
         if (keyboardLayoutSet == null) {
@@ -357,22 +345,6 @@
         return keyboardLayoutSet;
     }
 
-    // TODO: Get this information from the framework instead of maintaining here by ourselves.
-    // Sorted list of known Right-To-Left language codes.
-    private static final String[] SORTED_RTL_LANGUAGES = {
-        "ar", // Arabic
-        "fa", // Persian
-        "iw", // Hebrew
-    };
-    static {
-        Arrays.sort(SORTED_RTL_LANGUAGES);
-    }
-
-    public static boolean isRtlLanguage(final Locale locale) {
-        final String language = locale.getLanguage();
-        return Arrays.binarySearch(SORTED_RTL_LANGUAGES, language) >= 0;
-    }
-
     public static String getCombiningRulesExtraValue(final InputMethodSubtype subtype) {
         return subtype.getExtraValueOf(COMBINING_RULES);
     }
diff --git a/native/dicttoolkit/NativeFileList.mk b/native/dicttoolkit/NativeFileList.mk
index b39a248..1c004f7 100644
--- a/native/dicttoolkit/NativeFileList.mk
+++ b/native/dicttoolkit/NativeFileList.mk
@@ -24,11 +24,14 @@
         makedict_executor.cpp) \
     $(addprefix offdevice_intermediate_dict/, \
         offdevice_intermediate_dict.cpp) \
-    utils/command_utils.cpp
+    $(addprefix utils/, \
+        command_utils.cpp \
+        utf8_utils.cpp)
 
 LATIN_IME_DICT_TOOLKIT_TEST_FILES := \
     dict_toolkit_defines_test.cpp \
     $(addprefix offdevice_intermediate_dict/, \
         offdevice_intermediate_dict_test.cpp) \
     $(addprefix utils/, \
-        command_utils_test.cpp)
+        command_utils_test.cpp \
+        utf8_utils_test.cpp)
diff --git a/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict.h b/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict.h
index dfdb6a8..13d26ba 100644
--- a/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict.h
+++ b/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict.h
@@ -18,6 +18,7 @@
 #define LATINIME_DICT_TOOLKIT_OFFDEVICE_INTERMEDIATE_DICT_H
 
 #include "dict_toolkit_defines.h"
+#include "offdevice_intermediate_dict/offdevice_intermediate_dict_header.h"
 #include "offdevice_intermediate_dict/offdevice_intermediate_dict_pt_node_array.h"
 #include "suggest/core/dictionary/property/word_property.h"
 #include "utils/int_array_view.h"
@@ -30,13 +31,18 @@
  */
 class OffdeviceIntermediateDict final {
  public:
+    OffdeviceIntermediateDict(const OffdeviceIntermediateDictHeader &header)
+            : mHeader(header), mRootPtNodeArray() {}
+
     bool addWord(const WordProperty &wordProperty);
     // The returned value will be invalid after modifying the dictionary. e.g. calling addWord().
     const WordProperty *getWordProperty(const CodePointArrayView codePoints) const;
+    const OffdeviceIntermediateDictHeader &getHeader() const { return mHeader; }
 
  private:
     DISALLOW_ASSIGNMENT_OPERATOR(OffdeviceIntermediateDict);
 
+    const OffdeviceIntermediateDictHeader mHeader;
     OffdeviceIntermediateDictPtNodeArray mRootPtNodeArray;
 
     bool addWordInner(const CodePointArrayView codePoints, const WordProperty &wordProperty,
diff --git a/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict_header.h b/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict_header.h
new file mode 100644
index 0000000..440627a
--- /dev/null
+++ b/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict_header.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef LATINIME_DICT_TOOLKIT_OFFDEVICE_INTERMEDIATE_DICT_HEADER_H
+#define LATINIME_DICT_TOOLKIT_OFFDEVICE_INTERMEDIATE_DICT_HEADER_H
+
+#include <map>
+#include <vector>
+
+#include "dict_toolkit_defines.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+class OffdeviceIntermediateDictHeader final {
+ public:
+    using AttributeMap = std::map<std::vector<int>, std::vector<int>>;
+
+    OffdeviceIntermediateDictHeader(const AttributeMap &attributesMap)
+            : mAttributeMap(attributesMap) {}
+
+ private:
+    DISALLOW_DEFAULT_CONSTRUCTOR(OffdeviceIntermediateDictHeader);
+    DISALLOW_ASSIGNMENT_OPERATOR(OffdeviceIntermediateDictHeader);
+
+    const AttributeMap mAttributeMap;
+};
+
+} // namespace dicttoolkit
+} // namespace latinime
+#endif // LATINIME_DICT_TOOLKIT_OFFDEVICE_INTERMEDIATE_DICT_HEADER_H
diff --git a/native/dicttoolkit/src/utils/utf8_utils.cpp b/native/dicttoolkit/src/utils/utf8_utils.cpp
new file mode 100644
index 0000000..0f349f5
--- /dev/null
+++ b/native/dicttoolkit/src/utils/utf8_utils.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#include "utils/utf8_utils.h"
+
+#include "utils/char_utils.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+const size_t Utf8Utils::MAX_SEQUENCE_SIZE_FOR_A_CODE_POINT = 4;
+const uint8_t Utf8Utils::FIRST_BYTE_MARKER_MASKS[] = {0, 0x80, 0xE0, 0xF0, 0xF8};
+const uint8_t Utf8Utils::FIRST_BYTE_MARKERS[] = {0, 0x00, 0xC0, 0xE0, 0xF0};
+const uint8_t Utf8Utils::FIRST_BYTE_CODE_POINT_BITS_MASKS[] = {0, 0x7F, 0x1F, 0x0F, 0x03};
+const int Utf8Utils::MAX_ENCODED_CODE_POINT_VALUES[] = {-1, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
+
+const uint8_t Utf8Utils::TRAILING_BYTE_CODE_POINT_BITS_MASK = 0x3F;
+const uint8_t Utf8Utils::TRAILING_BYTE_MARKER = 0x80;
+const size_t Utf8Utils::CODE_POINT_BIT_COUNT_IN_TRAILING_BYTE = 6;
+
+/* static */ std::vector<int> Utf8Utils::getCodePoints(const std::string &utf8Str) {
+    std::vector<int> codePoints;
+    int remainingByteCountForCurrentCodePoint = 0;
+    int currentCodePointSequenceSize = 0;
+    int codePoint = 0;
+    for (const char c : utf8Str) {
+        if (remainingByteCountForCurrentCodePoint == 0) {
+            currentCodePointSequenceSize = getSequenceSizeByCheckingFirstByte(c);
+            if (currentCodePointSequenceSize <= 0) {
+                AKLOGE("%x is an invalid utf8 first byte value.", c);
+                return std::vector<int>();
+            }
+            remainingByteCountForCurrentCodePoint = currentCodePointSequenceSize;
+            codePoint = maskFirstByte(c, remainingByteCountForCurrentCodePoint);
+        } else {
+            codePoint <<= CODE_POINT_BIT_COUNT_IN_TRAILING_BYTE;
+            codePoint += maskTrailingByte(c);
+        }
+        remainingByteCountForCurrentCodePoint--;
+        if (remainingByteCountForCurrentCodePoint == 0) {
+            if (codePoint <= MAX_ENCODED_CODE_POINT_VALUES[currentCodePointSequenceSize - 1]) {
+                AKLOGE("%d bytes encode for codePoint(%x) is a redundant UTF-8 sequence.",
+                        currentCodePointSequenceSize,  codePoint);
+                return std::vector<int>();
+            }
+            codePoints.push_back(codePoint);
+        }
+    }
+    return codePoints;
+}
+
+/* static */ int Utf8Utils::getSequenceSizeByCheckingFirstByte(const uint8_t firstByte) {
+    for (size_t i = 1; i <= MAX_SEQUENCE_SIZE_FOR_A_CODE_POINT; ++i) {
+        if ((firstByte & FIRST_BYTE_MARKER_MASKS[i]) == FIRST_BYTE_MARKERS[i]) {
+            return i;
+        }
+    }
+    // Not a valid utf8 char first byte.
+    return -1;
+}
+
+/* static */ AK_FORCE_INLINE int Utf8Utils::maskFirstByte(const uint8_t firstByte,
+        const int sequenceSize) {
+    return firstByte & FIRST_BYTE_CODE_POINT_BITS_MASKS[sequenceSize];
+}
+
+/* static */ AK_FORCE_INLINE int Utf8Utils::maskTrailingByte(const uint8_t secondOrLaterByte) {
+    return secondOrLaterByte & TRAILING_BYTE_CODE_POINT_BITS_MASK;
+}
+
+/* static */ std::string Utf8Utils::getUtf8String(const CodePointArrayView codePoints) {
+    std::string utf8String;
+    for (const int codePoint : codePoints) {
+        const int sequenceSize = getSequenceSizeToEncodeCodePoint(codePoint);
+        if (sequenceSize <= 0) {
+            AKLOGE("Cannot encode code point (%d).", codePoint);
+            return std::string();
+        }
+        const int trailingByteCount = sequenceSize - 1;
+        // Output first byte.
+        const int value = codePoint >> (trailingByteCount * CODE_POINT_BIT_COUNT_IN_TRAILING_BYTE);
+        utf8String.push_back(static_cast<char>(value | FIRST_BYTE_MARKERS[sequenceSize]));
+        // Output second and later bytes.
+        for (int i = 1; i < sequenceSize; ++i) {
+            const int shiftAmount = (trailingByteCount - i) * CODE_POINT_BIT_COUNT_IN_TRAILING_BYTE;
+            const int value = (codePoint >> shiftAmount) & TRAILING_BYTE_CODE_POINT_BITS_MASK;
+            utf8String.push_back(static_cast<char>(value | TRAILING_BYTE_MARKER));
+        }
+    }
+    return utf8String;
+}
+
+/* static */ int Utf8Utils::getSequenceSizeToEncodeCodePoint(const int codePoint) {
+    if (codePoint < 0) {
+        return -1;
+    }
+    for (size_t i = 1; i <= MAX_SEQUENCE_SIZE_FOR_A_CODE_POINT; ++i) {
+        if (codePoint <= MAX_ENCODED_CODE_POINT_VALUES[i]) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/src/utils/utf8_utils.h b/native/dicttoolkit/src/utils/utf8_utils.h
new file mode 100644
index 0000000..35818e5
--- /dev/null
+++ b/native/dicttoolkit/src/utils/utf8_utils.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#ifndef LATINIME_DICT_TOOLKIT_UTF8_UTILS_H
+#define LATINIME_DICT_TOOLKIT_UTF8_UTILS_H
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include "dict_toolkit_defines.h"
+#include "utils/int_array_view.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+class Utf8Utils {
+public:
+    static std::vector<int> getCodePoints(const std::string &utf8Str);
+    static std::string getUtf8String(const CodePointArrayView codePoints);
+
+private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(Utf8Utils);
+
+    // Values indexed by sequence size.
+    static const size_t MAX_SEQUENCE_SIZE_FOR_A_CODE_POINT;
+    static const uint8_t FIRST_BYTE_MARKER_MASKS[];
+    static const uint8_t FIRST_BYTE_MARKERS[];
+    static const uint8_t FIRST_BYTE_CODE_POINT_BITS_MASKS[];
+    static const int MAX_ENCODED_CODE_POINT_VALUES[];
+
+    static const uint8_t TRAILING_BYTE_CODE_POINT_BITS_MASK;
+    static const uint8_t TRAILING_BYTE_MARKER;
+    static const size_t CODE_POINT_BIT_COUNT_IN_TRAILING_BYTE;
+
+    static int getSequenceSizeByCheckingFirstByte(const uint8_t firstByte);
+    static int maskFirstByte(const uint8_t firstByte, const int encodeSize);
+    static int maskTrailingByte(const uint8_t secondOrLaterByte);
+    static int getSequenceSizeToEncodeCodePoint(const int codePoint);
+};
+} // namespace dicttoolkit
+} // namespace latinime
+#endif // LATINIME_DICT_TOOLKIT_UTF8_UTILS_H
diff --git a/native/dicttoolkit/tests/offdevice_intermediate_dict/offdevice_intermediate_dict_test.cpp b/native/dicttoolkit/tests/offdevice_intermediate_dict/offdevice_intermediate_dict_test.cpp
index 3bcb89e..f2e24ab 100644
--- a/native/dicttoolkit/tests/offdevice_intermediate_dict/offdevice_intermediate_dict_test.cpp
+++ b/native/dicttoolkit/tests/offdevice_intermediate_dict/offdevice_intermediate_dict_test.cpp
@@ -41,7 +41,8 @@
 }
 
 TEST(OffdeviceIntermediateDictTest, TestAddWordProperties) {
-    OffdeviceIntermediateDict dict;
+    OffdeviceIntermediateDict dict = OffdeviceIntermediateDict(
+            OffdeviceIntermediateDictHeader(OffdeviceIntermediateDictHeader::AttributeMap()));
     EXPECT_EQ(nullptr, dict.getWordProperty(CodePointArrayView()));
 
     const WordProperty wordProperty0 = getDummpWordProperty(getCodePointVector("abcd"));
diff --git a/native/dicttoolkit/tests/utils/utf8_utils_test.cpp b/native/dicttoolkit/tests/utils/utf8_utils_test.cpp
new file mode 100644
index 0000000..9c59a8b
--- /dev/null
+++ b/native/dicttoolkit/tests/utils/utf8_utils_test.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+#include "utils/utf8_utils.h"
+
+#include <gtest/gtest.h>
+
+#include <vector>
+
+#include "utils/int_array_view.h"
+
+namespace latinime {
+namespace dicttoolkit {
+namespace {
+
+TEST(Utf8UtilsTests, TestGetCodePoints) {
+    {
+        const std::vector<int> codePoints = Utf8Utils::getCodePoints("");
+        EXPECT_EQ(0u, codePoints.size());
+    }
+    {
+        const std::vector<int> codePoints = Utf8Utils::getCodePoints("test");
+        EXPECT_EQ(4u, codePoints.size());
+        EXPECT_EQ('t', codePoints[0]);
+        EXPECT_EQ('e', codePoints[1]);
+        EXPECT_EQ('s', codePoints[2]);
+        EXPECT_EQ('t', codePoints[3]);
+    }
+    {
+        const std::vector<int> codePoints = Utf8Utils::getCodePoints(u8"\u3042a\u03C2\u0410");
+        EXPECT_EQ(4u, codePoints.size());
+        EXPECT_EQ(0x3042, codePoints[0]); // HIRAGANA LETTER A
+        EXPECT_EQ('a', codePoints[1]);
+        EXPECT_EQ(0x03C2, codePoints[2]); // CYRILLIC CAPITAL LETTER A
+        EXPECT_EQ(0x0410, codePoints[3]); // GREEK SMALL LETTER FINAL SIGMA
+    }
+    {
+        const std::vector<int> codePoints = Utf8Utils::getCodePoints(u8"\U0001F36A?\U0001F752");
+        EXPECT_EQ(3u, codePoints.size());
+        EXPECT_EQ(0x1F36A, codePoints[0]); // COOKIE
+        EXPECT_EQ('?', codePoints[1]);
+        EXPECT_EQ(0x1F752, codePoints[2]); // ALCHEMICAL SYMBOL FOR STARRED TRIDENT
+    }
+
+    // Redundant UTF-8 sequences must be rejected.
+    EXPECT_TRUE(Utf8Utils::getCodePoints("\xC0\xAF").empty());
+    EXPECT_TRUE(Utf8Utils::getCodePoints("\xE0\x80\xAF").empty());
+    EXPECT_TRUE(Utf8Utils::getCodePoints("\xF0\x80\x80\xAF").empty());
+}
+
+TEST(Utf8UtilsTests, TestGetUtf8String) {
+    {
+        const std::vector<int> codePoints = {'t', 'e', 's', 't'};
+        EXPECT_EQ("test", Utf8Utils::getUtf8String(CodePointArrayView(codePoints)));
+    }
+    {
+        const std::vector<int> codePoints = {
+                0x00E0 /* LATIN SMALL LETTER A WITH GRAVE */,
+                0x03C2 /* GREEK SMALL LETTER FINAL SIGMA */,
+                0x0430 /* CYRILLIC SMALL LETTER A */,
+                0x3042 /* HIRAGANA LETTER A */,
+                0x1F36A /* COOKIE */,
+                0x1F752 /* ALCHEMICAL SYMBOL FOR STARRED TRIDENT */
+        };
+        EXPECT_EQ(u8"\u00E0\u03C2\u0430\u3042\U0001F36A\U0001F752",
+                Utf8Utils::getUtf8String(CodePointArrayView(codePoints)));
+    }
+}
+
+} // namespace
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
index 6c60fdc..d833b97 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
@@ -26,7 +26,6 @@
 import com.android.inputmethod.latin.common.CodePointUtils;
 import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
 import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
-import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions;
 import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
 import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
 import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
index 813a712..a84df28 100644
--- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.latin.personalization;
 
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
@@ -24,6 +26,7 @@
 import com.android.inputmethod.latin.NgramContext;
 import com.android.inputmethod.latin.NgramContext.WordInfo;
 import com.android.inputmethod.latin.common.FileUtils;
+import com.android.inputmethod.latin.settings.LocalSettingsConstants;
 import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
 import com.android.inputmethod.latin.utils.DistracterFilter;
 
@@ -36,6 +39,8 @@
 import java.util.Random;
 import java.util.concurrent.TimeUnit;
 
+import javax.annotation.Nullable;
+
 /**
  * Unit tests for UserHistoryDictionary
  */
@@ -44,6 +49,7 @@
     private static final String TAG = UserHistoryDictionaryTests.class.getSimpleName();
     private static final int WAIT_FOR_WRITING_FILE_IN_MILLISECONDS = 3000;
     private static final String TEST_LOCALE_PREFIX = "test_";
+    private static final String TEST_ACCOUNT = "account@example.com";
 
     private static final String[] CHARACTERS = {
         "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
@@ -52,15 +58,18 @@
 
     private int mCurrentTime = 0;
 
+    private SharedPreferences mPrefs;
+    private String mLastKnownAccount = null;
+
     private void removeAllTestDictFiles() {
         final Locale dummyLocale = new Locale(TEST_LOCALE_PREFIX);
-        final String dictName = ExpandableBinaryDictionary.getDictName(
-                UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */);
+        final String dictName = UserHistoryDictionary.getUserHistoryDictName(
+                UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */, getContext());
         final File dictFile = ExpandableBinaryDictionary.getDictFile(
                 mContext, dictName, null /* dictFile */);
         final FilenameFilter filenameFilter = new FilenameFilter() {
             @Override
-            public boolean accept(File dir, String filename) {
+            public boolean accept(final File dir, final String filename) {
                 return filename.startsWith(UserHistoryDictionary.NAME + "." + TEST_LOCALE_PREFIX);
             }
         };
@@ -99,6 +108,12 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+
+        mPrefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+        // Keep track of the current account so that we restore it when the test finishes.
+        mLastKnownAccount = mPrefs.getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, null);
+        updateAccountName(TEST_ACCOUNT);
+
         resetCurrentTimeForTestMode();
         removeAllTestDictFiles();
     }
@@ -107,6 +122,10 @@
     protected void tearDown() throws Exception {
         removeAllTestDictFiles();
         stopTestModeInNativeCode();
+
+        // Restore the account that was present before running the test.
+        updateAccountName(mLastKnownAccount);
+
         super.tearDown();
     }
 
@@ -115,6 +134,14 @@
         setCurrentTimeForTestMode(mCurrentTime);
     }
 
+    private void updateAccountName(@Nullable final String accountName) {
+        if (accountName == null) {
+            mPrefs.edit().remove(LocalSettingsConstants.PREF_ACCOUNT_NAME).apply();
+        } else {
+            mPrefs.edit().putString(LocalSettingsConstants.PREF_ACCOUNT_NAME, accountName).apply();
+        }
+    }
+
     private void forcePassingShortTime() {
         // 3 days.
         final int timeToElapse = (int)TimeUnit.DAYS.toSeconds(3);
@@ -142,7 +169,7 @@
      */
     private static String generateWord(final int value) {
         final int lengthOfChars = CHARACTERS.length;
-        StringBuilder builder = new StringBuilder();
+        final StringBuilder builder = new StringBuilder();
         long lvalue = Math.abs((long)value);
         while (lvalue > 0) {
             builder.append(CHARACTERS[(int)(lvalue % lengthOfChars)]);
@@ -162,7 +189,7 @@
     private static void addToDict(final UserHistoryDictionary dict, final List<String> words,
             final int timestamp) {
         NgramContext ngramContext = NgramContext.EMPTY_PREV_WORDS_INFO;
-        for (String word : words) {
+        for (final String word : words) {
             UserHistoryDictionary.addToDictionary(dict, ngramContext, word, true, timestamp,
                     DistracterFilter.EMPTY_DISTRACTER_FILTER);
             ngramContext = ngramContext.getNextNgramContext(new WordInfo(word));
@@ -204,12 +231,12 @@
         Log.d(TAG, "This test can be used for profiling.");
         Log.d(TAG, "Usage: please set UserHistoryDictionary.PROFILE_SAVE_RESTORE to true.");
         final Locale dummyLocale = getDummyLocale("random_words");
-        final String dictName = ExpandableBinaryDictionary.getDictName(
-                UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */);
+        final String dictName = UserHistoryDictionary.getUserHistoryDictName(
+                UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */, getContext());
         final File dictFile = ExpandableBinaryDictionary.getDictFile(
                 mContext, dictName, null /* dictFile */);
         final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary(
-                getContext(), dummyLocale);
+                getContext(), dummyLocale, TEST_ACCOUNT);
 
         final int numberOfWords = 1000;
         final Random random = new Random(123456);
@@ -232,12 +259,12 @@
             // Create filename suffixes for this test.
             for (int i = 0; i < numberOfLanguages; i++) {
                 final Locale dummyLocale = getDummyLocale("switching_languages" + i);
-                final String dictName = ExpandableBinaryDictionary.getDictName(
-                        UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */);
+                final String dictName = UserHistoryDictionary.getUserHistoryDictName(
+                        UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */, getContext());
                 dictFiles[i] = ExpandableBinaryDictionary.getDictFile(
                         mContext, dictName, null /* dictFile */);
                 dicts[i] = PersonalizationHelper.getUserHistoryDictionary(getContext(),
-                        dummyLocale);
+                        dummyLocale, TEST_ACCOUNT);
                 clearHistory(dicts[i]);
             }
 
@@ -262,14 +289,14 @@
 
     public void testAddManyWords() {
         final Locale dummyLocale = getDummyLocale("many_random_words");
-        final String dictName = ExpandableBinaryDictionary.getDictName(
-                UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */);
+        final String dictName = UserHistoryDictionary.getUserHistoryDictName(
+                UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */, getContext());
         final File dictFile = ExpandableBinaryDictionary.getDictFile(
                 mContext, dictName, null /* dictFile */);
         final int numberOfWords = 10000;
         final Random random = new Random(123456);
         final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary(
-                getContext(), dummyLocale);
+                getContext(), dummyLocale, TEST_ACCOUNT);
         clearHistory(dict);
         try {
             addAndWriteRandomWords(dict, numberOfWords, random, true /* checksContents */);
@@ -281,7 +308,7 @@
     public void testDecaying() {
         final Locale dummyLocale = getDummyLocale("decaying");
         final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary(
-                getContext(), dummyLocale);
+                getContext(), dummyLocale, TEST_ACCOUNT);
         final int numberOfWords = 5000;
         final Random random = new Random(123456);
         resetCurrentTimeForTestMode();
@@ -309,4 +336,9 @@
             assertFalse(dict.isInDictionary(word));
         }
     }
-}
+
+    public void testRandomWords_NullAccount() {
+        updateAccountName(null);
+        testRandomWords();
+    }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
index 54f478f..03dcdfc 100644
--- a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
@@ -434,8 +434,8 @@
     // locale layout         |  display name
     // ------ -------------- - ----------------------
     //  sr    south_slavic   F  Српски
-    //  sr_ZZ serbian_qwertz F  српски (латиница)
-    //  sr_ZZ qwerty         T  српски (QWERTY)
+    //  sr_ZZ serbian_qwertz F  Српски (латиница)
+    //  sr_ZZ qwerty         T  Српски (QWERTY)
 
     public void testSerbianLatinSubtypesInSerbianSystemLocale() {
         final RunInLocale<Void> tests = new RunInLocale<Void>() {
@@ -445,12 +445,10 @@
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR));
                 // These are preliminary subtypes and may not exist.
                 if (SR_LATN != null) {
-                    // TODO: Uncommented because of the current translation of these strings
-                    // in Seriban are described in Latin script.
-//                    assertEquals("sr_ZZ", "српски (латиница)",
-//                            SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN));
-//                    assertEquals("sr_ZZ", "српски (QWERTY)",
-//                            SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN_QWERTY));
+                    assertEquals("sr_ZZ", "Српски (латиница)",
+                            SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN));
+                    assertEquals("sr_ZZ", "Српски (QWERTY)",
+                            SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN_QWERTY));
                 }
                 return null;
             }