diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java index 05e1bdd11db60..3d91feef70431 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; /** @@ -58,6 +60,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; @@ -239,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; @@ -248,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) { @@ -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; } @@ -382,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); @@ -394,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); @@ -419,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); @@ -438,7 +440,7 @@ final class InputMethodBindingController { } @GuardedBy("mMethodMap") - void unbindMainConnectionLocked() { + private void unbindMainConnectionLocked() { mContext.unbindService(mMainConnection); mHasConnection = false; } @@ -460,17 +462,61 @@ 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)); + } + } + } + + /** + * 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 3c6b0966dfc3c..bff4f273e1956 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; @@ -111,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; @@ -250,10 +250,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; 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; /** @@ -312,13 +306,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private int mMethodMapUpdateCount = 0; /** - * Indicates whether {@link InputMethodBindingController#getVisibleConnection} is currently - * in use. + * The display id for which the latest startInput was called. */ - private boolean isVisibleBound() { - return mBindingController.isVisibleBound(); + @GuardedBy("mMethodMap") + int getDisplayIdToShowIme() { + return mDisplayIdToShowIme; } + @GuardedBy("mMethodMap") + private int mDisplayIdToShowIme = INVALID_DISPLAY; + // Ongoing notification private NotificationManager mNotificationManager; KeyguardManager mKeyguardManager; @@ -2354,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); @@ -2378,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. @@ -2394,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") @@ -2590,7 +2587,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub finishSessionLocked(mEnabledSession); mEnabledSession = null; - mBindingController.clearCurMethodLocked(); scheduleNotifyImeUidToAudioService(Process.INVALID_UID); } hideStatusBarIconLocked(); @@ -3047,42 +3043,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 @@ -3166,9 +3140,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } else { res = false; } - if (hasConnection() && isVisibleBound()) { - mBindingController.unbindVisibleConnectionLocked(); - } + mBindingController.setCurrentMethodNotVisibleLocked(); mInputShown = false; mShowRequested = false; mShowExplicitlyRequested = false; @@ -3522,10 +3494,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. @@ -5114,7 +5082,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);