From d6d155f1c9aebf84d04da57e3b7c359ae638f5b1 Mon Sep 17 00:00:00 2001 From: Nikolas Havrikov Date: Fri, 19 Nov 2021 13:53:40 +0100 Subject: [PATCH 1/7] Move clear current method caller into controller Bug: 205676419 Test: make Change-Id: I4b3cbcd1681bae662e644a5c86b5b63df6bf4612 --- .../server/inputmethod/InputMethodBindingController.java | 7 ++++--- .../server/inputmethod/InputMethodManagerService.java | 1 - 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java index 05e1bdd11db60..84fb8379999e1 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java @@ -334,7 +334,7 @@ final class InputMethodBindingController { // We consider this to be a new bind attempt, since the system // should now try to restart the service for us. mLastBindTime = SystemClock.uptimeMillis(); - mService.clearClientSessionsLocked(); + clearCurMethodAndSessionsLocked(); mService.clearInputShowRequestLocked(); mService.unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME); } @@ -358,11 +358,12 @@ final class InputMethodBindingController { } mCurId = null; - mService.clearClientSessionsLocked(); + clearCurMethodAndSessionsLocked(); } @GuardedBy("mMethodMap") - void clearCurMethodLocked() { + private void clearCurMethodAndSessionsLocked() { + mService.clearClientSessionsLocked(); mCurMethod = null; mCurMethodUid = Process.INVALID_UID; } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 3c6b0966dfc3c..27d7dd612aec8 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -2590,7 +2590,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub finishSessionLocked(mEnabledSession); mEnabledSession = null; - mBindingController.clearCurMethodLocked(); scheduleNotifyImeUidToAudioService(Process.INVALID_UID); } hideStatusBarIconLocked(); From 19f60889d0fbe4523b94b50ffc0cceb2d48130e2 Mon Sep 17 00:00:00 2001 From: Nikolas Havrikov Date: Mon, 22 Nov 2021 14:41:04 +0100 Subject: [PATCH 2/7] Move TIME_TO_RECONNECT to binding controller Bug: 205676419 Test: make Change-Id: Iea54b699c15970c0ff0f1b61b1cc8079c60b0c1f --- .../server/inputmethod/InputMethodBindingController.java | 3 +++ .../android/server/inputmethod/InputMethodManagerService.java | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java index 84fb8379999e1..729cbfbd0aa7d 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java @@ -58,6 +58,9 @@ final class InputMethodBindingController { static final boolean DEBUG = false; private static final String TAG = InputMethodBindingController.class.getSimpleName(); + /** Time in milliseconds that the IME service has to bind before it is reconnected. */ + static final long TIME_TO_RECONNECT = 3 * 1000; + @NonNull private final InputMethodManagerService mService; @NonNull private final Context mContext; @NonNull private final ArrayMap mMethodMap; diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 27d7dd612aec8..ca5f808b4cfc7 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -49,6 +49,8 @@ import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.DISPLAY_IME_POLICY_HIDE; import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; +import static com.android.server.inputmethod.InputMethodBindingController.TIME_TO_RECONNECT; + import static java.lang.annotation.RetentionPolicy.SOURCE; import android.Manifest; @@ -250,8 +252,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub static final int MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE = 7000; - static final long TIME_TO_RECONNECT = 3 * 1000; - static final int SECURE_SUGGESTION_SPANS_MAX_SIZE = 20; private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID; From 9fdc456914ce5926d3b2171e3d4c12bda9f034b9 Mon Sep 17 00:00:00 2001 From: Nikolas Havrikov Date: Mon, 22 Nov 2021 15:10:49 +0100 Subject: [PATCH 3/7] Extract and move binding visibly to controller This CL flips the order of events: Now the IMMS first performs the "visible" binding, and only then showSoftInput gets called on the IME. Bug: 205676419 Test: make Change-Id: I5c29f880cd5eefd0998891012588774de25c55a7 --- .../InputMethodBindingController.java | 42 +++++++++++++++++-- .../InputMethodManagerService.java | 32 +++----------- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java index 729cbfbd0aa7d..59d382128a70e 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java @@ -39,6 +39,7 @@ import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; import android.util.ArrayMap; +import android.util.EventLog; import android.util.Slog; import android.view.IWindowManager; import android.view.WindowManager; @@ -49,6 +50,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.inputmethod.InputBindResult; import com.android.internal.inputmethod.UnbindReason; import com.android.internal.view.IInputMethod; +import com.android.server.EventLogTags; import com.android.server.wm.WindowManagerInternal; /** @@ -442,7 +444,7 @@ final class InputMethodBindingController { } @GuardedBy("mMethodMap") - void unbindMainConnectionLocked() { + private void unbindMainConnectionLocked() { mContext.unbindService(mMainConnection); mHasConnection = false; } @@ -464,17 +466,51 @@ final class InputMethodBindingController { } @GuardedBy("mMethodMap") - boolean bindCurrentInputMethodServiceVisibleConnectionLocked() { + private boolean bindCurrentInputMethodServiceVisibleConnectionLocked() { mVisibleBound = bindCurrentInputMethodServiceLocked(mVisibleConnection, IME_VISIBLE_BIND_FLAGS); return mVisibleBound; } @GuardedBy("mMethodMap") - boolean bindCurrentInputMethodServiceMainConnectionLocked() { + private boolean bindCurrentInputMethodServiceMainConnectionLocked() { mHasConnection = bindCurrentInputMethodServiceLocked(mMainConnection, mImeConnectionBindFlags); return mHasConnection; } + /** + * Bind the IME so that it can be shown. + * + *

+ * Performs a rebind if no binding is achieved in {@link #TIME_TO_RECONNECT} milliseconds. + */ + @GuardedBy("mMethodMap") + void setCurrentMethodVisibleLocked() { + if (mCurMethod != null) { + if (DEBUG) Slog.d(TAG, "setCurrentMethodVisibleLocked: mCurToken=" + mCurToken); + if (mHasConnection && !mVisibleBound) { + bindCurrentInputMethodServiceVisibleConnectionLocked(); + } + return; + } + + long bindingDuration = SystemClock.uptimeMillis() - mLastBindTime; + if (mHasConnection && bindingDuration >= TIME_TO_RECONNECT) { + // The client has asked to have the input method shown, but + // we have been sitting here too long with a connection to the + // service and no interface received, so let's disconnect/connect + // to try to prod things along. + EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, getSelectedMethodId(), + bindingDuration, 1); + Slog.w(TAG, "Force disconnect/connect to the IME in setCurrentMethodVisibleLocked()"); + unbindMainConnectionLocked(); + bindCurrentInputMethodServiceMainConnectionLocked(); + } else { + if (DEBUG) { + Slog.d(TAG, "Can't show input: connection = " + mHasConnection + ", time = " + + (TIME_TO_RECONNECT - bindingDuration)); + } + } + } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index ca5f808b4cfc7..e21d7da9cc0de 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -3046,42 +3046,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return false; } - boolean res = false; - IInputMethod curMethod = getCurMethod(); - if (curMethod != null) { - if (DEBUG) Slog.d(TAG, "showCurrentInputLocked: mCurToken=" + getCurToken()); + mBindingController.setCurrentMethodVisibleLocked(); + if (getCurMethod() != null) { // create a placeholder token for IMS so that IMS cannot inject windows into client app. Binder showInputToken = new Binder(); mShowRequestWindowMap.put(showInputToken, windowToken); + IInputMethod curMethod = getCurMethod(); executeOrSendMessage(curMethod, mCaller.obtainMessageIIOOO(MSG_SHOW_SOFT_INPUT, getImeShowFlagsLocked(), reason, curMethod, resultReceiver, showInputToken)); mInputShown = true; - if (hasConnection() && !isVisibleBound()) { - mBindingController.bindCurrentInputMethodServiceVisibleConnectionLocked(); - } - res = true; - } else { - long bindingDuration = SystemClock.uptimeMillis() - getLastBindTime(); - if (hasConnection() && bindingDuration >= TIME_TO_RECONNECT) { - // The client has asked to have the input method shown, but - // we have been sitting here too long with a connection to the - // service and no interface received, so let's disconnect/connect - // to try to prod things along. - EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, getSelectedMethodId(), - bindingDuration, 1); - Slog.w(TAG, "Force disconnect/connect to the IME in showCurrentInputLocked()"); - mBindingController.unbindMainConnectionLocked(); - mBindingController.bindCurrentInputMethodServiceMainConnectionLocked(); - } else { - if (DEBUG) { - Slog.d(TAG, "Can't show input: connection = " + hasConnection() + ", time = " - + (TIME_TO_RECONNECT - bindingDuration)); - } - } + return true; } - return res; + return false; } @Override From 25038e1da1040b9d557d68b8222acf3247bccd66 Mon Sep 17 00:00:00 2001 From: Nikolas Havrikov Date: Mon, 22 Nov 2021 15:40:11 +0100 Subject: [PATCH 4/7] Extract and move unbinding visibly to controller This also slightly reduces coupling between IMMS and the controller. Bug: 205676419 Test: make Change-Id: I8731910185c6f76a91a4b43e827ffe371db86674 --- .../InputMethodBindingController.java | 17 +++++++++++------ .../inputmethod/InputMethodManagerService.java | 15 +++------------ 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java index 59d382128a70e..6727a931de880 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java @@ -244,7 +244,7 @@ final class InputMethodBindingController { } /** - * Indicates whether {@link #getVisibleConnection} is currently in use. + * Indicates whether {@link #mVisibleConnection} is currently in use. */ boolean isVisibleBound() { return mVisibleBound; @@ -253,11 +253,6 @@ final class InputMethodBindingController { /** * Used to bring IME service up to visible adjustment while it is being shown. */ - @NonNull - ServiceConnection getVisibleConnection() { - return mVisibleConnection; - } - private final ServiceConnection mVisibleConnection = new ServiceConnection() { @Override public void onBindingDied(ComponentName name) { synchronized (mMethodMap) { @@ -513,4 +508,14 @@ final class InputMethodBindingController { } } } + + /** + * Remove the binding needed for the IME to be shown. + */ + @GuardedBy("mMethodMap") + void setCurrentMethodNotVisibleLocked() { + if (mVisibleBound) { + unbindVisibleConnectionLocked(); + } + } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index e21d7da9cc0de..b88a8e44ecc71 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -311,14 +311,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("mMethodMap") private int mMethodMapUpdateCount = 0; - /** - * Indicates whether {@link InputMethodBindingController#getVisibleConnection} is currently - * in use. - */ - private boolean isVisibleBound() { - return mBindingController.isVisibleBound(); - } - // Ongoing notification private NotificationManager mNotificationManager; KeyguardManager mKeyguardManager; @@ -3143,9 +3135,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } else { res = false; } - if (hasConnection() && isVisibleBound()) { - mBindingController.unbindVisibleConnectionLocked(); - } + mBindingController.setCurrentMethodNotVisibleLocked(); mInputShown = false; mShowRequested = false; mShowExplicitlyRequested = false; @@ -5091,7 +5081,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + " client=" + mCurFocusedWindowClient); focusedWindowClient = mCurFocusedWindowClient; p.println(" mCurId=" + getCurId() + " mHaveConnection=" + hasConnection() - + " mBoundToMethod=" + mBoundToMethod + " mVisibleBound=" + isVisibleBound()); + + " mBoundToMethod=" + mBoundToMethod + " mVisibleBound=" + + mBindingController.isVisibleBound()); p.println(" mCurToken=" + getCurToken()); p.println(" mCurTokenDisplayId=" + mCurTokenDisplayId); p.println(" mCurHostInputToken=" + mCurHostInputToken); From 1077ca51b4ecb24db4641958c127880095d067de Mon Sep 17 00:00:00 2001 From: Nikolas Havrikov Date: Mon, 22 Nov 2021 17:58:02 +0100 Subject: [PATCH 5/7] Remove unused method isImeVisible Bug: 205676419 Test: make Change-Id: I7d01b25c313467493fa7976bf7b127091da045ca --- .../android/server/inputmethod/InputMethodManagerService.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index b88a8e44ecc71..b9b71cd3b6f12 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -3489,10 +3489,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return mWindowManagerInternal.shouldRestoreImeVisibility(windowToken); } - private boolean isImeVisible() { - return (mImeWindowVis & InputMethodService.IME_VISIBLE) != 0; - } - @GuardedBy("mMethodMap") private boolean canShowInputMethodPickerLocked(IInputMethodClient client) { // TODO(yukawa): multi-display support. From fb11e0a86a9a5a810ce018c7c31b9f8c8514c8b1 Mon Sep 17 00:00:00 2001 From: Nikolas Havrikov Date: Mon, 22 Nov 2021 18:07:13 +0100 Subject: [PATCH 6/7] Remove unused suggestion span cache Bug: 205676419 Test: make Change-Id: I6ebb2df2f40c1c8cc94b93ef574fe8bfbfe6662d --- .../server/inputmethod/InputMethodManagerService.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index b9b71cd3b6f12..bf4c3670b8a3d 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -113,12 +113,10 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; -import android.text.style.SuggestionSpan; import android.util.ArrayMap; import android.util.ArraySet; import android.util.EventLog; import android.util.IndentingPrintWriter; -import android.util.LruCache; import android.util.Pair; import android.util.PrintWriterPrinter; import android.util.Printer; @@ -252,8 +250,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub static final int MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE = 7000; - static final int SECURE_SUGGESTION_SPANS_MAX_SIZE = 20; - private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID; private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher"; private static final String HANDLER_THREAD_NAME = "android.imms"; @@ -301,8 +297,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // lock for this class. final ArrayList mMethodList = new ArrayList<>(); final ArrayMap mMethodMap = new ArrayMap<>(); - private final LruCache mSecureSuggestionSpans = - new LruCache<>(SECURE_SUGGESTION_SPANS_MAX_SIZE); final InputMethodSubtypeSwitchingController mSwitchingController; /** From 5a2b8d3ffe418e4f7eb12f24aa05f98500d15cf4 Mon Sep 17 00:00:00 2001 From: Nikolas Havrikov Date: Thu, 2 Dec 2021 18:21:54 +0100 Subject: [PATCH 7/7] Store displayIdToShowIme in an IMMS field This change allows the IMMS to remember the last display id the IME was requested to bind on. One use case is when we want to prevent binding the IME if there is no currently focused editText, and yet we want to still be able to handle an explicit request to show the IME later. Remembering the display id helps to show it on the correct screen. Bug: 199887357 Bug: 37617707 Test: make Change-Id: I98459c4a44416fd7209150932a2a2520612da818 --- .../InputMethodBindingController.java | 7 +++--- .../InputMethodManagerService.java | 23 ++++++++++++++----- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java index 6727a931de880..3d91feef70431 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java @@ -383,7 +383,7 @@ final class InputMethodBindingController { @GuardedBy("mMethodMap") @NonNull - InputBindResult bindCurrentMethodLocked(int displayIdToShowIme) { + InputBindResult bindCurrentMethodLocked() { InputMethodInfo info = mMethodMap.get(mSelectedMethodId); if (info == null) { throw new IllegalArgumentException("Unknown id: " + mSelectedMethodId); @@ -395,7 +395,7 @@ final class InputMethodBindingController { mCurId = info.getId(); mLastBindTime = SystemClock.uptimeMillis(); - addFreshWindowTokenLocked(displayIdToShowIme); + addFreshWindowTokenLocked(); return new InputBindResult( InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING, null, null, mCurId, mCurSeq, false); @@ -420,7 +420,8 @@ final class InputMethodBindingController { } @GuardedBy("mMethodMap") - private void addFreshWindowTokenLocked(int displayIdToShowIme) { + private void addFreshWindowTokenLocked() { + int displayIdToShowIme = mService.getDisplayIdToShowIme(); mCurToken = new Binder(); mService.setCurTokenDisplayId(displayIdToShowIme); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index bf4c3670b8a3d..bff4f273e1956 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -305,6 +305,17 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("mMethodMap") private int mMethodMapUpdateCount = 0; + /** + * The display id for which the latest startInput was called. + */ + @GuardedBy("mMethodMap") + int getDisplayIdToShowIme() { + return mDisplayIdToShowIme; + } + + @GuardedBy("mMethodMap") + private int mDisplayIdToShowIme = INVALID_DISPLAY; + // Ongoing notification private NotificationManager mNotificationManager; KeyguardManager mKeyguardManager; @@ -2340,10 +2351,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } // Compute the final shown display ID with validated cs.selfReportedDisplayId for this // session & other conditions. - final int displayIdToShowIme = computeImeDisplayIdForTarget(cs.selfReportedDisplayId, + mDisplayIdToShowIme = computeImeDisplayIdForTarget(cs.selfReportedDisplayId, mImeDisplayValidator); - if (displayIdToShowIme == INVALID_DISPLAY) { + if (mDisplayIdToShowIme == INVALID_DISPLAY) { mImeHiddenByDisplayPolicy = true; hideCurrentInputLocked(mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE); @@ -2364,7 +2375,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // Check if the input method is changing. // We expect the caller has already verified that the client is allowed to access this // display ID. - if (isSelectedMethodBound(displayIdToShowIme)) { + if (isSelectedMethodBound()) { if (cs.curSession != null) { // Fast case: if we are already connected to the input method, // then just return it. @@ -2380,13 +2391,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mBindingController.unbindCurrentMethodLocked(); - return mBindingController.bindCurrentMethodLocked(displayIdToShowIme); + return mBindingController.bindCurrentMethodLocked(); } - private boolean isSelectedMethodBound(int displayIdToShowIme) { + private boolean isSelectedMethodBound() { String curId = getCurId(); return curId != null && curId.equals(getSelectedMethodId()) - && displayIdToShowIme == mCurTokenDisplayId; + && mDisplayIdToShowIme == mCurTokenDisplayId; } @GuardedBy("mMethodMap")