Merge "Fix some flaky tests."
diff --git a/java/src/com/android/inputmethod/compat/LooperCompatUtils.java b/java/src/com/android/inputmethod/compat/LooperCompatUtils.java
new file mode 100644
index 0000000..d647dbb
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/LooperCompatUtils.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.compat;
+
+import android.os.Looper;
+
+import java.lang.reflect.Method;
+
+/**
+ * Helper to call Looper#quitSafely, which was introduced in API
+ * level 18 (Build.VERSION_CODES.JELLY_BEAN_MR2).
+ *
+ * In unit tests, we create lots of instances of LatinIME, which means we need to clean up
+ * some Loopers lest we leak file descriptors. In normal use on a device though, this is never
+ * necessary (although it does not hurt).
+ */
+public final class LooperCompatUtils {
+    private static final Method METHOD_quitSafely = CompatUtils.getMethod(
+            Looper.class, "quitSafely");
+
+    public static void quitSafely(final Looper looper) {
+        if (null != METHOD_quitSafely) {
+            CompatUtils.invoke(looper, null /* default return value */, METHOD_quitSafely);
+        } else {
+            looper.quit();
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 6d36af7..1f15a97 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -635,6 +635,11 @@
         super.onDestroy();
     }
 
+    @UsedForTesting
+    public void recycle() {
+        mInputLogic.recycle();
+    }
+
     @Override
     public void onConfigurationChanged(final Configuration conf) {
         // If orientation changed while predicting, commit the change
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 7cf8c5e..0754b1f 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -148,6 +148,17 @@
         mInputLogicHandler.reset();
     }
 
+    // Normally this class just gets out of scope after the process ends, but in unit tests, we
+    // create several instances of LatinIME in the same process, which results in several
+    // instances of InputLogic. This cleans up the associated handler so that tests don't leak
+    // handlers.
+    public void recycle() {
+        final InputLogicHandler inputLogicHandler = mInputLogicHandler;
+        mInputLogicHandler = InputLogicHandler.NULL_HANDLER;
+        inputLogicHandler.destroy();
+        mSuggest.mDictionaryFacilitator.closeDictionaries();
+    }
+
     /**
      * React to a string input.
      *
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
index e3b8ab4..64bba68 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
@@ -20,6 +20,7 @@
 import android.os.HandlerThread;
 import android.os.Message;
 
+import com.android.inputmethod.compat.LooperCompatUtils;
 import com.android.inputmethod.latin.InputPointers;
 import com.android.inputmethod.latin.LatinIME;
 import com.android.inputmethod.latin.Suggest;
@@ -80,6 +81,12 @@
         mNonUIThreadHandler.removeCallbacksAndMessages(null);
     }
 
+    // In unit tests, we create several instances of LatinIME, which results in several instances
+    // of InputLogicHandler. To avoid these handlers lingering, we call this.
+    public void destroy() {
+        LooperCompatUtils.quitSafely(mNonUIThreadHandler.getLooper());
+    }
+
     /**
      * Handle a message.
      * @see android.os.Handler.Callback#handleMessage(android.os.Message)
diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
index 1383ff9..e5f111a 100644
--- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java
+++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
@@ -213,13 +213,18 @@
 
     @Override
     protected void tearDown() throws Exception {
+        mLatinIME.onFinishInputView(true);
+        mLatinIME.onFinishInput();
+        runMessages();
         mLatinIME.mHandler.removeAllMessages();
         setBooleanPreference(Settings.PREF_BIGRAM_PREDICTIONS, mPreviousBigramPredictionSettings,
                 true /* defaultValue */);
         setStringPreference(Settings.PREF_AUTO_CORRECTION_THRESHOLD, mPreviousAutoCorrectSetting,
                 DEFAULT_AUTO_CORRECTION_THRESHOLD);
         setDebugMode(false);
+        mLatinIME.recycle();
         super.tearDown();
+        mLatinIME = null;
     }
 
     // We need to run the messages added to the handler from LatinIME. The only way to do