Allow apps to hide the soft keyboard even in a transient state.

Since I69b88989ae4d0fe056e9ab8d67d0a955dd10e6d9, we have
asserted that the following test cases can pass with CTS.

    assertTrue(imm.showSoftInput(
            view, InputMethodManager.SHOW_IMPLICIT));
    assertTrue(imm.hideSoftInputFromWindow(token, 0));

This CL fixes the test failure caused by
I33dc6278fd892f26e56352722bf9449b8b102030 in the above
CTS case.

Note that the test failure occurs only when the application
tries to close the software keyboard during IMMS#mInputShown
and IMMS#mImeWindowVis are in a transiently inconsistent
state.

Bug: 21727232
Change-Id: I195a9f9644583cb1172f48801e87273ad8def850
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index a5d536e..2f153a0 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -2112,8 +2112,23 @@
             if (DEBUG) Slog.v(TAG, "Not hiding: forced show not cancelled by not-always hide");
             return false;
         }
+
+        // There is a chance that IMM#hideSoftInput() is called in a transient state where
+        // IMMS#InputShown is already updated to be true whereas IMMS#mImeWindowVis is still waiting
+        // to be updated with the new value sent from IME process.  Even in such a transient state
+        // historically we have accepted an incoming call of IMM#hideSoftInput() from the
+        // application process as a valid request, and have even promised such a behavior with CTS
+        // since Android Eclair.  That's why we need to accept IMM#hideSoftInput() even when only
+        // IMMS#InputShown indicates that the software keyboard is shown.
+        // TODO: Clean up, IMMS#mInputShown, IMMS#mImeWindowVis and mShowRequested.
+        final boolean shouldHideSoftInput = (mCurMethod != null) && (mInputShown ||
+                (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
         boolean res;
-        if ((mImeWindowVis & InputMethodService.IME_ACTIVE) != 0 && mCurMethod != null) {
+        if (shouldHideSoftInput) {
+            // The IME will report its visible state again after the following message finally
+            // delivered to the IME process as an IPC.  Hence the inconsistency between
+            // IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in
+            // the final state.
             executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
                     MSG_HIDE_SOFT_INPUT, mCurMethod, resultReceiver));
             res = true;