Merge "Merge dimens.xml to config.xml"
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 7107076..0560542 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -53,6 +53,7 @@
 
     /** Whether to print debug output to log */
     private static boolean DEBUG = false;
+    private static final boolean DBG_STRESS_TEST = false;
 
     private static final int TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS = 100;
 
@@ -496,6 +497,16 @@
                     + mFilenameDictionaryUpdateController.mLastUpdateRequestTime + " update="
                     + mFilenameDictionaryUpdateController.mLastUpdateTime);
         }
+        if (DBG_STRESS_TEST) {
+            // Test if this class does not cause problems when it takes long time to load binary
+            // dictionary.
+            try {
+                Log.w(TAG, "Start stress in loading: " + mFilename);
+                Thread.sleep(15000);
+                Log.w(TAG, "End stress in loading");
+            } catch (InterruptedException e) {
+            }
+        }
 
         final File file = new File(mContext.getFilesDir(), mFilename
                 + getFileNameExtentionToOpenDict());
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 87eed3b..16d860f 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -649,14 +649,13 @@
 
         final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
 
-        mUserHistoryDictionary = PersonalizationHelper.getUserHistoryDictionary(
-                this, localeStr, prefs);
+        mUserHistoryDictionary = PersonalizationHelper.getUserHistoryDictionary(this, localeStr);
         newSuggest.setUserHistoryDictionary(mUserHistoryDictionary);
-        mPersonalizationDictionary = PersonalizationHelper
-                .getPersonalizationDictionary(this, localeStr, prefs);
+        mPersonalizationDictionary =
+                PersonalizationHelper.getPersonalizationDictionary(this, localeStr);
         newSuggest.setPersonalizationDictionary(mPersonalizationDictionary);
-        mPersonalizationPredictionDictionary = PersonalizationHelper
-                .getPersonalizationPredictionDictionary(this, localeStr, prefs);
+        mPersonalizationPredictionDictionary =
+                PersonalizationHelper.getPersonalizationPredictionDictionary(this, localeStr);
         newSuggest.setPersonalizationPredictionDictionary(mPersonalizationPredictionDictionary);
 
         final Suggest oldSuggest = mSuggest;
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index f85431e..555c71b 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -379,6 +379,15 @@
             mHeaderSize = headerSize;
             mDictionaryOptions = dictionaryOptions;
             mFormatOptions = formatOptions;
+            if (null == getLocaleString()) {
+                throw new RuntimeException("Cannot create a FileHeader without a locale");
+            }
+            if (null == getVersion()) {
+                throw new RuntimeException("Cannot create a FileHeader without a version");
+            }
+            if (null == getId()) {
+                throw new RuntimeException("Cannot create a FileHeader without an ID");
+            }
         }
 
         // Helper method to get the locale as a String
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java
index 883709f..91d9cf3 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java
@@ -140,8 +140,10 @@
 
     @Override
     public void deleteWord(final String word) throws IOException, UnsupportedFormatException {
-        if (mDictBuffer == null) openDictBuffer();
-        readHeader();
+        if (mDictBuffer == null) {
+            openDictBuffer();
+            readHeader();
+        }
         final int wordPos = getTerminalPosition(word);
         if (wordPos != FormatSpec.NOT_VALID_WORD) {
             mDictBuffer.position(wordPos);
diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
index 5edac23..dbf6d00 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
@@ -17,7 +17,6 @@
 package com.android.inputmethod.latin.personalization;
 
 import android.content.Context;
-import android.content.SharedPreferences;
 import android.util.Log;
 
 import com.android.inputmethod.annotations.UsedForTesting;
@@ -25,11 +24,9 @@
 import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.Dictionary;
 import com.android.inputmethod.latin.ExpandableBinaryDictionary;
-import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.makedict.DictDecoder;
 import com.android.inputmethod.latin.makedict.FormatSpec;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
-import com.android.inputmethod.latin.settings.Settings;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils;
 import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.OnAddWordListener;
@@ -47,9 +44,7 @@
 public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableBinaryDictionary {
     private static final String TAG = DecayingExpandableBinaryDictionaryBase.class.getSimpleName();
     public static final boolean DBG_SAVE_RESTORE = false;
-    private static final boolean DBG_STRESS_TEST = false;
     private static final boolean DBG_DUMP_ON_CLOSE = false;
-    private static final boolean PROFILE_SAVE_RESTORE = LatinImeLogger.sDBG;
 
     /** Any pair being typed or picked */
     public static final int FREQUENCY_FOR_TYPED = 2;
@@ -64,8 +59,6 @@
 
     private final String mFileName;
 
-    private final SharedPreferences mPrefs;
-
     private final ArrayList<PersonalizationDictionaryUpdateSession> mSessions =
             CollectionUtils.newArrayList();
 
@@ -73,12 +66,10 @@
     @UsedForTesting boolean mIsTest = false;
 
     /* package */ DecayingExpandableBinaryDictionaryBase(final Context context,
-            final String locale, final SharedPreferences sp, final String dictionaryType,
-            final String fileName) {
+            final String locale, final String dictionaryType, final String fileName) {
         super(context, fileName, dictionaryType, true);
         mLocale = locale;
         mFileName = fileName;
-        mPrefs = sp;
         if (mLocale != null && mLocale.length() > 1) {
             reloadDictionaryIfRequired();
         }
@@ -92,7 +83,6 @@
         // Flush pending writes.
         // TODO: Remove after this class become to use a dynamic binary dictionary.
         asyncFlashAllBinaryDictionary();
-        Settings.writeLastUserHistoryWriteTime(mPrefs, mLocale);
     }
 
     @Override
@@ -171,67 +161,7 @@
 
     @Override
     protected void loadDictionaryAsync() {
-        final int[] profTotalCount = { 0 };
-        final String locale = getLocale();
-        if (DBG_STRESS_TEST) {
-            try {
-                Log.w(TAG, "Start stress in loading: " + locale);
-                Thread.sleep(15000);
-                Log.w(TAG, "End stress in loading");
-            } catch (InterruptedException e) {
-            }
-        }
-        final long last = Settings.readLastUserHistoryWriteTime(mPrefs, locale);
-        final long now = System.currentTimeMillis();
-        final ExpandableBinaryDictionary dictionary = this;
-        final OnAddWordListener listener = new OnAddWordListener() {
-            @Override
-            public void setUnigram(final String word, final String shortcutTarget,
-                    final int frequency, final int shortcutFreq) {
-                if (DBG_SAVE_RESTORE) {
-                    Log.d(TAG, "load unigram: " + word + "," + frequency);
-                }
-                addWord(word, shortcutTarget, frequency, shortcutFreq, false /* isNotAWord */);
-                ++profTotalCount[0];
-            }
-
-            @Override
-            public void setBigram(final String word0, final String word1, final int frequency) {
-                if (word0.length() < Constants.DICTIONARY_MAX_WORD_LENGTH
-                        && word1.length() < Constants.DICTIONARY_MAX_WORD_LENGTH) {
-                    if (DBG_SAVE_RESTORE) {
-                        Log.d(TAG, "load bigram: " + word0 + "," + word1 + "," + frequency);
-                    }
-                    ++profTotalCount[0];
-                    addBigram(word0, word1, frequency, last);
-                }
-            }
-        };
-
-        // Load the dictionary from binary file
-        final File dictFile = new File(mContext.getFilesDir(), mFileName);
-        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(dictFile,
-                DictDecoder.USE_BYTEARRAY);
-        if (dictDecoder == null) {
-            // This is an expected condition: we don't have a user history dictionary for this
-            // language yet. It will be created sometime later.
-            return;
-        }
-
-        try {
-            dictDecoder.openDictBuffer();
-            UserHistoryDictIOUtils.readDictionaryBinary(dictDecoder, listener);
-        } catch (IOException e) {
-            Log.d(TAG, "IOException on opening a bytebuffer", e);
-        } catch (UnsupportedFormatException e) {
-            Log.d(TAG, "Unsupported format, can't read the dictionary", e);
-        } finally {
-            if (PROFILE_SAVE_RESTORE) {
-                final long diff = System.currentTimeMillis() - now;
-                Log.d(TAG, "PROF: Load UserHistoryDictionary: "
-                        + locale + ", " + diff + "ms. load " + profTotalCount[0] + "entries.");
-            }
-        }
+        // Never loaded to memory in Java side.
     }
 
     protected String getLocale() {
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
index f257165..67015f4 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
@@ -36,8 +36,7 @@
     /** Locale for which this user history dictionary is storing words */
     private final String mLocale;
 
-    public PersonalizationDictionary(final Context context, final String locale,
-            final SharedPreferences prefs) {
+    public PersonalizationDictionary(final Context context, final String locale) {
         // TODO: Make isUpdatable true.
         super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_PERSONALIZATION,
                 false /* isUpdatable */);
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
index 221ddee..a47cc4d 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
@@ -41,7 +41,7 @@
                             CollectionUtils.newConcurrentHashMap();
 
     public static UserHistoryDictionary getUserHistoryDictionary(
-            final Context context, final String locale, final SharedPreferences sp) {
+            final Context context, final String locale) {
         synchronized (sLangUserHistoryDictCache) {
             if (sLangUserHistoryDictCache.containsKey(locale)) {
                 final SoftReference<UserHistoryDictionary> ref =
@@ -55,7 +55,7 @@
                     return dict;
                 }
             }
-            final UserHistoryDictionary dict = new UserHistoryDictionary(context, locale, sp);
+            final UserHistoryDictionary dict = new UserHistoryDictionary(context, locale);
             sLangUserHistoryDictCache.put(locale, new SoftReference<UserHistoryDictionary>(dict));
             return dict;
         }
@@ -76,17 +76,14 @@
     public static void registerPersonalizationDictionaryUpdateSession(final Context context,
             final PersonalizationDictionaryUpdateSession session, String locale) {
         final PersonalizationPredictionDictionary predictionDictionary =
-                getPersonalizationPredictionDictionary(context, locale,
-                        PreferenceManager.getDefaultSharedPreferences(context));
+                getPersonalizationPredictionDictionary(context, locale);
         predictionDictionary.registerUpdateSession(session);
-        final PersonalizationDictionary dictionary =
-                getPersonalizationDictionary(context, locale,
-                        PreferenceManager.getDefaultSharedPreferences(context));
+        final PersonalizationDictionary dictionary = getPersonalizationDictionary(context, locale);
         dictionary.registerUpdateSession(session);
     }
 
     public static PersonalizationDictionary getPersonalizationDictionary(
-            final Context context, final String locale, final SharedPreferences sp) {
+            final Context context, final String locale) {
         synchronized (sLangPersonalizationDictCache) {
             if (sLangPersonalizationDictCache.containsKey(locale)) {
                 final SoftReference<PersonalizationDictionary> ref =
@@ -99,8 +96,7 @@
                     return dict;
                 }
             }
-            final PersonalizationDictionary dict =
-                    new PersonalizationDictionary(context, locale, sp);
+            final PersonalizationDictionary dict = new PersonalizationDictionary(context, locale);
             sLangPersonalizationDictCache.put(
                     locale, new SoftReference<PersonalizationDictionary>(dict));
             return dict;
@@ -108,7 +104,7 @@
     }
 
     public static PersonalizationPredictionDictionary getPersonalizationPredictionDictionary(
-            final Context context, final String locale, final SharedPreferences sp) {
+            final Context context, final String locale) {
         synchronized (sLangPersonalizationPredictionDictCache) {
             if (sLangPersonalizationPredictionDictCache.containsKey(locale)) {
                 final SoftReference<PersonalizationPredictionDictionary> ref =
@@ -122,7 +118,7 @@
                 }
             }
             final PersonalizationPredictionDictionary dict =
-                    new PersonalizationPredictionDictionary(context, locale, sp);
+                    new PersonalizationPredictionDictionary(context, locale);
             sLangPersonalizationPredictionDictCache.put(
                     locale, new SoftReference<PersonalizationPredictionDictionary>(dict));
             return dict;
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java
index 4329544..16107e2 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java
@@ -20,14 +20,12 @@
 import com.android.inputmethod.latin.ExpandableBinaryDictionary;
 
 import android.content.Context;
-import android.content.SharedPreferences;
 
 public class PersonalizationPredictionDictionary extends DecayingExpandableBinaryDictionaryBase {
     private static final String NAME = PersonalizationPredictionDictionary.class.getSimpleName();
 
-    /* package */ PersonalizationPredictionDictionary(final Context context, final String locale,
-            final SharedPreferences sp) {
-        super(context, locale, sp, Dictionary.TYPE_PERSONALIZATION_PREDICTION_IN_JAVA,
+    /* package */ PersonalizationPredictionDictionary(final Context context, final String locale) {
+        super(context, locale, Dictionary.TYPE_PERSONALIZATION_PREDICTION_IN_JAVA,
                 getDictionaryFileName(locale));
     }
 
diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
index a60226d..95a6fe1 100644
--- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
@@ -20,7 +20,6 @@
 import com.android.inputmethod.latin.ExpandableBinaryDictionary;
 
 import android.content.Context;
-import android.content.SharedPreferences;
 
 /**
  * Locally gathers stats about the words user types and various other signals like auto-correction
@@ -29,9 +28,8 @@
 public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBase {
     /* package for tests */ static final String NAME =
             UserHistoryDictionary.class.getSimpleName();
-    /* package */ UserHistoryDictionary(final Context context, final String locale,
-            final SharedPreferences sp) {
-        super(context, locale, sp, Dictionary.TYPE_USER_HISTORY, getDictionaryFileName(locale));
+    /* package */ UserHistoryDictionary(final Context context, final String locale) {
+        super(context, locale, Dictionary.TYPE_USER_HISTORY, getDictionaryFileName(locale));
     }
 
     private static String getDictionaryFileName(final String locale) {
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index df2c690..f5c1d10 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -53,8 +53,6 @@
     public static final String PREF_AUTO_CORRECTION_THRESHOLD = "auto_correction_threshold";
     public static final String PREF_SHOW_SUGGESTIONS_SETTING = "show_suggestions_setting";
     public static final String PREF_MISC_SETTINGS = "misc_settings";
-    public static final String PREF_LAST_USER_DICTIONARY_WRITE_TIME =
-            "last_user_dictionary_write_time";
     public static final String PREF_ADVANCED_SETTINGS = "pref_advanced_settings";
     public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict";
     public static final String PREF_KEY_USE_DOUBLE_SPACE_PERIOD =
@@ -333,25 +331,6 @@
         return prefs.getBoolean(DebugSettings.PREF_USABILITY_STUDY_MODE, true);
     }
 
-    public static long readLastUserHistoryWriteTime(final SharedPreferences prefs,
-            final String locale) {
-        final String str = prefs.getString(PREF_LAST_USER_DICTIONARY_WRITE_TIME, "");
-        final HashMap<String, Long> map = LocaleUtils.localeAndTimeStrToHashMap(str);
-        if (map.containsKey(locale)) {
-            return map.get(locale);
-        }
-        return 0;
-    }
-
-    public static void writeLastUserHistoryWriteTime(final SharedPreferences prefs,
-            final String locale) {
-        final String oldStr = prefs.getString(PREF_LAST_USER_DICTIONARY_WRITE_TIME, "");
-        final HashMap<String, Long> map = LocaleUtils.localeAndTimeStrToHashMap(oldStr);
-        map.put(locale, System.currentTimeMillis());
-        final String newStr = LocaleUtils.localeAndTimeHashMapToStr(map);
-        prefs.edit().putString(PREF_LAST_USER_DICTIONARY_WRITE_TIME, newStr).apply();
-    }
-
     public static boolean readUseFullscreenMode(final Resources res) {
         return res.getBoolean(R.bool.config_use_fullscreen_mode);
     }
diff --git a/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java
index 22045aa..b4ff849 100644
--- a/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java
@@ -30,9 +30,6 @@
  * dictionary pack.
  */
 public final class LocaleUtils {
-    private static final HashMap<String, Long> EMPTY_LT_HASH_MAP = CollectionUtils.newHashMap();
-    private static final String LOCALE_AND_TIME_STR_SEPARATER = ",";
-
     private LocaleUtils() {
         // Intentional empty constructor for utility class.
     }
@@ -188,38 +185,4 @@
             return retval;
         }
     }
-
-    public static HashMap<String, Long> localeAndTimeStrToHashMap(String str) {
-        if (TextUtils.isEmpty(str)) {
-            return EMPTY_LT_HASH_MAP;
-        }
-        final String[] ss = str.split(LOCALE_AND_TIME_STR_SEPARATER);
-        final int N = ss.length;
-        if (N < 2 || N % 2 != 0) {
-            return EMPTY_LT_HASH_MAP;
-        }
-        final HashMap<String, Long> retval = CollectionUtils.newHashMap();
-        for (int i = 0; i < N / 2; ++i) {
-            final String localeStr = ss[i * 2];
-            final long time = Long.valueOf(ss[i * 2 + 1]);
-            retval.put(localeStr, time);
-        }
-        return retval;
-    }
-
-    public static String localeAndTimeHashMapToStr(HashMap<String, Long> map) {
-        if (map == null || map.isEmpty()) {
-            return "";
-        }
-        final StringBuilder builder = new StringBuilder();
-        for (String localeStr : map.keySet()) {
-            if (builder.length() > 0) {
-                builder.append(LOCALE_AND_TIME_STR_SEPARATER);
-            }
-            final Long time = map.get(localeStr);
-            builder.append(localeStr).append(LOCALE_AND_TIME_STR_SEPARATER);
-            builder.append(String.valueOf(time));
-        }
-        return builder.toString();
-    }
 }
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index fbcd612..5648115 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -341,12 +341,21 @@
 #define INPUTLENGTH_FOR_DEBUG (-1)
 #define MIN_OUTPUT_INDEX_FOR_DEBUG (-1)
 
-#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
-  TypeName(const TypeName&);               \
+#define DISALLOW_DEFAULT_CONSTRUCTOR(TypeName) \
+  TypeName()
+
+#define DISALLOW_COPY_CONSTRUCTOR(TypeName) \
+  TypeName(const TypeName&)
+
+#define DISALLOW_ASSIGNMENT_OPERATOR(TypeName) \
   void operator=(const TypeName&)
 
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+  DISALLOW_COPY_CONSTRUCTOR(TypeName);     \
+  DISALLOW_ASSIGNMENT_OPERATOR(TypeName)
+
 #define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
-  TypeName();                                    \
+  DISALLOW_DEFAULT_CONSTRUCTOR(TypeName);        \
   DISALLOW_COPY_AND_ASSIGN(TypeName)
 
 // Used as a return value for character comparison
diff --git a/native/jni/src/suggest/core/dictionary/bloom_filter.h b/native/jni/src/suggest/core/dictionary/bloom_filter.h
index 5205456..5f97004 100644
--- a/native/jni/src/suggest/core/dictionary/bloom_filter.h
+++ b/native/jni/src/suggest/core/dictionary/bloom_filter.h
@@ -50,6 +50,8 @@
     }
 
  private:
+    DISALLOW_ASSIGNMENT_OPERATOR(BloomFilter);
+
     // Size, in bytes, of the bloom filter index for bigrams
     // 128 gives us 1024 buckets. The probability of false positive is (1 - e ** (-kn/m))**k,
     // where k is the number of hash functions, n the number of bigrams, and m the number of
diff --git a/native/jni/src/suggest/core/dictionary/multi_bigram_map.cpp b/native/jni/src/suggest/core/dictionary/multi_bigram_map.cpp
index b1d2f4b..49d82e6 100644
--- a/native/jni/src/suggest/core/dictionary/multi_bigram_map.cpp
+++ b/native/jni/src/suggest/core/dictionary/multi_bigram_map.cpp
@@ -30,4 +30,75 @@
 // Most common previous word contexts currently have 100 bigrams
 const int MultiBigramMap::BigramMap::DEFAULT_HASH_MAP_SIZE_FOR_EACH_BIGRAM_MAP = 100;
 
+// Look up the bigram probability for the given word pair from the cached bigram maps.
+// Also caches the bigrams if there is space remaining and they have not been cached already.
+int MultiBigramMap::getBigramProbability(
+        const DictionaryStructureWithBufferPolicy *const structurePolicy,
+        const int wordPosition, const int nextWordPosition, const int unigramProbability) {
+    hash_map_compat<int, BigramMap>::const_iterator mapPosition =
+            mBigramMaps.find(wordPosition);
+    if (mapPosition != mBigramMaps.end()) {
+        return mapPosition->second.getBigramProbability(structurePolicy, nextWordPosition,
+                unigramProbability);
+    }
+    if (mBigramMaps.size() < MAX_CACHED_PREV_WORDS_IN_BIGRAM_MAP) {
+        addBigramsForWordPosition(structurePolicy, wordPosition);
+        return mBigramMaps[wordPosition].getBigramProbability(structurePolicy,
+                nextWordPosition, unigramProbability);
+    }
+    return readBigramProbabilityFromBinaryDictionary(structurePolicy, wordPosition,
+            nextWordPosition, unigramProbability);
+}
+
+void MultiBigramMap::BigramMap::init(
+        const DictionaryStructureWithBufferPolicy *const structurePolicy, const int nodePos) {
+    const int bigramsListPos = structurePolicy->getBigramsPositionOfPtNode(nodePos);
+    BinaryDictionaryBigramsIterator bigramsIt(structurePolicy->getBigramsStructurePolicy(),
+            bigramsListPos);
+    while (bigramsIt.hasNext()) {
+        bigramsIt.next();
+        if (bigramsIt.getBigramPos() == NOT_A_DICT_POS) {
+            continue;
+        }
+        mBigramMap[bigramsIt.getBigramPos()] = bigramsIt.getProbability();
+        mBloomFilter.setInFilter(bigramsIt.getBigramPos());
+    }
+}
+
+int MultiBigramMap::BigramMap::getBigramProbability(
+        const DictionaryStructureWithBufferPolicy *const structurePolicy,
+        const int nextWordPosition, const int unigramProbability) const {
+    int bigramProbability = NOT_A_PROBABILITY;
+    if (mBloomFilter.isInFilter(nextWordPosition)) {
+        const hash_map_compat<int, int>::const_iterator bigramProbabilityIt =
+                mBigramMap.find(nextWordPosition);
+        if (bigramProbabilityIt != mBigramMap.end()) {
+            bigramProbability = bigramProbabilityIt->second;
+        }
+    }
+    return structurePolicy->getProbability(unigramProbability, bigramProbability);
+}
+
+void MultiBigramMap::addBigramsForWordPosition(
+        const DictionaryStructureWithBufferPolicy *const structurePolicy, const int position) {
+    mBigramMaps[position].init(structurePolicy, position);
+}
+
+int MultiBigramMap::readBigramProbabilityFromBinaryDictionary(
+        const DictionaryStructureWithBufferPolicy *const structurePolicy, const int nodePos,
+        const int nextWordPosition, const int unigramProbability) {
+    int bigramProbability = NOT_A_PROBABILITY;
+    const int bigramsListPos = structurePolicy->getBigramsPositionOfPtNode(nodePos);
+    BinaryDictionaryBigramsIterator bigramsIt(structurePolicy->getBigramsStructurePolicy(),
+            bigramsListPos);
+    while (bigramsIt.hasNext()) {
+        bigramsIt.next();
+        if (bigramsIt.getBigramPos() == nextWordPosition) {
+            bigramProbability = bigramsIt.getProbability();
+            break;
+        }
+    }
+    return structurePolicy->getProbability(unigramProbability, bigramProbability);
+}
+
 } // namespace latinime
diff --git a/native/jni/src/suggest/core/dictionary/multi_bigram_map.h b/native/jni/src/suggest/core/dictionary/multi_bigram_map.h
index 4633c07..421b268 100644
--- a/native/jni/src/suggest/core/dictionary/multi_bigram_map.h
+++ b/native/jni/src/suggest/core/dictionary/multi_bigram_map.h
@@ -38,21 +38,7 @@
     // Look up the bigram probability for the given word pair from the cached bigram maps.
     // Also caches the bigrams if there is space remaining and they have not been cached already.
     int getBigramProbability(const DictionaryStructureWithBufferPolicy *const structurePolicy,
-            const int wordPosition, const int nextWordPosition, const int unigramProbability) {
-        hash_map_compat<int, BigramMap>::const_iterator mapPosition =
-                mBigramMaps.find(wordPosition);
-        if (mapPosition != mBigramMaps.end()) {
-            return mapPosition->second.getBigramProbability(structurePolicy, nextWordPosition,
-                    unigramProbability);
-        }
-        if (mBigramMaps.size() < MAX_CACHED_PREV_WORDS_IN_BIGRAM_MAP) {
-            addBigramsForWordPosition(structurePolicy, wordPosition);
-            return mBigramMaps[wordPosition].getBigramProbability(structurePolicy,
-                    nextWordPosition, unigramProbability);
-        }
-        return readBigramProbabilityFromBinaryDictionary(structurePolicy, wordPosition,
-                nextWordPosition, unigramProbability);
-    }
+            const int wordPosition, const int nextWordPosition, const int unigramProbability);
 
     void clear() {
         mBigramMaps.clear();
@@ -67,33 +53,11 @@
         ~BigramMap() {}
 
         void init(const DictionaryStructureWithBufferPolicy *const structurePolicy,
-                const int nodePos) {
-            const int bigramsListPos = structurePolicy->getBigramsPositionOfPtNode(nodePos);
-            BinaryDictionaryBigramsIterator bigramsIt(structurePolicy->getBigramsStructurePolicy(),
-                    bigramsListPos);
-            while (bigramsIt.hasNext()) {
-                bigramsIt.next();
-                if (bigramsIt.getBigramPos() == NOT_A_DICT_POS) {
-                    continue;
-                }
-                mBigramMap[bigramsIt.getBigramPos()] = bigramsIt.getProbability();
-                mBloomFilter.setInFilter(bigramsIt.getBigramPos());
-            }
-        }
+                const int nodePos);
 
-        AK_FORCE_INLINE int getBigramProbability(
+        int getBigramProbability(
                 const DictionaryStructureWithBufferPolicy *const structurePolicy,
-                const int nextWordPosition, const int unigramProbability) const {
-            int bigramProbability = NOT_A_PROBABILITY;
-            if (mBloomFilter.isInFilter(nextWordPosition)) {
-                const hash_map_compat<int, int>::const_iterator bigramProbabilityIt =
-                        mBigramMap.find(nextWordPosition);
-                if (bigramProbabilityIt != mBigramMap.end()) {
-                    bigramProbability = bigramProbabilityIt->second;
-                }
-            }
-            return structurePolicy->getProbability(unigramProbability, bigramProbability);
-        }
+                const int nextWordPosition, const int unigramProbability) const;
 
      private:
         // NOTE: The BigramMap class doesn't use DISALLOW_COPY_AND_ASSIGN() because its default
@@ -103,27 +67,12 @@
         BloomFilter mBloomFilter;
     };
 
-    AK_FORCE_INLINE void addBigramsForWordPosition(
-            const DictionaryStructureWithBufferPolicy *const structurePolicy, const int position) {
-        mBigramMaps[position].init(structurePolicy, position);
-    }
+    void addBigramsForWordPosition(
+            const DictionaryStructureWithBufferPolicy *const structurePolicy, const int position);
 
-    AK_FORCE_INLINE int readBigramProbabilityFromBinaryDictionary(
+    int readBigramProbabilityFromBinaryDictionary(
             const DictionaryStructureWithBufferPolicy *const structurePolicy, const int nodePos,
-            const int nextWordPosition, const int unigramProbability) {
-        int bigramProbability = NOT_A_PROBABILITY;
-        const int bigramsListPos = structurePolicy->getBigramsPositionOfPtNode(nodePos);
-        BinaryDictionaryBigramsIterator bigramsIt(structurePolicy->getBigramsStructurePolicy(),
-                bigramsListPos);
-        while (bigramsIt.hasNext()) {
-            bigramsIt.next();
-            if (bigramsIt.getBigramPos() == nextWordPosition) {
-                bigramProbability = bigramsIt.getProbability();
-                break;
-            }
-        }
-        return structurePolicy->getProbability(unigramProbability, bigramProbability);
-    }
+            const int nextWordPosition, const int unigramProbability);
 
     static const size_t MAX_CACHED_PREV_WORDS_IN_BIGRAM_MAP;
     hash_map_compat<int, BigramMap> mBigramMaps;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.cpp
index 7160f6f..48ddb2f 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.cpp
@@ -26,12 +26,18 @@
 
 void Ver4BigramListPolicy::getNextBigram(int *const outBigramPos, int *const outProbability,
         bool *const outHasNext, int *const bigramEntryPos) const {
-    int targetTerminalId = Ver4DictConstants::NOT_A_TERMINAL_ID;
-    mBigramDictContent->getBigramEntryAndAdvancePosition(outProbability, outHasNext,
-            &targetTerminalId, bigramEntryPos);
+    const BigramEntry bigramEntry =
+            mBigramDictContent->getBigramEntryAndAdvancePosition(bigramEntryPos);
     if (outBigramPos) {
         // Lookup target PtNode position.
-        *outBigramPos = mTerminalPositionLookupTable->getTerminalPtNodePosition(targetTerminalId);
+        *outBigramPos = mTerminalPositionLookupTable->getTerminalPtNodePosition(
+                bigramEntry.getTargetTerminalId());
+    }
+    if (outProbability) {
+        *outProbability = bigramEntry.getProbability();
+    }
+    if (outHasNext) {
+        *outHasNext = bigramEntry.hasNext();
     }
 }
 
@@ -47,12 +53,13 @@
         if (!mBigramDictContent->createNewBigramList(terminalId)) {
             return false;
         }
-        const int probabilityToWrite = getUpdatedProbability(
-                NOT_A_PROBABILITY /* originalProbability */, newProbability);
+        const BigramEntry newBigramEntry(false /* hasNext */, NOT_A_PROBABILITY,
+                newTargetTerminalId);
+        const BigramEntry bigramEntryToWrite = getUpdatedBigramEntry(&newBigramEntry,
+                newProbability, timestamp);
         // Write an entry.
         const int writingPos =  mBigramDictContent->getBigramListHeadPos(terminalId);
-        if (!mBigramDictContent->writeBigramEntry(probabilityToWrite, false /* hasNext */,
-                newTargetTerminalId, writingPos)) {
+        if (!mBigramDictContent->writeBigramEntry(&bigramEntryToWrite, writingPos)) {
             return false;
         }
         if (outAddedNewEntry) {
@@ -64,18 +71,19 @@
     const int entryPosToUpdate = getEntryPosToUpdate(newTargetTerminalId, bigramListPos);
     if (entryPosToUpdate != NOT_A_DICT_POS) {
         // Overwrite existing entry.
-        bool hasNext = false;
-        int probability = NOT_A_PROBABILITY;
-        int targetTerminalId = Ver4DictConstants::NOT_A_TERMINAL_ID;
-        mBigramDictContent->getBigramEntry(&probability, &hasNext, &targetTerminalId,
-                entryPosToUpdate);
-        const int probabilityToWrite = getUpdatedProbability(probability, newProbability);
-        if (targetTerminalId == Ver4DictConstants::NOT_A_TERMINAL_ID && outAddedNewEntry) {
+        const BigramEntry originalBigramEntry =
+                mBigramDictContent->getBigramEntry(entryPosToUpdate);
+        if (!originalBigramEntry.isValid()) {
             // Reuse invalid entry.
-            *outAddedNewEntry = true;
+            if (outAddedNewEntry) {
+                *outAddedNewEntry = true;
+            }
         }
-        return mBigramDictContent->writeBigramEntry(probabilityToWrite, hasNext,
-                newTargetTerminalId, entryPosToUpdate);
+        const BigramEntry updatedBigramEntry =
+                originalBigramEntry.updateTargetTerminalIdAndGetEntry(newTargetTerminalId);
+        const BigramEntry bigramEntryToWrite = getUpdatedBigramEntry(
+                &updatedBigramEntry, newProbability, timestamp);
+        return mBigramDictContent->writeBigramEntry(&bigramEntryToWrite, entryPosToUpdate);
     }
 
     // Add new entry to the bigram list.
@@ -85,10 +93,10 @@
     }
     // Write new entry at a head position of the bigram list.
     int writingPos = mBigramDictContent->getBigramListHeadPos(terminalId);
-    const int probabilityToWrite = getUpdatedProbability(
-            NOT_A_PROBABILITY /* originalProbability */, newProbability);
-    if (!mBigramDictContent->writeBigramEntryAndAdvancePosition(probabilityToWrite,
-            true /* hasNext */, newTargetTerminalId, &writingPos)) {
+    const BigramEntry newBigramEntry(true /* hasNext */, NOT_A_PROBABILITY, newTargetTerminalId);
+    const BigramEntry bigramEntryToWrite = getUpdatedBigramEntry(
+            &newBigramEntry, newProbability, timestamp);
+    if (!mBigramDictContent->writeBigramEntryAndAdvancePosition(&bigramEntryToWrite, &writingPos)) {
         return false;
     }
     if (outAddedNewEntry) {
@@ -109,18 +117,14 @@
         // Bigram entry doesn't exist.
         return false;
     }
-    bool hasNext = false;
-    int probability = NOT_A_PROBABILITY;
-    int originalTargetTerminalId = Ver4DictConstants::NOT_A_TERMINAL_ID;
-    mBigramDictContent->getBigramEntry(&probability, &hasNext, &originalTargetTerminalId,
-            entryPosToUpdate);
-    if (targetTerminalId != originalTargetTerminalId) {
+    const BigramEntry bigramEntry = mBigramDictContent->getBigramEntry(entryPosToUpdate);
+    if (targetTerminalId != bigramEntry.getTargetTerminalId()) {
         // Bigram entry doesn't exist.
         return false;
     }
-    // Remove bigram entry by overwriting target terminal Id.
-    return mBigramDictContent->writeBigramEntry(probability, hasNext,
-            Ver4DictConstants::NOT_A_TERMINAL_ID /* targetTerminalId */, entryPosToUpdate);
+    // Remove bigram entry by marking it as invalid entry and overwriting the original entry.
+    const BigramEntry updatedBigramEntry = bigramEntry.getInvalidatedEntry();
+    return mBigramDictContent->writeBigramEntry(&updatedBigramEntry, entryPosToUpdate);
 }
 
 bool Ver4BigramListPolicy::updateAllBigramEntriesAndDeleteUselessEntries(const int terminalId,
@@ -134,34 +138,35 @@
     int readingPos = bigramListPos;
     while (hasNext) {
         const int entryPos = readingPos;
-        int probability = NOT_A_PROBABILITY;
-        int targetTerminalId = Ver4DictConstants::NOT_A_TERMINAL_ID;
-        mBigramDictContent->getBigramEntryAndAdvancePosition(&probability, &hasNext,
-                &targetTerminalId, &readingPos);
-        if (targetTerminalId == Ver4DictConstants::NOT_A_TERMINAL_ID) {
+        const BigramEntry bigramEntry =
+                mBigramDictContent->getBigramEntryAndAdvancePosition(&readingPos);
+        hasNext = bigramEntry.hasNext();
+        if (!bigramEntry.isValid()) {
             continue;
         }
         const int targetPtNodePos = mTerminalPositionLookupTable->getTerminalPtNodePosition(
-                targetTerminalId);
+                bigramEntry.getTargetTerminalId());
         if (targetPtNodePos == NOT_A_DICT_POS) {
             // Invalidate bigram entry.
-            if (!mBigramDictContent->writeBigramEntry(probability, hasNext,
-                    Ver4DictConstants::NOT_A_TERMINAL_ID /* targetTerminalId */, entryPos)) {
+            const BigramEntry updatedBigramEntry = bigramEntry.getInvalidatedEntry();
+            if (!mBigramDictContent->writeBigramEntry(&updatedBigramEntry, entryPos)) {
                 return false;
             }
         } else if (mNeedsToDecayWhenUpdating) {
-            probability = ForgettingCurveUtils::getEncodedProbabilityToSave(
-                    probability, mHeaderPolicy);
+            // TODO: Quit decaying probability during GC.
+            const int probability = ForgettingCurveUtils::getEncodedProbabilityToSave(
+                    bigramEntry.getProbability(), mHeaderPolicy);
             if (ForgettingCurveUtils::isValidEncodedProbability(probability)) {
-                if (!mBigramDictContent->writeBigramEntry(probability, hasNext, targetTerminalId,
-                        entryPos)) {
+                const BigramEntry updatedBigramEntry =
+                        bigramEntry.updateProbabilityAndGetEntry(probability);
+                if (!mBigramDictContent->writeBigramEntry(&updatedBigramEntry, entryPos)) {
                     return false;
                 }
                 *outBigramCount += 1;
             } else {
                 // Remove entry.
-                if (!mBigramDictContent->writeBigramEntry(probability, hasNext,
-                        Ver4DictConstants::NOT_A_TERMINAL_ID /* targetTerminalId */, entryPos)) {
+                const BigramEntry updatedBigramEntry = bigramEntry.getInvalidatedEntry();
+                if (!mBigramDictContent->writeBigramEntry(&updatedBigramEntry, entryPos)) {
                     return false;
                 }
             }
@@ -182,10 +187,10 @@
     bool hasNext = true;
     int readingPos = bigramListPos;
     while (hasNext) {
-        int targetTerminalId = Ver4DictConstants::NOT_A_TERMINAL_ID;
-        mBigramDictContent->getBigramEntryAndAdvancePosition(0 /* probability */, &hasNext,
-                &targetTerminalId, &readingPos);
-        if (targetTerminalId != Ver4DictConstants::NOT_A_TERMINAL_ID) {
+        const BigramEntry bigramEntry =
+                mBigramDictContent->getBigramEntryAndAdvancePosition(&readingPos);
+        hasNext = bigramEntry.hasNext();
+        if (bigramEntry.isValid()) {
             bigramCount++;
         }
     }
@@ -199,13 +204,13 @@
     int readingPos = bigramListPos;
     while (hasNext) {
         const int entryPos = readingPos;
-        int targetTerminalId = Ver4DictConstants::NOT_A_TERMINAL_ID;
-        mBigramDictContent->getBigramEntryAndAdvancePosition(0 /* probability */, &hasNext,
-                &targetTerminalId, &readingPos);
-        if (targetTerminalId == targetTerminalIdToFind) {
+        const BigramEntry bigramEntry =
+                mBigramDictContent->getBigramEntryAndAdvancePosition(&readingPos);
+        hasNext = bigramEntry.hasNext();
+        if (bigramEntry.getTargetTerminalId() == targetTerminalIdToFind) {
             // Entry with same target is found.
             return entryPos;
-        } else if (targetTerminalId == Ver4DictConstants::NOT_A_TERMINAL_ID) {
+        } else if (!bigramEntry.isValid()) {
             // Invalid entry that can be reused is found.
             invalidEntryPos = entryPos;
         }
@@ -213,13 +218,16 @@
     return invalidEntryPos;
 }
 
-int Ver4BigramListPolicy::getUpdatedProbability(const int originalProbability,
-        const int newProbability) const {
+const BigramEntry Ver4BigramListPolicy::getUpdatedBigramEntry(
+        const BigramEntry *const originalBigramEntry, const int newProbability,
+        const int timestamp) const {
     if (mNeedsToDecayWhenUpdating) {
-        return ForgettingCurveUtils::getUpdatedEncodedProbability(originalProbability,
-                newProbability);
+        // TODO: Update historical information.
+        const int probability = ForgettingCurveUtils::getUpdatedEncodedProbability(
+                originalBigramEntry->getProbability(), newProbability);
+        return originalBigramEntry->updateProbabilityAndGetEntry(probability);
     } else {
-        return newProbability;
+        return originalBigramEntry->updateProbabilityAndGetEntry(newProbability);
     }
 }
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.h
index c095911..e718645 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.h
@@ -19,6 +19,7 @@
 
 #include "defines.h"
 #include "suggest/core/policy/dictionary_bigrams_structure_policy.h"
+#include "suggest/policyimpl/dictionary/structure/v4/content/bigram_entry.h"
 
 namespace latinime {
 
@@ -58,7 +59,8 @@
 
     int getEntryPosToUpdate(const int targetTerminalIdToFind, const int bigramListPos) const;
 
-    int getUpdatedProbability(const int originalProbability, const int newProbability) const;
+    const BigramEntry getUpdatedBigramEntry(const BigramEntry *const originalBigramEntry,
+            const int newProbability, const int timestamp) const;
 
     BigramDictContent *const mBigramDictContent;
     const TerminalPositionLookupTable *const mTerminalPositionLookupTable;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp
index 4cd9672..2a78354 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp
@@ -20,52 +20,99 @@
 
 namespace latinime {
 
-void BigramDictContent::getBigramEntryAndAdvancePosition(int *const outProbability,
-        bool *const outHasNext, int *const outTargetTerminalId, int *const bigramEntryPos) const {
+const BigramEntry BigramDictContent::getBigramEntryAndAdvancePosition(
+        int *const bigramEntryPos) const {
     const BufferWithExtendableBuffer *const bigramListBuffer = getContentBuffer();
     const int bigramFlags = bigramListBuffer->readUintAndAdvancePosition(
             Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE, bigramEntryPos);
-    if (outProbability) {
-        *outProbability = bigramFlags & Ver4DictConstants::BIGRAM_PROBABILITY_MASK;
+    const int hasNext = (bigramFlags & Ver4DictConstants::BIGRAM_HAS_NEXT_MASK) != 0;
+    int probability = NOT_A_PROBABILITY;
+    int timestamp = Ver4DictConstants::NOT_A_TIME_STAMP;
+    int level = 0;
+    int count = 0;
+    if (mHasHistoricalInfo) {
+        probability = bigramListBuffer->readUintAndAdvancePosition(
+                Ver4DictConstants::PROBABILITY_SIZE, bigramEntryPos);
+        timestamp = bigramListBuffer->readUintAndAdvancePosition(
+                Ver4DictConstants::TIME_STAMP_FIELD_SIZE, bigramEntryPos);
+        level = bigramListBuffer->readUintAndAdvancePosition(
+                Ver4DictConstants::WORD_LEVEL_FIELD_SIZE, bigramEntryPos);
+        count = bigramListBuffer->readUintAndAdvancePosition(
+                Ver4DictConstants::WORD_COUNT_FIELD_SIZE, bigramEntryPos);
+    } else {
+        probability = bigramFlags & Ver4DictConstants::BIGRAM_PROBABILITY_MASK;
     }
-    if (outHasNext) {
-        *outHasNext = (bigramFlags & Ver4DictConstants::BIGRAM_HAS_NEXT_MASK) != 0;
-    }
-    const int targetTerminalId = bigramListBuffer->readUintAndAdvancePosition(
+    const int encodedTargetTerminalId = bigramListBuffer->readUintAndAdvancePosition(
             Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE, bigramEntryPos);
-    if (outTargetTerminalId) {
-        *outTargetTerminalId =
-                (targetTerminalId == Ver4DictConstants::INVALID_BIGRAM_TARGET_TERMINAL_ID) ?
-                        Ver4DictConstants::NOT_A_TERMINAL_ID : targetTerminalId;
+    const int targetTerminalId =
+            (encodedTargetTerminalId == Ver4DictConstants::INVALID_BIGRAM_TARGET_TERMINAL_ID) ?
+                    Ver4DictConstants::NOT_A_TERMINAL_ID : encodedTargetTerminalId;
+    if (mHasHistoricalInfo) {
+        return BigramEntry(hasNext, probability, timestamp, level, count, targetTerminalId);
+    } else {
+        return BigramEntry(hasNext, probability, targetTerminalId);
     }
 }
 
-bool BigramDictContent::writeBigramEntryAndAdvancePosition(const int probability, const int hasNext,
-        const int targetTerminalId, int *const entryWritingPos) {
+bool BigramDictContent::writeBigramEntryAndAdvancePosition(
+        const BigramEntry *const bigramEntryToWrite, int *const entryWritingPos) {
     BufferWithExtendableBuffer *const bigramListBuffer = getWritableContentBuffer();
-    const int bigramFlags = createAndGetBigramFlags(probability, hasNext);
+    const int bigramFlags = createAndGetBigramFlags(
+            mHasHistoricalInfo ? 0 : bigramEntryToWrite->getProbability(),
+            bigramEntryToWrite->hasNext());
     if (!bigramListBuffer->writeUintAndAdvancePosition(bigramFlags,
             Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE, entryWritingPos)) {
+        AKLOGE("Cannot write bigram flags. pos: %d, flags: %x", *entryWritingPos, bigramFlags);
         return false;
     }
+    if (mHasHistoricalInfo) {
+        if (!bigramListBuffer->writeUintAndAdvancePosition(bigramEntryToWrite->getProbability(),
+                Ver4DictConstants::PROBABILITY_SIZE, entryWritingPos)) {
+            AKLOGE("Cannot write bigram probability. pos: %d, probability: %d", *entryWritingPos,
+                    bigramEntryToWrite->getProbability());
+            return false;
+        }
+        if (!bigramListBuffer->writeUintAndAdvancePosition(bigramEntryToWrite->getTimeStamp(),
+                Ver4DictConstants::TIME_STAMP_FIELD_SIZE, entryWritingPos)) {
+            AKLOGE("Cannot write bigram timestamps. pos: %d, timestamp: %d", *entryWritingPos,
+                    bigramEntryToWrite->getTimeStamp());
+            return false;
+        }
+        if (!bigramListBuffer->writeUintAndAdvancePosition(bigramEntryToWrite->getLevel(),
+                Ver4DictConstants::WORD_LEVEL_FIELD_SIZE, entryWritingPos)) {
+            AKLOGE("Cannot write bigram level. pos: %d, level: %d", *entryWritingPos,
+                    bigramEntryToWrite->getLevel());
+            return false;
+        }
+        if (!bigramListBuffer->writeUintAndAdvancePosition(bigramEntryToWrite->getCount(),
+                Ver4DictConstants::WORD_COUNT_FIELD_SIZE, entryWritingPos)) {
+            AKLOGE("Cannot write bigram count. pos: %d, count: %d", *entryWritingPos,
+                    bigramEntryToWrite->getCount());
+            return false;
+        }
+    }
     const int targetTerminalIdToWrite =
-            (targetTerminalId == Ver4DictConstants::NOT_A_TERMINAL_ID) ?
-                    Ver4DictConstants::INVALID_BIGRAM_TARGET_TERMINAL_ID : targetTerminalId;
-    return bigramListBuffer->writeUintAndAdvancePosition(targetTerminalIdToWrite,
-            Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE, entryWritingPos);
+            (bigramEntryToWrite->getTargetTerminalId() == Ver4DictConstants::NOT_A_TERMINAL_ID) ?
+                    Ver4DictConstants::INVALID_BIGRAM_TARGET_TERMINAL_ID :
+                            bigramEntryToWrite->getTargetTerminalId();
+    if (!bigramListBuffer->writeUintAndAdvancePosition(targetTerminalIdToWrite,
+            Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE, entryWritingPos)) {
+        AKLOGE("Cannot write bigram target terminal id. pos: %d, target terminal id: %d",
+                *entryWritingPos, bigramEntryToWrite->getTargetTerminalId());
+        return false;
+    }
+    return true;
 }
 
 bool BigramDictContent::copyBigramList(const int bigramListPos, const int toPos) {
-    bool hasNext = true;
     int readingPos = bigramListPos;
     int writingPos = toPos;
+    bool hasNext = true;
     while (hasNext) {
-        int probability = NOT_A_PROBABILITY;
-        int targetTerminalId = Ver4DictConstants::NOT_A_TERMINAL_ID;
-        getBigramEntryAndAdvancePosition(&probability, &hasNext, &targetTerminalId,
-                &readingPos);
-        if (!writeBigramEntryAndAdvancePosition(probability, hasNext, targetTerminalId,
-                &writingPos)) {
+        const BigramEntry bigramEntry = getBigramEntryAndAdvancePosition(&readingPos);
+        hasNext = bigramEntry.hasNext();
+        if (!writeBigramEntryAndAdvancePosition(&bigramEntry, &writingPos)) {
+            AKLOGE("Cannot write bigram entry to copy. pos: %d", writingPos);
             return false;
         }
     }
@@ -88,6 +135,8 @@
         // Copy bigram list with GC from original content.
         if (!runGCBigramList(originalBigramListPos, originalBigramDictContent, bigramListPos,
                 terminalIdMap, &bigramEntryCount)) {
+            AKLOGE("Cannot complete GC for the bigram list. original pos: %d, pos: %d",
+                    originalBigramListPos, bigramListPos);
             return false;
         }
         if (bigramEntryCount == 0) {
@@ -97,6 +146,8 @@
         *outBigramEntryCount += bigramEntryCount;
         // Set bigram list position to the lookup table.
         if (!getUpdatableAddressLookupTable()->set(it->second, bigramListPos)) {
+            AKLOGE("Cannot set bigram list position. terminal id: %d, pos: %d",
+                    it->second, bigramListPos);
             return false;
         }
     }
@@ -111,26 +162,38 @@
     bool hasNext = true;
     int readingPos = bigramListPos;
     int writingPos = toPos;
+    int lastEntryPos = NOT_A_DICT_POS;
     while (hasNext) {
-        int probability = NOT_A_PROBABILITY;
-        int targetTerminalId = Ver4DictConstants::NOT_A_TERMINAL_ID;
-        sourceBigramDictContent->getBigramEntryAndAdvancePosition(&probability, &hasNext,
-                &targetTerminalId, &readingPos);
-        if (targetTerminalId == Ver4DictConstants::NOT_A_TERMINAL_ID) {
+        const BigramEntry originalBigramEntry =
+                sourceBigramDictContent->getBigramEntryAndAdvancePosition(&readingPos);
+        hasNext = originalBigramEntry.hasNext();
+        if (originalBigramEntry.getTargetTerminalId() == Ver4DictConstants::NOT_A_TERMINAL_ID) {
             continue;
         }
         TerminalPositionLookupTable::TerminalIdMap::const_iterator it =
-                terminalIdMap->find(targetTerminalId);
+                terminalIdMap->find(originalBigramEntry.getTargetTerminalId());
         if (it == terminalIdMap->end()) {
             // Target word has been removed.
             continue;
         }
-        if (!writeBigramEntryAndAdvancePosition(probability, hasNext, it->second,
-                &writingPos)) {
+        lastEntryPos = hasNext ? writingPos : NOT_A_DICT_POS;
+        const BigramEntry updatedBigramEntry =
+                originalBigramEntry.updateTargetTerminalIdAndGetEntry(it->second);
+        if (!writeBigramEntryAndAdvancePosition(&updatedBigramEntry, &writingPos)) {
+            AKLOGE("Cannot write bigram entry to run GC. pos: %d", writingPos);
             return false;
         }
         *outEntrycount += 1;
     }
+    if (lastEntryPos != NOT_A_DICT_POS) {
+        // Update has next flag in the last written entry.
+        const BigramEntry bigramEntry = getBigramEntry(lastEntryPos).updateHasNextAndGetEntry(
+                false /* hasNext */);
+        if (!writeBigramEntry(&bigramEntry, writingPos)) {
+            AKLOGE("Cannot write bigram entry to set hasNext flag after GC. pos: %d", writingPos);
+            return false;
+        }
+    }
     return true;
 }
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h
index cf380f4..ac05b21 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h
@@ -18,6 +18,7 @@
 #define LATINIME_BIGRAM_DICT_CONTENT_H
 
 #include "defines.h"
+#include "suggest/policyimpl/dictionary/structure/v4/content/bigram_entry.h"
 #include "suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.h"
 #include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h"
 #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h"
@@ -26,27 +27,27 @@
 
 class BigramDictContent : public SparseTableDictContent {
  public:
-    BigramDictContent(const char *const dictDirPath, const bool isUpdatable)
+    BigramDictContent(const char *const dictDirPath, const bool hasHistoricalInfo,
+            const bool isUpdatable)
             : SparseTableDictContent(dictDirPath,
                       Ver4DictConstants::BIGRAM_LOOKUP_TABLE_FILE_EXTENSION,
                       Ver4DictConstants::BIGRAM_CONTENT_TABLE_FILE_EXTENSION,
                       Ver4DictConstants::BIGRAM_FILE_EXTENSION, isUpdatable,
                       Ver4DictConstants::BIGRAM_ADDRESS_TABLE_BLOCK_SIZE,
-                      Ver4DictConstants::BIGRAM_ADDRESS_TABLE_DATA_SIZE) {}
+                      Ver4DictConstants::BIGRAM_ADDRESS_TABLE_DATA_SIZE),
+              mHasHistoricalInfo(hasHistoricalInfo) {}
 
-    BigramDictContent()
+    BigramDictContent(const bool hasHistoricalInfo)
             : SparseTableDictContent(Ver4DictConstants::BIGRAM_ADDRESS_TABLE_BLOCK_SIZE,
-                      Ver4DictConstants::BIGRAM_ADDRESS_TABLE_DATA_SIZE) {}
+                      Ver4DictConstants::BIGRAM_ADDRESS_TABLE_DATA_SIZE),
+              mHasHistoricalInfo(hasHistoricalInfo) {}
 
-    void getBigramEntry(int *const outProbability, bool *const outHasNext,
-            int *const outTargetTerminalId, const int bigramEntryPos) const {
+    const BigramEntry getBigramEntry(const int bigramEntryPos) const {
         int readingPos = bigramEntryPos;
-        getBigramEntryAndAdvancePosition(outProbability, outHasNext, outTargetTerminalId,
-                &readingPos);
+        return getBigramEntryAndAdvancePosition(&readingPos);
     }
 
-    void getBigramEntryAndAdvancePosition(int *const outProbability, bool *const outHasNext,
-            int *const outTargetTerminalId, int *const bigramEntryPos) const;
+    const BigramEntry getBigramEntryAndAdvancePosition(int *const bigramEntryPos) const;
 
     // Returns head position of bigram list for a PtNode specified by terminalId.
     int getBigramListHeadPos(const int terminalId) const {
@@ -57,15 +58,13 @@
         return addressLookupTable->get(terminalId);
     }
 
-    bool writeBigramEntry(const int probability, const int hasNext, const int targetTerminalId,
-            const int entryWritingPos) {
+    bool writeBigramEntry(const BigramEntry *const bigramEntryToWrite, const int entryWritingPos) {
         int writingPos = entryWritingPos;
-        return writeBigramEntryAndAdvancePosition(probability, hasNext, targetTerminalId,
-                &writingPos);
+        return writeBigramEntryAndAdvancePosition(bigramEntryToWrite, &writingPos);
     }
 
-    bool writeBigramEntryAndAdvancePosition(const int probability, const int hasNext,
-            const int targetTerminalId, int *const entryWritingPos);
+    bool writeBigramEntryAndAdvancePosition(const BigramEntry *const bigramEntryToWrite,
+            int *const entryWritingPos);
 
     bool createNewBigramList(const int terminalId) {
         const int bigramListPos = getContentBuffer()->getTailPosition();
@@ -96,6 +95,8 @@
             const BigramDictContent *const sourceBigramDictContent, const int toPos,
             const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap,
             int *const outEntryCount);
+
+    bool mHasHistoricalInfo;
 };
 } // namespace latinime
 #endif /* LATINIME_BIGRAM_DICT_CONTENT_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_entry.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_entry.h
new file mode 100644
index 0000000..10b3ec8
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_entry.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2013, 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_BIGRAM_ENTRY_H
+#define LATINIME_BIGRAM_ENTRY_H
+
+#include "defines.h"
+#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h"
+
+namespace latinime {
+
+class BigramEntry {
+ public:
+    BigramEntry(const BigramEntry& bigramEntry)
+            : mHasNext(bigramEntry.mHasNext), mProbability(bigramEntry.mProbability),
+              mTimestamp(bigramEntry.mTimestamp), mLevel(bigramEntry.mLevel),
+              mCount(bigramEntry.mCount), mTargetTerminalId(bigramEntry.mTargetTerminalId) {}
+
+    // Entry with historical information.
+    BigramEntry(const bool hasNext, const int probability, const int targetTerminalId)
+            : mHasNext(hasNext), mProbability(probability),
+              mTimestamp(Ver4DictConstants::NOT_A_TIME_STAMP), mLevel(0), mCount(0),
+              mTargetTerminalId(targetTerminalId) {}
+
+    // Entry with historical information.
+    BigramEntry(const bool hasNext, const int probability, const int timestamp, const int level,
+            const int count, const int targetTerminalId)
+            : mHasNext(hasNext), mProbability(probability), mTimestamp(timestamp),
+              mLevel(level), mCount(count), mTargetTerminalId(targetTerminalId) {}
+
+    const BigramEntry getInvalidatedEntry() const {
+        return updateTargetTerminalIdAndGetEntry(Ver4DictConstants::NOT_A_TERMINAL_ID);
+    }
+
+    const BigramEntry updateHasNextAndGetEntry(const bool hasNext) const {
+        return BigramEntry(hasNext, mProbability, mTimestamp, mLevel, mCount,
+                mTargetTerminalId);
+    }
+
+    const BigramEntry updateTargetTerminalIdAndGetEntry(const int newTargetTerminalId) const {
+        return BigramEntry(mHasNext, mProbability, mTimestamp, mLevel, mCount,
+                newTargetTerminalId);
+    }
+
+    const BigramEntry updateProbabilityAndGetEntry(const int probability) const {
+        return BigramEntry(mHasNext, probability, mTimestamp, mLevel, mCount,
+                mTargetTerminalId);
+    }
+
+    bool isValid() const {
+        return mTargetTerminalId != Ver4DictConstants::NOT_A_TERMINAL_ID;
+    }
+
+    bool hasNext() const {
+        return mHasNext;
+    }
+
+    int getProbability() const {
+        return mProbability;
+    }
+
+    int getTimeStamp() const {
+        return mTimestamp;
+    }
+
+    int getLevel() const {
+        return mLevel;
+    }
+
+    int getCount() const {
+        return mCount;
+    }
+
+    int getTargetTerminalId() const {
+        return mTargetTerminalId;
+    }
+
+ private:
+    // Copy constructor is public to use this class as a type of return value.
+    DISALLOW_DEFAULT_CONSTRUCTOR(BigramEntry);
+    DISALLOW_ASSIGNMENT_OPERATOR(BigramEntry);
+
+    const bool mHasNext;
+    const int mProbability;
+    const int mTimestamp;
+    const int mLevel;
+    const int mCount;
+    const int mTargetTerminalId;
+};
+} // namespace latinime
+#endif /* LATINIME_BIGRAM_ENTRY_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h
index 8fdbbed..07b8f18 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h
@@ -127,7 +127,7 @@
               // TODO: Quit using header size.
               mTerminalPositionLookupTable(dictDirPath, isUpdatable, mHeaderSize),
               mProbabilityDictContent(dictDirPath, false /* hasHistoricalInfo */, isUpdatable),
-              mBigramDictContent(dictDirPath, isUpdatable),
+              mBigramDictContent(dictDirPath, false /* hasHistoricalInfo */, isUpdatable),
               mShortcutDictContent(dictDirPath, isUpdatable),
               mIsUpdatable(isUpdatable) {}
 
@@ -137,7 +137,8 @@
               mExpandableTrieBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE),
               mTerminalPositionLookupTable(),
               mProbabilityDictContent(false /* hasHistoricalInfo */),
-              mBigramDictContent(), mShortcutDictContent(), mIsUpdatable(true) {}
+              mBigramDictContent(false /* hasHistoricalInfo */), mShortcutDictContent(),
+              mIsUpdatable(true) {}
 
     const MmappedBuffer::MmappedBufferPtr mDictBuffer;
     const int mHeaderSize;
diff --git a/native/jni/src/utils/exclusive_ownership_pointer.h b/native/jni/src/utils/exclusive_ownership_pointer.h
index 3cf7895..6c67df2 100644
--- a/native/jni/src/utils/exclusive_ownership_pointer.h
+++ b/native/jni/src/utils/exclusive_ownership_pointer.h
@@ -56,8 +56,7 @@
  private:
     // This class allows to copy and assign and ensures only one instance has the ownership of the
     // managed pointer.
-
-    ExclusiveOwnershipPointer() : mPointer(0), mSharedOwnerPtr(0) {}
+    DISALLOW_DEFAULT_CONSTRUCTOR(ExclusiveOwnershipPointer);
 
     void transferOwnership(const ExclusiveOwnershipPointer<T> *const src) {
         if (*mSharedOwnerPtr != src) {
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
index c4749df..05de37d 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
@@ -265,7 +265,7 @@
                 getContext().getCacheDir());
 
         final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
-                BinaryDictUtils.getDictionaryOptions(dictName, dictVersion));
+                BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion));
         addUnigrams(words.size(), dict, words, shortcuts);
         addBigrams(dict, words, bigrams);
         checkDictionary(dict, words, bigrams, shortcuts);
@@ -444,7 +444,7 @@
 
         // making the dictionary from lists of words.
         final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
-                BinaryDictUtils.getDictionaryOptions(dictName, dictVersion));
+                BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion));
         addUnigrams(words.size(), dict, words, null /* shortcutMap */);
         addBigrams(dict, words, bigrams);
 
@@ -555,7 +555,7 @@
                 getContext().getCacheDir());
 
         final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
-                BinaryDictUtils.getDictionaryOptions(dictName, dictVersion));
+                BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion));
         addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */);
         addBigrams(dict, words, bigrams);
         timeWritingDictToFile(file, dict, formatOptions);
@@ -649,8 +649,7 @@
                 getContext().getCacheDir());
 
         final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
-                new FusionDictionary.DictionaryOptions(
-                        new HashMap<String, String>(), false, false));
+                BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion));
         addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */);
         timeWritingDictToFile(file, dict, formatOptions);
 
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
index 8bea3c0..da217ce 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
@@ -233,7 +233,7 @@
 
         // set an initial dictionary.
         final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
-                BinaryDictUtils.getDictionaryOptions(testName, version));
+                BinaryDictUtils.makeDictionaryOptions(testName, version));
         dict.add("abcd", 10, null, false);
 
         try {
@@ -301,7 +301,7 @@
 
         // set an initial dictionary.
         final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
-                BinaryDictUtils.getDictionaryOptions(testName, version));
+                BinaryDictUtils.makeDictionaryOptions(testName, version));
         dict.add("abcd", 10, null, false);
         dict.add("efgh", 15, null, false);
 
@@ -341,7 +341,7 @@
 
         // set an initial dictionary.
         final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
-                BinaryDictUtils.getDictionaryOptions(testName, version));
+                BinaryDictUtils.makeDictionaryOptions(testName, version));
         dict.add("initial", 10, null, false);
 
         try {
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
index 5ec3725..8b1521a 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.latin.makedict;
 
+import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
 import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
 import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
 
@@ -41,11 +42,12 @@
             new FormatSpec.FormatOptions(4, true /* supportsDynamicUpdate */,
                     true /* hasTimestamp */);
 
-    public static DictionaryOptions getDictionaryOptions(final String id, final String version) {
+    public static DictionaryOptions makeDictionaryOptions(final String id, final String version) {
         final DictionaryOptions options = new DictionaryOptions(new HashMap<String, String>(),
                 false /* germanUmlautProcessing */, false /* frenchLigatureProcessing */);
-        options.mAttributes.put("dictionary", id);
-        options.mAttributes.put("version", version);
+        options.mAttributes.put(FileHeader.DICTIONARY_LOCALE_ATTRIBUTE, "en_US");
+        options.mAttributes.put(FileHeader.DICTIONARY_ID_ATTRIBUTE, id);
+        options.mAttributes.put(FileHeader.DICTIONARY_VERSION_ATTRIBUTE, version);
         return options;
     }
 
diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
index c3e062b..9c93b8b 100644
--- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
@@ -16,8 +16,6 @@
 
 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;
@@ -38,7 +36,6 @@
 @LargeTest
 public class UserHistoryDictionaryTests extends AndroidTestCase {
     private static final String TAG = UserHistoryDictionaryTests.class.getSimpleName();
-    private SharedPreferences mPrefs;
 
     private static final String[] CHARACTERS = {
         "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
@@ -48,11 +45,6 @@
     private static final int MIN_USER_HISTORY_DICTIONARY_FILE_SIZE = 1000;
     private static final int WAIT_TERMINATING_IN_MILLISECONDS = 100;
 
-    @Override
-    public void setUp() {
-        mPrefs = PreferenceManager.getDefaultSharedPreferences(getContext());
-    }
-
     /**
      * Generates a random word.
      */
@@ -92,7 +84,7 @@
         final List<String> words = generateWords(numberOfWords, random);
         final UserHistoryDictionary dict =
                 PersonalizationHelper.getUserHistoryDictionary(getContext(),
-                        testFilenameSuffix /* locale */, mPrefs);
+                        testFilenameSuffix /* locale */);
         // Add random words to the user history dictionary.
         addToDict(dict, words);
         if (checkContents) {
@@ -116,7 +108,7 @@
     private void clearHistory(final String testFilenameSuffix) {
         final UserHistoryDictionary dict =
                 PersonalizationHelper.getUserHistoryDictionary(getContext(),
-                        testFilenameSuffix /* locale */, mPrefs);
+                        testFilenameSuffix /* locale */);
         dict.clearAndFlushDictionary();
         dict.close();
     }
@@ -129,7 +121,7 @@
         try {
             final UserHistoryDictionary dict =
                     PersonalizationHelper.getUserHistoryDictionary(getContext(),
-                            testFilenameSuffix, mPrefs);
+                            testFilenameSuffix);
             dict.shutdownExecutorForTests();
             while (!dict.isTerminatedForTests()) {
                 Thread.sleep(WAIT_TERMINATING_IN_MILLISECONDS);