Merge "Fix Possible NPE"
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index 9997ff4..5e68c70 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -301,7 +301,9 @@
             final int xmlId = mResources.getIdentifier(keyboardLayoutSetName, "xml", packageName);
             try {
                 parseKeyboardLayoutSet(mResources, xmlId);
-            } catch (final Exception e) {
+            } catch (final IOException e) {
+                throw new RuntimeException(e.getMessage() + " in " + keyboardLayoutSetName, e);
+            } catch (final XmlPullParserException e) {
                 throw new RuntimeException(e.getMessage() + " in " + keyboardLayoutSetName, e);
             }
             return new KeyboardLayoutSet(mContext, mParams);
@@ -311,8 +313,8 @@
                 throws XmlPullParserException, IOException {
             final XmlResourceParser parser = res.getXml(resId);
             try {
-                int event;
-                while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+                while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                    final int event = parser.next();
                     if (event == XmlPullParser.START_TAG) {
                         final String tag = parser.getName();
                         if (TAG_KEYBOARD_SET.equals(tag)) {
@@ -329,8 +331,8 @@
 
         private void parseKeyboardLayoutSetContent(final XmlPullParser parser)
                 throws XmlPullParserException, IOException {
-            int event;
-            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                final int event = parser.next();
                 if (event == XmlPullParser.START_TAG) {
                     final String tag = parser.getName();
                     if (TAG_ELEMENT.equals(tag)) {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index 34f26bc..be178f5 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -164,10 +164,10 @@
             parseKeyboard(parser);
         } catch (XmlPullParserException e) {
             Log.w(BUILDER_TAG, "keyboard XML parse error", e);
-            throw new IllegalArgumentException(e);
+            throw new IllegalArgumentException(e.getMessage(), e);
         } catch (IOException e) {
             Log.w(BUILDER_TAG, "keyboard XML parse error", e);
-            throw new RuntimeException(e);
+            throw new RuntimeException(e.getMessage(), e);
         } finally {
             parser.close();
         }
@@ -210,8 +210,8 @@
     private void parseKeyboard(final XmlPullParser parser)
             throws XmlPullParserException, IOException {
         if (DEBUG) startTag("<%s> %s", TAG_KEYBOARD, mParams.mId);
-        int event;
-        while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+        while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+            final int event = parser.next();
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
                 if (TAG_KEYBOARD.equals(tag)) {
@@ -303,8 +303,8 @@
 
     private void parseKeyboardContent(final XmlPullParser parser, final boolean skip)
             throws XmlPullParserException, IOException {
-        int event;
-        while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+        while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+            final int event = parser.next();
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
                 if (TAG_ROW.equals(tag)) {
@@ -358,8 +358,8 @@
 
     private void parseRowContent(final XmlPullParser parser, final KeyboardRow row,
             final boolean skip) throws XmlPullParserException, IOException {
-        int event;
-        while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+        while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+            final int event = parser.next();
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
                 if (TAG_KEY.equals(tag)) {
@@ -506,8 +506,8 @@
     private void parseMerge(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
             throws XmlPullParserException, IOException {
         if (DEBUG) startTag("<%s>", TAG_MERGE);
-        int event;
-        while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+        while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+            final int event = parser.next();
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
                 if (TAG_MERGE.equals(tag)) {
@@ -539,8 +539,8 @@
             final boolean skip) throws XmlPullParserException, IOException {
         if (DEBUG) startTag("<%s> %s", TAG_SWITCH, mParams.mId);
         boolean selected = false;
-        int event;
-        while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+        while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+            final int event = parser.next();
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
                 if (TAG_CASE.equals(tag)) {
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index ae2ee57..fd81d13 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.text.TextUtils;
+import android.util.Log;
 
 import com.android.inputmethod.keyboard.ProximityInfo;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
@@ -31,6 +32,7 @@
  * be searched for suggestions and valid words.
  */
 public class ExpandableDictionary extends Dictionary {
+    private static final String TAG = ExpandableDictionary.class.getSimpleName();
     /**
      * The weight to give to a word if it's length is the same as the number of typed characters.
      */
@@ -551,8 +553,13 @@
         // word. We do want however to return the correct case for the right hand side.
         // So we want to squash the case of the left hand side, and preserve that of the right
         // hand side word.
-        Node firstWord = searchWord(mRoots, word1.toLowerCase(), 0, null);
-        Node secondWord = searchWord(mRoots, word2, 0, null);
+        final String word1Lower = word1.toLowerCase();
+        if (TextUtils.isEmpty(word1Lower) || TextUtils.isEmpty(word2)) {
+            Log.e(TAG, "Invalid bigram pair: " + word1 + ", " + word1Lower + ", " + word2);
+            return frequency;
+        }
+        final Node firstWord = searchWord(mRoots, word1Lower, 0, null);
+        final Node secondWord = searchWord(mRoots, word2, 0, null);
         LinkedList<NextWord> bigrams = firstWord.mNGrams;
         if (bigrams == null || bigrams.size() == 0) {
             firstWord.mNGrams = CollectionUtils.newLinkedList();
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 7bd0981..92b68dc 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -803,10 +803,6 @@
 
     @Override
     public void onWindowHidden() {
-        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
-            ResearchLogger.latinIME_onWindowHidden(mLastSelectionStart, mLastSelectionEnd,
-                    getCurrentInputConnection());
-        }
         super.onWindowHidden();
         final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
         if (mainKeyboardView != null) {
@@ -834,8 +830,10 @@
         // Remove pending messages related to update suggestions
         mHandler.cancelUpdateSuggestionStrip();
         resetComposingState(true /* alsoResetLastComposedWord */);
+        // Notify ResearchLogger
         if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
-            ResearchLogger.getInstance().latinIME_onFinishInputViewInternal();
+            ResearchLogger.latinIME_onFinishInputViewInternal(finishingInput, mLastSelectionStart,
+                    mLastSelectionEnd, getCurrentInputConnection());
         }
     }
 
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
index a38a226..aa4c033 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -1122,10 +1122,6 @@
         }
     }
 
-    public void latinIME_onFinishInputViewInternal() {
-        stop();
-    }
-
     /**
      * Log a change in preferences.
      *
@@ -1208,16 +1204,22 @@
     }
 
     /**
-     * Log a call to LatinIME.onWindowHidden().
+     * The IME is finishing; it is either being destroyed, or is about to be hidden.
      *
      * UserAction: The user has performed an action that has caused the IME to be closed.  They may
      * have focused on something other than a text field, or explicitly closed it.
      */
-    private static final LogStatement LOGSTATEMENT_LATINIME_ONWINDOWHIDDEN =
-            new LogStatement("LatinIMEOnWindowHidden", false, false, "isTextTruncated", "text");
-    public static void latinIME_onWindowHidden(final int savedSelectionStart,
-            final int savedSelectionEnd, final InputConnection ic) {
-        if (ic != null) {
+    private static final LogStatement LOGSTATEMENT_LATINIME_ONFINISHINPUTVIEWINTERNAL =
+            new LogStatement("LatinIMEOnFinishInputViewInternal", false, false, "isTextTruncated",
+                    "text");
+    public static void latinIME_onFinishInputViewInternal(final boolean finishingInput,
+            final int savedSelectionStart, final int savedSelectionEnd, final InputConnection ic) {
+        // The finishingInput flag is set in InputMethodService.  It is true if called from
+        // doFinishInput(), which can be called as part of doStartInput().  This can happen at times
+        // when the IME is not closing, such as when powering up.  The finishinInput flag is false
+        // if called from finishViews(), which is called from hideWindow() and onDestroy().  These
+        // are the situations in which we want to finish up the researchLog.
+        if (ic != null && !finishingInput) {
             final boolean isTextTruncated;
             final String text;
             if (LOG_FULL_TEXTVIEW_CONTENTS) {
@@ -1261,8 +1263,8 @@
             // Assume that OUTPUT_ENTIRE_BUFFER is only true when we don't care about privacy (e.g.
             // during a live user test), so the normal isPotentiallyPrivate and
             // isPotentiallyRevealing flags do not apply
-            researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_ONWINDOWHIDDEN, isTextTruncated,
-                    text);
+            researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_ONFINISHINPUTVIEWINTERNAL,
+                    isTextTruncated, text);
             researchLogger.commitCurrentLogUnit();
             getInstance().stop();
         }
diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
index 30ca3f1..3c482ca 100644
--- a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
+++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
@@ -43,9 +43,12 @@
 }
 
 static JNINativeMethod sMethods[] = {
-    {"setProximityInfoNative", "(Ljava/lang/String;IIIII[II[I[I[I[I[I[F[F[F)J",
-            reinterpret_cast<void *>(latinime_Keyboard_setProximityInfo)},
-    {"releaseProximityInfoNative", "(J)V", reinterpret_cast<void *>(latinime_Keyboard_release)}
+    {const_cast<char *>("setProximityInfoNative"),
+     const_cast<char *>("(Ljava/lang/String;IIIII[II[I[I[I[I[I[F[F[F)J"),
+     reinterpret_cast<void *>(latinime_Keyboard_setProximityInfo)},
+    {const_cast<char *>("releaseProximityInfoNative"),
+     const_cast<char *>("(J)V"),
+     reinterpret_cast<void *>(latinime_Keyboard_release)}
 };
 
 int register_ProximityInfo(JNIEnv *env) {
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index 9321c4b..11fa3da 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -280,19 +280,27 @@
 }
 
 static JNINativeMethod sMethods[] = {
-    {"openNative", "(Ljava/lang/String;JJ)J",
-            reinterpret_cast<void *>(latinime_BinaryDictionary_open)},
-    {"closeNative", "(J)V", reinterpret_cast<void *>(latinime_BinaryDictionary_close)},
-    {"getSuggestionsNative", "(JJJ[I[I[I[I[IIIZ[IZ[I[I[I[I)I",
-            reinterpret_cast<void *>(latinime_BinaryDictionary_getSuggestions)},
-    {"getProbabilityNative", "(J[I)I",
-            reinterpret_cast<void *>(latinime_BinaryDictionary_getProbability)},
-    {"isValidBigramNative", "(J[I[I)Z",
-            reinterpret_cast<void *>(latinime_BinaryDictionary_isValidBigram)},
-    {"calcNormalizedScoreNative", "([I[II)F",
-            reinterpret_cast<void *>(latinime_BinaryDictionary_calcNormalizedScore)},
-    {"editDistanceNative", "([I[I)I",
-            reinterpret_cast<void *>(latinime_BinaryDictionary_editDistance)}
+    {const_cast<char *>("openNative"),
+     const_cast<char *>("(Ljava/lang/String;JJ)J"),
+     reinterpret_cast<void *>(latinime_BinaryDictionary_open)},
+    {const_cast<char *>("closeNative"),
+     const_cast<char *>("(J)V"),
+     reinterpret_cast<void *>(latinime_BinaryDictionary_close)},
+    {const_cast<char *>("getSuggestionsNative"),
+     const_cast<char *>("(JJJ[I[I[I[I[IIIZ[IZ[I[I[I[I)I"),
+     reinterpret_cast<void *>(latinime_BinaryDictionary_getSuggestions)},
+    {const_cast<char *>("getProbabilityNative"),
+     const_cast<char *>("(J[I)I"),
+     reinterpret_cast<void *>(latinime_BinaryDictionary_getProbability)},
+    {const_cast<char *>("isValidBigramNative"),
+     const_cast<char *>("(J[I[I)Z"),
+     reinterpret_cast<void *>(latinime_BinaryDictionary_isValidBigram)},
+    {const_cast<char *>("calcNormalizedScoreNative"),
+     const_cast<char *>("([I[II)F"),
+     reinterpret_cast<void *>(latinime_BinaryDictionary_calcNormalizedScore)},
+    {const_cast<char *>("editDistanceNative"),
+     const_cast<char *>("([I[I)I"),
+     reinterpret_cast<void *>(latinime_BinaryDictionary_editDistance)}
 };
 
 int register_BinaryDictionary(JNIEnv *env) {
diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp
index 9b39245..dfe3b09 100644
--- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp
+++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp
@@ -48,12 +48,15 @@
 }
 
 static JNINativeMethod sMethods[] = {
-    {"setDicTraverseSessionNative", "(Ljava/lang/String;)J",
-            reinterpret_cast<void *>(latinime_setDicTraverseSession)},
-    {"initDicTraverseSessionNative", "(JJ[II)V",
-            reinterpret_cast<void *>(latinime_initDicTraverseSession)},
-    {"releaseDicTraverseSessionNative", "(J)V",
-            reinterpret_cast<void *>(latinime_releaseDicTraverseSession)}
+    {const_cast<char *>("setDicTraverseSessionNative"),
+     const_cast<char *>("(Ljava/lang/String;)J"),
+     reinterpret_cast<void *>(latinime_setDicTraverseSession)},
+    {const_cast<char *>("initDicTraverseSessionNative"),
+     const_cast<char *>("(JJ[II)V"),
+     reinterpret_cast<void *>(latinime_initDicTraverseSession)},
+    {const_cast<char *>("releaseDicTraverseSessionNative"),
+     const_cast<char *>("(J)V"),
+     reinterpret_cast<void *>(latinime_releaseDicTraverseSession)}
 };
 
 int register_DicTraverseSession(JNIEnv *env) {