Move notifyUserAction() to IInputMethodPrivilegedOperations

This CL re-implements the way to propagate user action on an IME to
InputMethodManagerService (IMMS) so that we can dynamically update IME
Subtype rotation list discussed as requested in Bug 7043015.

It turns out that my previous CLs [1][2][3][4] are unnecessarily
complex because I tried to monitor user behavior in the IME client
process rather than in the IME process.  In the end, I ended up
introducing a sequence number protocol for the sake of performance
with a ton of complexity.

This could have been implemented in a much safer and simpler way by
sending user action signals from the IME process to IMMS, because

 A. IME already knows when it switches to a new subtype. IME needs to
    send a signal only once per subtype change.  There is no need to
    use sequence counter.
 B. Malicious IME client is unable to disturb IME rotation list by
    sending a fake signal because the IPC endpoint is no longer exposed
    to IME client processes.

In case there remain some applitations that still call this hidden API
via reflection without gracefully handling exceptions, this CL keeps
InputMethodManager.notifyUserAction() as a stub method so as not to
break such applications.

 [1]: I11ed9a767588f8080753cd9bce011dac7db579ad
      d7443c83ceae0bdd20d68bf84648cf3b40115d85
 [2]: I7f3e13a7226ef0dceee82b67e8a0d8536f7e9807
      2a6a8d2fbb063c84e388c185402c4ca788618c72
 [3]: I19ad8542659bc092b92ee13eb9f1d68ddd4b815a
      b56c6c721fc01fba8e36632d8e28f5123831abc5
 [4]: I03fa436df0a5e348b3f93170aab3a8ac5a7e1677
      c21ccc151631663d71230a3c1c756d94b575ab9e

Bug: 113177698
Fix: 114159783
Test: Manually verified as follows
  1. Build and flush aosp_taimen-userdebug
  2. make -j SoftKeyboard
  3. adb install -r $OUT/system/app/SoftKeyboard/SoftKeyboard.apk
  4. adb shell ime enable com.example.android.softkeyboard/.SoftKeyboard
  5. Open AOSP Keyboard settings
  6. Enable "English (US)", "French", and "German"
  7. Open SoftKeyboard settings
  8. Enable "English (United States)", "English (GB)"
  9. Open the Dialer app and tap the top edit field.
 10. Make sure that the IME layout rotation order when tapping the
     globe key will be updated only when you tap the keyboard to enter
     some character.
 11. Also confirm it with "adb shell dumpsys input_method" by checking
     "mSwitchingController:" section there.
Change-Id: Icc1f9c7f530f0144ecfd460e86114e109ae0044e
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index 185215a..b0fca00 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -252,4 +252,12 @@
         return;
     }
 
+    /**
+     * Called when the user took some actions that should be taken into consideration to update the
+     * MRU list for input method rotation.
+     *
+     * @hide
+     */
+    public void notifyUserActionIfNecessary() {
+    }
 }
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index aa5cf09..2d12b86 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -22,6 +22,7 @@
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import android.annotation.AnyThread;
 import android.annotation.CallSuper;
 import android.annotation.DrawableRes;
 import android.annotation.IntDef;
@@ -79,6 +80,7 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.inputmethod.IInputContentUriToken;
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
@@ -402,6 +404,10 @@
     @BackDispositionMode
     int mBackDisposition;
 
+    private Object mLock = new Object();
+    @GuardedBy("mLock")
+    private boolean mNotifyUserActionSent;
+
     /**
      * {@code true} when the previous IME had non-empty inset at the bottom of the screen and we
      * have not shown our own window yet.  In this situation, the previous inset continues to be
@@ -594,7 +600,7 @@
         @MainThread
         @Override
         public void changeInputMethodSubtype(InputMethodSubtype subtype) {
-            onCurrentInputMethodSubtypeChanged(subtype);
+            dispatchOnCurrentInputMethodSubtypeChanged(subtype);
         }
     }
 
@@ -2792,6 +2798,13 @@
         }
     }
 
+    private void dispatchOnCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
+        synchronized (mLock) {
+            mNotifyUserActionSent = false;
+        }
+        onCurrentInputMethodSubtypeChanged(newSubtype);
+    }
+
     // TODO: Handle the subtype change event
     /**
      * Called when the subtype was changed.
@@ -2848,6 +2861,22 @@
     }
 
     /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @AnyThread
+    @Override
+    public final void notifyUserActionIfNecessary() {
+        synchronized (mLock) {
+            if (mNotifyUserActionSent) {
+                return;
+            }
+            mPrivOps.notifyUserActionAsync();
+            mNotifyUserActionSent = true;
+        }
+    }
+
+    /**
      * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
      * permission to the content.
      *
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 84d1b70..c51c5e2 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -354,28 +354,6 @@
     int mCursorCandEnd;
 
     /**
-     * Represents an invalid action notification sequence number.
-     * {@link com.android.server.InputMethodManagerService} always issues a positive integer for
-     * action notification sequence numbers. Thus {@code -1} is guaranteed to be different from any
-     * valid sequence number.
-     */
-    private static final int NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER = -1;
-    /**
-     * The next sequence number that is to be sent to
-     * {@link com.android.server.InputMethodManagerService} via
-     * {@link IInputMethodManager#notifyUserAction(int)} at once when a user action is observed.
-     */
-    private int mNextUserActionNotificationSequenceNumber =
-            NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER;
-
-    /**
-     * The last sequence number that is already sent to
-     * {@link com.android.server.InputMethodManagerService}.
-     */
-    private int mLastSentUserActionNotificationSequenceNumber =
-            NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER;
-
-    /**
      * The instance that has previously been sent to the input method.
      */
     private CursorAnchorInfo mCursorAnchorInfo = null;
@@ -421,7 +399,6 @@
     static final int MSG_SEND_INPUT_EVENT = 5;
     static final int MSG_TIMEOUT_INPUT_EVENT = 6;
     static final int MSG_FLUSH_INPUT_EVENT = 7;
-    static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 9;
     static final int MSG_REPORT_FULLSCREEN_MODE = 10;
 
     private static boolean isAutofillUIShowing(View servedView) {
@@ -558,12 +535,6 @@
                     finishedInputEvent(msg.arg1, false, false);
                     return;
                 }
-                case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: {
-                    synchronized (mH) {
-                        mNextUserActionNotificationSequenceNumber = msg.arg1;
-                    }
-                    return;
-                }
                 case MSG_REPORT_FULLSCREEN_MODE: {
                     final boolean fullscreen = msg.arg1 != 0;
                     InputConnection ic = null;
@@ -606,11 +577,6 @@
         }
 
         @Override
-        protected void onUserAction() {
-            mParentInputMethodManager.notifyUserAction();
-        }
-
-        @Override
         public String toString() {
             return "ControlledInputConnectionWrapper{"
                     + "connection=" + getInputConnection()
@@ -657,12 +623,6 @@
         }
 
         @Override
-        public void setUserActionNotificationSequenceNumber(int sequenceNumber) {
-            mH.obtainMessage(MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER, sequenceNumber, 0)
-                    .sendToTarget();
-        }
-
-        @Override
         public void reportFullscreenMode(boolean fullscreen) {
             mH.obtainMessage(MSG_REPORT_FULLSCREEN_MODE, fullscreen ? 1 : 0, 0)
                     .sendToTarget();
@@ -1355,8 +1315,6 @@
                     mBindSequence = res.sequence;
                     mCurMethod = res.method;
                     mCurId = res.id;
-                    mNextUserActionNotificationSequenceNumber =
-                            res.userActionNotificationSequenceNumber;
                 } else if (res.channel != null && res.channel != mCurChannel) {
                     res.channel.dispose();
                 }
@@ -2190,37 +2148,16 @@
 
     /**
      * Notify that a user took some action with this input method.
+     *
+     * @deprecated Just kept to avoid possible app compat issue.
      * @hide
      */
+    @Deprecated
     @UnsupportedAppUsage(trackingBug = 114740982, maxTargetSdk = Build.VERSION_CODES.P)
     public void notifyUserAction() {
-        synchronized (mH) {
-            if (mLastSentUserActionNotificationSequenceNumber ==
-                    mNextUserActionNotificationSequenceNumber) {
-                if (DEBUG) {
-                    Log.w(TAG, "Ignoring notifyUserAction as it has already been sent."
-                            + " mLastSentUserActionNotificationSequenceNumber: "
-                            + mLastSentUserActionNotificationSequenceNumber
-                            + " mNextUserActionNotificationSequenceNumber: "
-                            + mNextUserActionNotificationSequenceNumber);
-                }
-                return;
-            }
-            try {
-                if (DEBUG) {
-                    Log.w(TAG, "notifyUserAction: "
-                            + " mLastSentUserActionNotificationSequenceNumber: "
-                            + mLastSentUserActionNotificationSequenceNumber
-                            + " mNextUserActionNotificationSequenceNumber: "
-                            + mNextUserActionNotificationSequenceNumber);
-                }
-                mService.notifyUserAction(mNextUserActionNotificationSequenceNumber);
-                mLastSentUserActionNotificationSequenceNumber =
-                        mNextUserActionNotificationSequenceNumber;
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
+        Log.w(TAG, "notifyUserAction() is a hidden method, which is now just a stub method"
+                + " that does nothing.  Leave comments in b.android.com/114740982 if your "
+                + " application still depends on the previous behavior of this method.");
     }
 
     /**
@@ -2421,10 +2358,6 @@
                 + " mCursorSelEnd=" + mCursorSelEnd
                 + " mCursorCandStart=" + mCursorCandStart
                 + " mCursorCandEnd=" + mCursorCandEnd);
-        p.println("  mNextUserActionNotificationSequenceNumber="
-                + mNextUserActionNotificationSequenceNumber
-                + " mLastSentUserActionNotificationSequenceNumber="
-                + mLastSentUserActionNotificationSequenceNumber);
     }
 
     /**
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index e998a52..c32894d 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -39,4 +39,6 @@
     boolean switchToPreviousInputMethod();
     boolean switchToNextInputMethod(boolean onlyCurrentIme);
     boolean shouldOfferSwitchingToNextInputMethod();
+
+    oneway void notifyUserActionAsync();
 }
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 7891540..f0e8dc5 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -346,4 +346,20 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Calls {@link IInputMethodPrivilegedOperations#notifyUserActionAsync()}
+     */
+    @AnyThread
+    public void notifyUserActionAsync() {
+        final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+        if (ops == null) {
+            return;
+        }
+        try {
+            ops.notifyUserActionAsync();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index d4c4ab3..6f9c87a 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -110,12 +110,6 @@
 
     abstract protected boolean isActive();
 
-    /**
-     * Called when the user took some actions that should be taken into consideration to update the
-     * LRU list for input method rotation.
-     */
-    abstract protected void onUserAction();
-
     public void getTextAfterCursor(int length, int flags, int seq, IInputContextCallback callback) {
         dispatchMessage(obtainMessageIISC(DO_GET_TEXT_AFTER_CURSOR, length, flags, seq, callback));
     }
@@ -342,7 +336,6 @@
                     return;
                 }
                 ic.commitText((CharSequence)msg.obj, msg.arg1);
-                onUserAction();
                 return;
             }
             case DO_SET_SELECTION: {
@@ -397,7 +390,6 @@
                     return;
                 }
                 ic.setComposingText((CharSequence)msg.obj, msg.arg1);
-                onUserAction();
                 return;
             }
             case DO_SET_COMPOSING_REGION: {
@@ -437,7 +429,6 @@
                     return;
                 }
                 ic.sendKeyEvent((KeyEvent)msg.obj);
-                onUserAction();
                 return;
             }
             case DO_CLEAR_META_KEY_STATES: {
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index 5c6dea8..2618356 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -27,6 +27,5 @@
     // unbindReason corresponds to InputMethodClient.UnbindReason.
     void onUnbindMethod(int sequence, int unbindReason);
     void setActive(boolean active, boolean fullscreen);
-    void setUserActionNotificationSequenceNumber(int sequenceNumber);
     void reportFullscreenMode(boolean fullscreen);
 }
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 5afc53f..543f4a5 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -79,6 +79,4 @@
     // This is kept due to @UnsupportedAppUsage.
     // TODO(Bug 113914148): Consider removing this.
     int getInputMethodWindowVisibleHeight();
-
-    oneway void notifyUserAction(int sequenceNumber);
 }
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 9d4383f..e03c1f8 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -171,20 +171,13 @@
      */
     public final int sequence;
 
-    /**
-     * Sequence number of user action notification.
-     */
-    public final int userActionNotificationSequenceNumber;
-
     public InputBindResult(@ResultCode int _result,
-            IInputMethodSession _method, InputChannel _channel,
-            String _id, int _sequence, int _userActionNotificationSequenceNumber) {
+            IInputMethodSession _method, InputChannel _channel, String _id, int _sequence) {
         result = _result;
         method = _method;
         channel = _channel;
         id = _id;
         sequence = _sequence;
-        userActionNotificationSequenceNumber = _userActionNotificationSequenceNumber;
     }
 
     InputBindResult(Parcel source) {
@@ -197,14 +190,12 @@
         }
         id = source.readString();
         sequence = source.readInt();
-        userActionNotificationSequenceNumber = source.readInt();
     }
 
     @Override
     public String toString() {
         return "InputBindResult{result=" + getResultString() + " method="+ method + " id=" + id
                 + " sequence=" + sequence
-                + " userActionNotificationSequenceNumber=" + userActionNotificationSequenceNumber
                 + "}";
     }
 
@@ -226,7 +217,6 @@
         }
         dest.writeString(id);
         dest.writeInt(sequence);
-        dest.writeInt(userActionNotificationSequenceNumber);
     }
 
     /**
@@ -285,7 +275,7 @@
     }
 
     private static InputBindResult error(@ResultCode int result) {
-        return new InputBindResult(result, null, null, null, -1, -1);
+        return new InputBindResult(result, null, null, null, -1);
     }
 
     /**
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index 5b65bbe..3bd3072 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -371,6 +371,7 @@
     public boolean commitText(CharSequence text, int newCursorPosition) {
         try {
             mIInputContext.commitText(text, newCursorPosition);
+            notifyUserActionIfNecessary();
             return true;
         } catch (RemoteException e) {
             return false;
@@ -378,6 +379,16 @@
     }
 
     @AnyThread
+    private void notifyUserActionIfNecessary() {
+        final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+        if (inputMethodService == null) {
+            // This basically should not happen, because it's the the caller of this method.
+            return;
+        }
+        inputMethodService.notifyUserActionIfNecessary();
+    }
+
+    @AnyThread
     public boolean commitCompletion(CompletionInfo text) {
         if (isMethodMissing(MissingMethodFlags.COMMIT_CORRECTION)) {
             // This method is not implemented.
@@ -449,6 +460,7 @@
     public boolean setComposingText(CharSequence text, int newCursorPosition) {
         try {
             mIInputContext.setComposingText(text, newCursorPosition);
+            notifyUserActionIfNecessary();
             return true;
         } catch (RemoteException e) {
             return false;
@@ -489,6 +501,7 @@
     public boolean sendKeyEvent(KeyEvent event) {
         try {
             mIInputContext.sendKeyEvent(event);
+            notifyUserActionIfNecessary();
             return true;
         } catch (RemoteException e) {
             return false;
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 93d023d..e435a4f 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -210,7 +210,6 @@
     static final int MSG_BIND_CLIENT = 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_REPORT_FULLSCREEN_MODE = 3045;
 
     static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
@@ -598,8 +597,6 @@
      */
     boolean mIsInteractive = true;
 
-    int mCurUserActionNotificationSequenceNumber = 0;
-
     int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
 
     /**
@@ -1822,7 +1819,7 @@
         }
         return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
                 session.session, (session.channel != null ? session.channel.dup() : null),
-                mCurId, mCurSeq, mCurUserActionNotificationSequenceNumber);
+                mCurId, mCurSeq);
     }
 
     @GuardedBy("mMethodMap")
@@ -1881,8 +1878,7 @@
                     requestClientSessionLocked(cs);
                     return new InputBindResult(
                             InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
-                            null, null, mCurId, mCurSeq,
-                            mCurUserActionNotificationSequenceNumber);
+                            null, null, mCurId, mCurSeq);
                 } else if (SystemClock.uptimeMillis()
                         < (mLastBindTime+TIME_TO_RECONNECT)) {
                     // In this case we have connected to the service, but
@@ -1894,8 +1890,7 @@
                     // to see if we can get back in touch with the service.
                     return new InputBindResult(
                             InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
-                            null, null, mCurId, mCurSeq,
-                            mCurUserActionNotificationSequenceNumber);
+                            null, null, mCurId, mCurSeq);
                 } else {
                     EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
                             mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0);
@@ -1917,8 +1912,7 @@
             // party code.
             return new InputBindResult(
                     InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
-                    null, null, mCurMethodId, mCurSeq,
-                    mCurUserActionNotificationSequenceNumber);
+                    null, null, mCurMethodId, mCurSeq);
         }
 
         InputMethodInfo info = mMethodMap.get(mCurMethodId);
@@ -1946,8 +1940,7 @@
             }
             return new InputBindResult(
                     InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
-                    null, null, mCurId, mCurSeq,
-                    mCurUserActionNotificationSequenceNumber);
+                    null, null, mCurId, mCurSeq);
         }
         mCurIntent = null;
         Slog.w(TAG, "Failure connecting to input method service: " + mCurIntent);
@@ -2754,7 +2747,7 @@
                     }
                     return new InputBindResult(
                             InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
-                            null, null, null, -1, -1);
+                            null, null, null, -1);
                 }
                 mCurFocusedWindow = windowToken;
                 mCurFocusedWindowSoftInputMode = softInputMode;
@@ -3172,17 +3165,16 @@
         mWindowManagerInternal.clearLastInputMethodWindowForTransition();
     }
 
-    @Override
-    public void notifyUserAction(int sequenceNumber) {
+    @BinderThread
+    private void notifyUserAction(@NonNull IBinder token) {
         if (DEBUG) {
-            Slog.d(TAG, "Got the notification of a user action. sequenceNumber:" + sequenceNumber);
+            Slog.d(TAG, "Got the notification of a user action.");
         }
         synchronized (mMethodMap) {
-            if (mCurUserActionNotificationSequenceNumber != sequenceNumber) {
+            if (mCurToken != token) {
                 if (DEBUG) {
-                    Slog.d(TAG, "Ignoring the user action notification due to the sequence number "
-                            + "mismatch. expected:" + mCurUserActionNotificationSequenceNumber
-                            + " actual: " + sequenceNumber);
+                    Slog.d(TAG, "Ignoring the user action notification from IMEs that are no longer"
+                            + " active.");
                 }
                 return;
             }
@@ -3445,20 +3437,6 @@
             case MSG_START_VR_INPUT:
                 startVrInputMethodNoCheck((ComponentName) msg.obj);
                 return true;
-            case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: {
-                final int sequenceNumber = msg.arg1;
-                final ClientState clientState = (ClientState)msg.obj;
-                try {
-                    clientState.client.setUserActionNotificationSequenceNumber(sequenceNumber);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Got RemoteException sending "
-                            + "setUserActionNotificationSequenceNumber("
-                            + sequenceNumber + ") notification to pid "
-                            + clientState.pid + " uid "
-                            + clientState.uid);
-                }
-                return true;
-            }
             case MSG_REPORT_FULLSCREEN_MODE: {
                 final boolean fullscreen = msg.arg1 != 0;
                 final ClientState clientState = (ClientState)msg.obj;
@@ -3944,19 +3922,6 @@
             mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype);
         }
 
-        mCurUserActionNotificationSequenceNumber =
-                Math.max(mCurUserActionNotificationSequenceNumber + 1, 1);
-        if (DEBUG) {
-            Slog.d(TAG, "Bump mCurUserActionNotificationSequenceNumber:"
-                    + mCurUserActionNotificationSequenceNumber);
-        }
-
-        if (mCurClient != null && mCurClient.client != null) {
-            executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
-                    MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER,
-                    mCurUserActionNotificationSequenceNumber, mCurClient));
-        }
-
         if (isVrInput) {
             // Updates to InputMethod are transient in VR mode. Any changes to Settings are skipped.
             return;
@@ -4558,8 +4523,6 @@
                     + " mShowForced=" + mShowForced
                     + " mInputShown=" + mInputShown);
             p.println("  mInFullscreenMode=" + mInFullscreenMode);
-            p.println("  mCurUserActionNotificationSequenceNumber="
-                    + mCurUserActionNotificationSequenceNumber);
             p.println("  mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive);
             p.println("  mSettingsObserver=" + mSettingsObserver);
             p.println("  mSwitchingController:");
@@ -5057,5 +5020,11 @@
         public boolean shouldOfferSwitchingToNextInputMethod() {
             return mImms.shouldOfferSwitchingToNextInputMethod(mToken);
         }
+
+        @BinderThread
+        @Override
+        public void notifyUserActionAsync() {
+            mImms.notifyUserAction(mToken);
+        }
     }
 }