diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 05b1a2ee19af5d321d44d29eff4f2a95c033a9cc..4425d7d256cf873f2c6c01a1732a3cae90fe44b5 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -91,7 +91,8 @@ public final class BinaryDictionary extends Dictionary {
         JniUtils.loadNativeLibrary();
     }
 
-    private static native long openNative(String sourceDir, long dictOffset, long dictSize);
+    private static native long openNative(String sourceDir, long dictOffset, long dictSize,
+            boolean isUpdatable);
     private static native void closeNative(long dict);
     private static native int getProbabilityNative(long dict, int[] word);
     private static native boolean isValidBigramNative(long dict, int[] word1, int[] word2);
@@ -106,7 +107,7 @@ public final class BinaryDictionary extends Dictionary {
     // TODO: Move native dict into session
     private final void loadDictionary(final String path, final long startOffset,
             final long length) {
-        mNativeDict = openNative(path, startOffset, length);
+        mNativeDict = openNative(path, startOffset, length, false /* isUpdatable */);
     }
 
     @Override
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index cbe7ce45132a7cb283173a970638d8c220bb6c51..b856718c6a3995dfa77c0ebaf2cd046bb2a6e6f2 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -40,7 +40,7 @@ class ProximityInfo;
 static void releaseDictBuf(const void *dictBuf, const size_t length, const int fd);
 
 static jlong latinime_BinaryDictionary_open(JNIEnv *env, jclass clazz, jstring sourceDir,
-        jlong dictOffset, jlong dictSize) {
+        jlong dictOffset, jlong dictSize, jboolean isUpdatable) {
     PROF_OPEN;
     PROF_START(66);
     const jsize sourceDirUtf8Length = env->GetStringUTFLength(sourceDir);
@@ -54,7 +54,9 @@ static jlong latinime_BinaryDictionary_open(JNIEnv *env, jclass clazz, jstring s
     int fd = 0;
     void *dictBuf = 0;
     int offset = 0;
-    fd = open(sourceDirChars, O_RDONLY);
+    const bool updatableMmap = (isUpdatable == JNI_TRUE);
+    const int openMode = updatableMmap ? O_RDWR : O_RDONLY;
+    fd = open(sourceDirChars, openMode);
     if (fd < 0) {
         AKLOGE("DICT: Can't open sourceDir. sourceDirChars=%s errno=%d", sourceDirChars, errno);
         return 0;
@@ -63,7 +65,8 @@ static jlong latinime_BinaryDictionary_open(JNIEnv *env, jclass clazz, jstring s
     offset = static_cast<int>(dictOffset) % pagesize;
     int adjDictOffset = static_cast<int>(dictOffset) - offset;
     int adjDictSize = static_cast<int>(dictSize) + offset;
-    dictBuf = mmap(0, adjDictSize, PROT_READ, MAP_PRIVATE, fd, adjDictOffset);
+    const int protMode = updatableMmap ? PROT_READ | PROT_WRITE : PROT_READ;
+    dictBuf = mmap(0, adjDictSize, protMode, MAP_PRIVATE, fd, adjDictOffset);
     if (dictBuf == MAP_FAILED) {
         AKLOGE("DICT: Can't mmap dictionary. errno=%d", errno);
         return 0;
@@ -80,7 +83,8 @@ static jlong latinime_BinaryDictionary_open(JNIEnv *env, jclass clazz, jstring s
         AKLOGE("DICT: dictionary format is unknown, bad magic number");
         releaseDictBuf(static_cast<const char *>(dictBuf) - offset, adjDictSize, fd);
     } else {
-        dictionary = new Dictionary(dictBuf, static_cast<int>(dictSize), fd, offset);
+        dictionary = new Dictionary(
+                dictBuf, static_cast<int>(dictSize), fd, offset, updatableMmap);
     }
     PROF_END(66);
     PROF_CLOSE;
@@ -245,7 +249,7 @@ static void releaseDictBuf(const void *dictBuf, const size_t length, const int f
 static const JNINativeMethod sMethods[] = {
     {
         const_cast<char *>("openNative"),
-        const_cast<char *>("(Ljava/lang/String;JJ)J"),
+        const_cast<char *>("(Ljava/lang/String;JJZ)J"),
         reinterpret_cast<void *>(latinime_BinaryDictionary_open)
     },
     {
diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_info.h b/native/jni/src/suggest/core/dictionary/binary_dictionary_info.h
index c16b5310d989f2cabd992a5b0e3cb5842e487faf..399817981f42018d1c80eee4e0c3431323eaa599 100644
--- a/native/jni/src/suggest/core/dictionary/binary_dictionary_info.h
+++ b/native/jni/src/suggest/core/dictionary/binary_dictionary_info.h
@@ -30,9 +30,9 @@ class BinaryDictionaryHeader;
 class BinaryDictionaryInfo {
  public:
     BinaryDictionaryInfo(const uint8_t *const dictBuf, const int dictSize, const int mmapFd,
-            const int dictBufOffset)
+            const int dictBufOffset, const bool isUpdatable)
             : mDictBuf(dictBuf), mDictSize(dictSize), mMmapFd(mmapFd),
-              mDictBufOffset(dictBufOffset),
+              mDictBufOffset(dictBufOffset), mIsUpdatable(isUpdatable),
               mDictionaryFormat(BinaryDictionaryFormat::detectFormatVersion(mDictBuf, mDictSize)),
               mDictionaryHeader(this), mDictRoot(mDictBuf + mDictionaryHeader.getSize()) {}
 
@@ -75,6 +75,7 @@ class BinaryDictionaryInfo {
     const int mDictSize;
     const int mMmapFd;
     const int mDictBufOffset;
+    const bool mIsUpdatable;
     const BinaryDictionaryFormat::FORMAT_VERSION mDictionaryFormat;
     const BinaryDictionaryHeader mDictionaryHeader;
     const uint8_t *const mDictRoot;
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp
index b92e5644cf303bbe780fd7e5218e193c0d1f6d75..028b61506ad2e84ccdb1781fb8b89f2ac82297c9 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.cpp
+++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp
@@ -32,9 +32,9 @@
 
 namespace latinime {
 
-Dictionary::Dictionary(void *dict, int dictSize, int mmapFd, int dictBufOffset)
-        : mBinaryDictionaryInfo(
-                static_cast<const uint8_t *>(dict), dictSize, mmapFd, dictBufOffset),
+Dictionary::Dictionary(void *dict, int dictSize, int mmapFd, int dictBufOffset, bool isUpdatable)
+        : mBinaryDictionaryInfo(static_cast<const uint8_t *>(dict), dictSize, mmapFd,
+                dictBufOffset, isUpdatable),
           mBigramDictionary(new BigramDictionary(&mBinaryDictionaryInfo)),
           mGestureSuggest(new Suggest(GestureSuggestPolicyFactory::getGestureSuggestPolicy())),
           mTypingSuggest(new Suggest(TypingSuggestPolicyFactory::getTypingSuggestPolicy())) {
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h
index 12884a43064ec8d065ccde45506731221865905e..afd0818414a6c8521e70ddece281e1ce8962d5fa 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.h
+++ b/native/jni/src/suggest/core/dictionary/dictionary.h
@@ -52,7 +52,7 @@ class Dictionary {
     static const int KIND_FLAG_POSSIBLY_OFFENSIVE = 0x80000000;
     static const int KIND_FLAG_EXACT_MATCH = 0x40000000;
 
-    Dictionary(void *dict, int dictSize, int mmapFd, int dictBufOffset);
+    Dictionary(void *dict, int dictSize, int mmapFd, int dictBufOffset, bool isUpdatable);
 
     int getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession,
             int *xcoordinates, int *ycoordinates, int *times, int *pointerIds, int *inputCodePoints,