diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index d970bca5e6e4e66a51a7f1d9d6e7b6743a26f989..6339e9c5000dc1ededa2aea76a335f1bbd32b3b9 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -2530,7 +2530,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         final int maxFreq = AutoCorrectionUtils.getMaxFrequency(
                 suggest.getUnigramDictionaries(), suggestion);
         if (maxFreq == 0) return null;
-        userHistoryPredictionDictionary.addToUserHistory(prevWord, secondWord, maxFreq > 0);
+        userHistoryPredictionDictionary
+                .addToPersonalizationPredictionDictionary(prevWord, secondWord, maxFreq > 0);
         return prevWord;
     }
 
diff --git a/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java
index 9d041f4eb66cd9a1b63b63d2487b89ac34c8910f..6498bf9c1a977f04884f8304a4ba4bc0e5e9ad4d 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java
@@ -31,6 +31,7 @@ import com.android.inputmethod.latin.WordComposer;
 import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
 import com.android.inputmethod.latin.settings.Settings;
 import com.android.inputmethod.latin.utils.ByteArrayWrapper;
+import com.android.inputmethod.latin.utils.CollectionUtils;
 import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils;
 import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.BigramDictionaryInterface;
 import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.OnAddWordListener;
@@ -49,9 +50,6 @@ import java.util.concurrent.locks.ReentrantLock;
  * This class is a base class of a dictionary for the personalized prediction language model.
  */
 public abstract class DynamicPredictionDictionaryBase extends ExpandableDictionary {
-    public static void registerUpdateListener(PersonalizationDictionaryUpdateListener listener) {
-        // TODO: Implement
-    }
 
     private static final String TAG = DynamicPredictionDictionaryBase.class.getSimpleName();
     public static final boolean DBG_SAVE_RESTORE = false;
@@ -75,6 +73,9 @@ public abstract class DynamicPredictionDictionaryBase extends ExpandableDictiona
     private final ReentrantLock mBigramListLock = new ReentrantLock();
     private final SharedPreferences mPrefs;
 
+    private final ArrayList<PersonalizationDictionaryUpdateSession> mSessions =
+            CollectionUtils.newArrayList();
+
     // Should always be false except when we use this class for test
     @UsedForTesting boolean isTest = false;
 
@@ -118,14 +119,15 @@ public abstract class DynamicPredictionDictionaryBase extends ExpandableDictiona
     }
 
     /**
-     * Pair will be added to the user history dictionary.
+     * Pair will be added to the personalization prediction dictionary.
      *
      * The first word may be null. That means we don't know the context, in other words,
      * it's only a unigram. The first word may also be an empty string : this means start
      * context, as in beginning of a sentence for example.
      * The second word may not be null (a NullPointerException would be thrown).
      */
-    public int addToUserHistory(final String word1, final String word2, final boolean isValid) {
+    public int addToPersonalizationPredictionDictionary(
+            final String word1, final String word2, final boolean isValid) {
         if (word2.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH ||
                 (word1 != null && word1.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH)) {
             return -1;
@@ -393,9 +395,14 @@ public abstract class DynamicPredictionDictionaryBase extends ExpandableDictiona
             final String word1, final String word2, final boolean isValid) {
         mBigramListLock.lock();
         try {
-            addToUserHistory(word1, word2, isValid);
+            addToPersonalizationPredictionDictionary(word1, word2, isValid);
         } finally {
             mBigramListLock.unlock();
         }
     }
+
+    public void registerUpdateSession(PersonalizationDictionaryUpdateSession session) {
+        session.setDictionary(this);
+        mSessions.add(session);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
index 19554d63912bdeff1ba71f9cf2b05bab43d5af42..e38a235e91c49eaf4029ace1790505681537d08c 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
@@ -27,7 +27,7 @@ import android.content.Context;
 public class PersonalizationDictionary extends ExpandableBinaryDictionary {
     private static final String NAME = "personalization";
 
-    public static void registerUpdateListener(PersonalizationDictionaryUpdateListener listener) {
+    public static void registerUpdateListener(PersonalizationDictionaryUpdateSession listener) {
         // TODO: Implement
     }
 
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java
index 9f013df1c2433a512c86dfa829af37800545239d..da256f8272caf0291953be6d6a6d20ec32b33761 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java
@@ -20,6 +20,7 @@ import com.android.inputmethod.latin.utils.CollectionUtils;
 
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
 import android.util.Log;
 
 import java.lang.ref.SoftReference;
@@ -58,6 +59,16 @@ public class PersonalizationDictionaryHelper {
         }
     }
 
+    public static void
+            registerPersonalizationDictionaryUpdateSession(final Context context,
+                    final PersonalizationDictionaryUpdateSession session) {
+        final PersonalizationPredictionDictionary dictionary =
+                getPersonalizationPredictionDictionary(context,
+                        context.getResources().getConfiguration().locale.toString(),
+                        PreferenceManager.getDefaultSharedPreferences(context));
+        dictionary.registerUpdateSession(session);
+    }
+
     public static PersonalizationPredictionDictionary getPersonalizationPredictionDictionary(
             final Context context, final String locale, final SharedPreferences sp) {
         synchronized (sLangPersonalizationDictCache) {
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateListener.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateListener.java
deleted file mode 100644
index c78e5a95bb2fd352a7de115a57a6a99e32cf2782..0000000000000000000000000000000000000000
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateListener.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.inputmethod.latin.personalization;
-
-public interface PersonalizationDictionaryUpdateListener {
-    // TODO: Implement
-}
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java
new file mode 100644
index 0000000000000000000000000000000000000000..d62aec19daf6fc198d37d06b872b0ad1e8c87e77
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+package com.android.inputmethod.latin.personalization;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+/**
+ * This class is a session where a data provider can communicate with a personalization
+ * dictionary.
+ */
+public abstract class PersonalizationDictionaryUpdateSession {
+    /**
+     * This class is a parameter for a new unigram or bigram word which will be added
+     * to the personalization dictionary.
+     */
+    public static class PersonalizationLanguageModelParam {
+        public final String mWord0;
+        public final String mWord1;
+        public final boolean mIsValid;
+        public final int mFrequency;
+        public PersonalizationLanguageModelParam(String word0, String word1, boolean isValid,
+                int frequency) {
+            mWord0 = word0;
+            mWord1 = word1;
+            mIsValid = isValid;
+            mFrequency = frequency;
+        }
+    }
+
+    // TODO: Use a dynamic binary dictionary instead
+    public WeakReference<DynamicPredictionDictionaryBase> mDictionary;
+
+    public abstract void onDictionaryReady();
+
+    public void setDictionary(DynamicPredictionDictionaryBase dictionary) {
+        mDictionary = new WeakReference<DynamicPredictionDictionaryBase>(dictionary);
+    }
+
+    public void addToPersonalizationDictionary(
+            final ArrayList<PersonalizationLanguageModelParam> lmParams) {
+        final DynamicPredictionDictionaryBase dictionary = mDictionary == null
+                ? null : mDictionary.get();
+        if (dictionary == null) {
+            return;
+        }
+        for (final PersonalizationLanguageModelParam lmParam : lmParams) {
+            dictionary.addToPersonalizationPredictionDictionary(
+                    lmParam.mWord0, lmParam.mWord1, lmParam.mIsValid);
+        }
+    }
+}