Don't rely on broadcast intent for waking up input method.

Basically this is a copy of Iabef96921dd554ce3768fb18619cefc
for InputMethodManagerService.

As described in JavaDoc of Intent#ACTION_SCREEN_OFF and
Intent#ACTION_SCREEN_ON, one can use those Intents to be
notified when the device becomes non-interactive and
interactive.  IMMS has relied on them to enable and disable
InputConnection between the IME and the application so as not
to allow IMEs to update text when the user does not present.
This is actually our design goal as documented in JavaDoc of
InputMethodManager.

   An IME can never interact with an InputConnection while
   the screen is off.  This is enforced by making all clients
   inactive while the screen is off, and prevents bad IMEs from
   driving the UI when the user can not be aware of its
   behavior.

The goal of this CL is to improve the timeliness of above
mechianism by introducing a direct communication channel from
PowerManagerService to InputMethodManagerService via Notifier.
Actually this is what InputManager has been doing since
Iabef96921dd554ce3768fb18619cefc3230b5fb0.

Reasons behind this change are:

  1. There are several bugreports that imply those Intents can
     dispatch tens of seconds after it is enqueued. This is
     indeed problematic because the user cannot type password
     to unlock their devices until queued
     Intent#ACTION_SCREEN_ON is dispatched. This CL addresses
     such an issue without waiting for figuring out the root
     cause of the delay.
  2. Intent#ACTION_SCREEN_OFF and Intent#ACTION_SCREEN_ON are
     sent as a ordered broadcast, which may not be suitable for
     tasks that require a certain level of timeliness, and what
     IMMS wants is to enable users to start typing immediately
     after the system.

This CL was originally authored by Seigo Nonaka.

Bug: 22423200
Bug: 22555778
Change-Id: I747c37ff6dd8f233faef43f2b5713a4320e848eb
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index dbe8781..64ee5f1 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -114,6 +114,7 @@
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodManagerInternal;
 import android.view.inputmethod.InputMethodSubtype;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
 import android.widget.ArrayAdapter;
@@ -166,6 +167,7 @@
     static final int MSG_UNBIND_METHOD = 3000;
     static final int MSG_BIND_METHOD = 3010;
     static final int MSG_SET_ACTIVE = 3020;
+    static final int MSG_SET_INTERACTIVE = 3030;
     static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 3040;
 
     static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
@@ -394,9 +396,9 @@
     SessionState mEnabledSession;
 
     /**
-     * True if the screen is on.  The value is true initially.
+     * True if the device is currently interactive with user.  The value is true initially.
      */
-    boolean mScreenOn = true;
+    boolean mIsInteractive = true;
 
     int mCurUserActionNotificationSequenceNumber = 0;
 
@@ -492,30 +494,12 @@
     }
 
     class ImmsBroadcastReceiver extends android.content.BroadcastReceiver {
-        private void updateActive() {
-            // Inform the current client of the change in active status
-            if (mCurClient != null && mCurClient.client != null) {
-                executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
-                        MSG_SET_ACTIVE, mScreenOn ? 1 : 0, mCurClient));
-            }
-        }
-
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
-            if (Intent.ACTION_SCREEN_ON.equals(action)) {
-                mScreenOn = true;
-                updateSystemUi(mCurToken, mImeWindowVis, mBackDisposition);
-                updateActive();
-                return;
-            } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
-                mScreenOn = false;
-                updateSystemUi(mCurToken, 0 /* vis */, mBackDisposition);
-                updateActive();
-                return;
-            } else if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
+            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
                 hideInputMethodMenu();
-                // No need to updateActive
+                // No need to update mIsInteractive
                 return;
             } else if (Intent.ACTION_USER_ADDED.equals(action)
                     || Intent.ACTION_USER_REMOVED.equals(action)) {
@@ -817,8 +801,6 @@
         mShowOngoingImeSwitcherForPhones = false;
 
         final IntentFilter broadcastFilter = new IntentFilter();
-        broadcastFilter.addAction(Intent.ACTION_SCREEN_ON);
-        broadcastFilter.addAction(Intent.ACTION_SCREEN_OFF);
         broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
         broadcastFilter.addAction(Intent.ACTION_USER_ADDED);
         broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
@@ -938,6 +920,7 @@
                         }
                     }
                 }, filter);
+        LocalServices.addService(InputMethodManagerInternal.class, new LocalServiceImpl(mHandler));
     }
 
     private void resetDefaultImeLocked(Context context) {
@@ -1379,9 +1362,9 @@
                     + cs.client.asBinder() + " keyguard=" + mCurClientInKeyguard);
 
             // If the screen is on, inform the new client it is active
-            if (mScreenOn) {
+            if (mIsInteractive) {
                 executeOrSendMessage(cs.client, mCaller.obtainMessageIO(
-                        MSG_SET_ACTIVE, mScreenOn ? 1 : 0, cs));
+                        MSG_SET_ACTIVE, mIsInteractive ? 1 : 0, cs));
             }
         }
 
@@ -2851,6 +2834,9 @@
                             + ((ClientState)msg.obj).uid);
                 }
                 return true;
+            case MSG_SET_INTERACTIVE:
+                handleSetInteractive(msg.arg1 != 0);
+                return true;
             case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: {
                 final int sequenceNumber = msg.arg1;
                 final ClientState clientState = (ClientState)msg.obj;
@@ -2874,6 +2860,19 @@
         return false;
     }
 
+    private void handleSetInteractive(final boolean interactive) {
+        synchronized (mMethodMap) {
+            mIsInteractive = interactive;
+            updateSystemUiLocked(mCurToken, interactive ? mImeWindowVis : 0, mBackDisposition);
+
+            // Inform the current client of the change in active status
+            if (mCurClient != null && mCurClient.client != null) {
+                executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
+                        MSG_SET_ACTIVE, mIsInteractive ? 1 : 0, mCurClient));
+            }
+        }
+    }
+
     private boolean chooseNewDefaultIMELocked() {
         final InputMethodInfo imi = InputMethodUtils.getMostApplicableDefaultIME(
                 mSettings.getEnabledInputMethodListLocked());
@@ -3734,6 +3733,22 @@
         }
     }
 
+    private static final class LocalServiceImpl implements InputMethodManagerInternal {
+        @NonNull
+        private final Handler mHandler;
+
+        LocalServiceImpl(@NonNull final Handler handler) {
+            mHandler = handler;
+        }
+
+        @Override
+        public void setInteractive(boolean interactive) {
+            // Do everything in handler so as not to block the caller.
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_INTERACTIVE,
+                    interactive ? 1 : 0, 0));
+        }
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -3784,7 +3799,7 @@
                     + " mInputShown=" + mInputShown);
             p.println("  mCurUserActionNotificationSequenceNumber="
                     + mCurUserActionNotificationSequenceNumber);
-            p.println("  mSystemReady=" + mSystemReady + " mInteractive=" + mScreenOn);
+            p.println("  mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive);
             p.println("  mSettingsObserver=" + mSettingsObserver);
             p.println("  mSwitchingController:");
             mSwitchingController.dump(p);