diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java index 2d2b0f5369d15..f4daec121fc60 100644 --- a/core/java/android/view/ImeFocusController.java +++ b/core/java/android/view/ImeFocusController.java @@ -136,45 +136,58 @@ public final class ImeFocusController { boolean forceFocus = false; final InputMethodManagerDelegate immDelegate = getImmDelegate(); - if (immDelegate.isRestartOnNextWindowFocus(true /* reset */)) { - if (DEBUG) Log.v(TAG, "Restarting due to isRestartOnNextWindowFocus as true"); - forceFocus = true; - } + synchronized (immDelegate.getLockObject()) { + // TODO(b/244504062): Remove isRestartOnNextWindowFocus. + if (immDelegate.isRestartOnNextWindowFocus(true /* reset */)) { + if (DEBUG) Log.v(TAG, "Restarting due to isRestartOnNextWindowFocus as true"); + forceFocus = true; + } - // Update mNextServedView when focusedView changed. - onViewFocusChanged(viewForWindowFocus, true); + // Update mNextServedView when focusedView changed. + onViewFocusChanged(viewForWindowFocus, true); - // Starting new input when the next focused view is same as served view but the currently - // active connection (if any) is not associated with it. - final boolean nextFocusIsServedView = mServedView == viewForWindowFocus; - if (nextFocusIsServedView && !immDelegate.hasActiveConnection(viewForWindowFocus)) { - forceFocus = true; + // Starting new input when the next focused view is same as served view but the + // currently active connection (if any) is not associated with it. + final boolean nextFocusIsServedView = mServedView == viewForWindowFocus; + + if (nextFocusIsServedView && !immDelegate.hasActiveConnection(viewForWindowFocus)) { + forceFocus = true; + } } immDelegate.startInputAsyncOnWindowFocusGain(viewForWindowFocus, windowAttribute.softInputMode, windowAttribute.flags, forceFocus); } + /** + * @see InputMethodManager#checkFocus() + */ public boolean checkFocus(boolean forceNewFocus, boolean startInput) { final InputMethodManagerDelegate immDelegate = getImmDelegate(); - if (!immDelegate.isCurrentRootView(mViewRootImpl) - || (mServedView == mNextServedView && !forceNewFocus)) { - return false; + synchronized (immDelegate.getLockObject()) { + if (!immDelegate.isCurrentRootView(mViewRootImpl)) { + return false; + } + if (mServedView == mNextServedView && !forceNewFocus) { + return false; + } + if (DEBUG) { + Log.v(TAG, "checkFocus: view=" + mServedView + + " next=" + mNextServedView + + " force=" + forceNewFocus + + " package=" + + (mServedView != null ? mServedView.getContext().getPackageName() + : "")); + } + // Close the connection when no next served view coming. + if (mNextServedView == null) { + immDelegate.finishInput(); + immDelegate.closeCurrentIme(); + return false; + } + mServedView = mNextServedView; + immDelegate.finishComposingText(); } - if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView - + " next=" + mNextServedView - + " force=" + forceNewFocus - + " package=" - + (mServedView != null ? mServedView.getContext().getPackageName() : "")); - - // Close the connection when no next served view coming. - if (mNextServedView == null) { - immDelegate.finishInput(); - immDelegate.closeCurrentIme(); - return false; - } - mServedView = mNextServedView; - immDelegate.finishComposingText(); if (startInput) { immDelegate.startInput(StartInputReason.CHECK_FOCUS, null /* focusedView */, @@ -188,51 +201,61 @@ public final class ImeFocusController { if (view == null || view.isTemporarilyDetached()) { return; } - if (!getImmDelegate().isCurrentRootView(view.getViewRootImpl())) { - return; - } - if (!view.hasImeFocus() || !view.hasWindowFocus()) { - return; - } - if (DEBUG) Log.d(TAG, "onViewFocusChanged, view=" + InputMethodDebug.dumpViewInfo(view) - + ", mServedView=" + InputMethodDebug.dumpViewInfo(mServedView)); + final InputMethodManagerDelegate immDelegate = getImmDelegate(); + synchronized (immDelegate.getLockObject()) { + if (!immDelegate.isCurrentRootView(view.getViewRootImpl())) { + return; + } + if (!view.hasImeFocus() || !view.hasWindowFocus()) { + return; + } + if (DEBUG) { + Log.d(TAG, "onViewFocusChanged, view=" + InputMethodDebug.dumpViewInfo(view) + + ", mServedView=" + InputMethodDebug.dumpViewInfo(mServedView)); + } - // We don't need to track the next served view when the view lost focus here because: - // 1) The current view focus may be cleared temporary when in touch mode, closing input - // at this moment isn't the right way. - // 2) We only care about the served view change when it focused, since changing input - // connection when the focus target changed is reasonable. - // 3) Setting the next served view as null when no more served view should be handled in - // other special events (e.g. view detached from window or the window dismissed). - if (hasFocus) { - mNextServedView = view; + // We don't need to track the next served view when the view lost focus here because: + // 1) The current view focus may be cleared temporary when in touch mode, closing input + // at this moment isn't the right way. + // 2) We only care about the served view change when it focused, since changing input + // connection when the focus target changed is reasonable. + // 3) Setting the next served view as null when no more served view should be handled in + // other special events (e.g. view detached from window or the window dismissed). + if (hasFocus) { + mNextServedView = view; + } } mViewRootImpl.dispatchCheckFocus(); } @UiThread void onViewDetachedFromWindow(View view) { - if (!getImmDelegate().isCurrentRootView(view.getViewRootImpl())) { - return; - } - if (mNextServedView == view) { - mNextServedView = null; - } - if (mServedView == view) { - mViewRootImpl.dispatchCheckFocus(); + final InputMethodManagerDelegate immDelegate = getImmDelegate(); + synchronized (immDelegate.getLockObject()) { + if (!immDelegate.isCurrentRootView(view.getViewRootImpl())) { + return; + } + if (mNextServedView == view) { + mNextServedView = null; + } + if (mServedView == view) { + mViewRootImpl.dispatchCheckFocus(); + } } } @UiThread void onWindowDismissed() { final InputMethodManagerDelegate immDelegate = getImmDelegate(); - if (!immDelegate.isCurrentRootView(mViewRootImpl)) { - return; + synchronized (immDelegate.getLockObject()) { + if (!immDelegate.isCurrentRootView(mViewRootImpl)) { + return; + } + if (mServedView != null) { + immDelegate.finishInput(); + } + immDelegate.setCurrentRootView(null); } - if (mServedView != null) { - immDelegate.finishInput(); - } - immDelegate.setCurrentRootView(null); mHasImeFocus = false; } @@ -283,9 +306,21 @@ public final class ImeFocusController { * @hide */ public interface InputMethodManagerDelegate { + /** + * Starts the input connection. + * Note that this method must not hold the {@link InputMethodManager} lock with + * {@link InputMethodManagerDelegate#getLockObject()} while {@link InputMethodManager} + * calling into app-code in different threads. + */ boolean startInput(@StartInputReason int startInputReason, View focusedView, @StartInputFlags int startInputFlags, @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags); + /** + * Starts the input connection when gaining the window focus. + * Note that this method must not hold the {@link InputMethodManager} lock with + * {@link InputMethodManagerDelegate#getLockObject()} while {@link InputMethodManager} + * calling into app-code in different threads. + */ void startInputAsyncOnWindowFocusGain(View rootView, @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags, boolean forceNewFocus); @@ -302,8 +337,6 @@ public final class ImeFocusController { * Returns the {@code InputMethodManager#mH} lock object. * Used for {@link ImeFocusController} to guard the served view being accessed by * {@link InputMethodManager} in different threads. - * - * TODO(b/244504062): Use this to all places requires synchronization in controller. */ Object getLockObject(); }