summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
authorYohei Yukawa <yukawa@google.com>2018-10-26 08:43:30 -0700
committerYohei Yukawa <yukawa@google.com>2018-10-26 13:03:11 -0700
commit052ab8ceea4b2d188578ef529c5dabda4f0a256f (patch)
tree05368c246b90c131aa12326794f967b8101e1b9f /core/java
parentf95d6a17f5721144ac207b91307ce5146a64eab7 (diff)
More robust display ID mismatch detection in IMM
This is another follow up CL to my previous CL [1] that enabled per-display InputMethodManager (IMM) instance (Bug 115893206). What we learned in Bug 118341760 is that for some apps view.getContext() may return a ContextWrapper subclass whose Context.getDisplayId() and Context.getSystemService() are not consistent with each other. Although this is considered to be a bug of such a ContextWrapper subclass, application developers may not be aware of the issue when such a problematic ContextWrapper came from libraries that app developers happened to, or had to, depend on. The key idea of this CL is that view.getViewRootImpl().getDisplayId() would be a more robust source of display ID than view.getContext().getDisplayId() for most of cases, because to which ViewRootImpl a given View belongs is already a strong signal about to which display the View belongs. As far as I've tested locally, this approach seems to be more promising and is expected to give us better app compatibility in multi-display scenarios. [1]: I78ad7cccb9586474c83f7e2f90c0bcabb221c47b 4052a10f2970f83d40bf5a45f3632cd63d084e51 Bug: 118252837 Fix: 118341760 Test: atest ActivityManagerMultiDisplayTests Test: atest CtsInputMethodTestCases CtsInputMethodServiceHostTestCases Test: atest FrameworksCoreTests:android.view.inputmethod.InputMethodManagerTest Change-Id: If47e48bc8176657bf4bb882146d2affdfe457c90
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/view/ViewRootImpl.java3
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java132
2 files changed, 83 insertions, 52 deletions
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index fef2d43a5baf..48761486d9dc 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -243,9 +243,10 @@ public final class ViewRootImpl implements ViewParent,
final Context mContext;
/**
* TODO(b/116349163): Check if we can merge this into {@link #mContext}.
+ * @hide
*/
@NonNull
- private Context mDisplayContext;
+ public Context mDisplayContext;
@UnsupportedAppUsage
final IWindowSession mWindowSession;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index d24b822279b2..4cbb097580d6 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -470,26 +470,53 @@ public final class InputMethodManager {
}
/**
- * Checks the consistency between {@link InputMethodManager} state and {@link View} state.
+ * Returns fallback {@link InputMethodManager} if the called one is not likely to be compatible
+ * with the given {@code view}.
*
- * @param view {@link View} to be checked
- * @return {@code true} if {@code view} is not {@code null} and there is a {@link Context}
- * mismatch between {@link InputMethodManager} and {@code view}
- */
- private boolean shouldDispatchToViewContext(@Nullable View view) {
+ * @param view {@link View} to be checked.
+ * @return {@code null} when it is unnecessary (or impossible) to use fallback
+ * {@link InputMethodManager} to which IME API calls need to be re-dispatched.
+ * Non-{@code null} {@link InputMethodManager} if this method believes it'd be safer to
+ * re-dispatch IME APIs calls on it.
+ */
+ @Nullable
+ private InputMethodManager getFallbackInputMethodManagerIfNecessary(@Nullable View view) {
if (view == null) {
- return false;
- }
- final int viewDisplayId = view.getContext().getDisplayId();
- if (viewDisplayId != mDisplayId) {
- Log.w(TAG, "b/117267690: Context mismatch found. view=" + view + " belongs to"
- + " displayId=" + viewDisplayId
- + " but InputMethodManager belongs to displayId=" + mDisplayId
- + ". Use the right InputMethodManager instance to avoid performance overhead.",
- new Throwable());
- return true;
- }
- return false;
+ return null;
+ }
+ // As evidenced in Bug 118341760, view.getViewRootImpl().getDisplayId() is supposed to be
+ // more reliable to determine with which display the given view is interacting than
+ // view.getContext().getDisplayId() / view.getContext().getSystemService(), which can be
+ // easily messed up by app developers (or library authors) by creating inconsistent
+ // ContextWrapper objects that re-dispatch those methods to other Context such as
+ // ApplicationContext.
+ final ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ if (viewRootImpl == null) {
+ return null;
+ }
+ final int viewRootDisplayId = viewRootImpl.getDisplayId();
+ if (viewRootDisplayId == mDisplayId) {
+ // Expected case. Good to go.
+ return null;
+ }
+ final InputMethodManager fallbackImm =
+ viewRootImpl.mDisplayContext.getSystemService(InputMethodManager.class);
+ if (fallbackImm == null) {
+ Log.e(TAG, "b/117267690: Failed to get non-null fallback IMM. view=" + view);
+ return null;
+ }
+ if (fallbackImm.mDisplayId != viewRootDisplayId) {
+ Log.e(TAG, "b/117267690: Failed to get fallback IMM with expected displayId="
+ + viewRootDisplayId + " actual IMM#displayId=" + fallbackImm.mDisplayId
+ + " view=" + view);
+ return null;
+ }
+ Log.w(TAG, "b/117267690: Display ID mismatch found."
+ + " ViewRootImpl displayId=" + viewRootDisplayId
+ + " InputMethodManager displayId=" + mDisplayId
+ + ". Use the right InputMethodManager instance to avoid performance overhead.",
+ new Throwable());
+ return fallbackImm;
}
private static boolean canStartInput(View servedView) {
@@ -1000,8 +1027,9 @@ public final class InputMethodManager {
*/
public boolean isActive(View view) {
// Re-dispatch if there is a context mismatch.
- if (shouldDispatchToViewContext(view)) {
- return view.getContext().getSystemService(InputMethodManager.class).isActive(view);
+ final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+ if (fallbackImm != null) {
+ return fallbackImm.isActive(view);
}
checkFocus();
@@ -1088,9 +1116,9 @@ public final class InputMethodManager {
public void displayCompletions(View view, CompletionInfo[] completions) {
// Re-dispatch if there is a context mismatch.
- if (shouldDispatchToViewContext(view)) {
- view.getContext().getSystemService(InputMethodManager.class)
- .displayCompletions(view, completions);
+ final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+ if (fallbackImm != null) {
+ fallbackImm.displayCompletions(view, completions);
return;
}
@@ -1113,9 +1141,9 @@ public final class InputMethodManager {
public void updateExtractedText(View view, int token, ExtractedText text) {
// Re-dispatch if there is a context mismatch.
- if (shouldDispatchToViewContext(view)) {
- view.getContext().getSystemService(InputMethodManager.class)
- .updateExtractedText(view, token, text);
+ final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+ if (fallbackImm != null) {
+ fallbackImm.updateExtractedText(view, token, text);
return;
}
@@ -1161,9 +1189,9 @@ public final class InputMethodManager {
*/
public boolean showSoftInput(View view, int flags) {
// Re-dispatch if there is a context mismatch.
- if (shouldDispatchToViewContext(view)) {
- return view.getContext().getSystemService(InputMethodManager.class)
- .showSoftInput(view, flags);
+ final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+ if (fallbackImm != null) {
+ return fallbackImm.showSoftInput(view, flags);
}
return showSoftInput(view, flags, null);
@@ -1229,9 +1257,9 @@ public final class InputMethodManager {
*/
public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
// Re-dispatch if there is a context mismatch.
- if (shouldDispatchToViewContext(view)) {
- return view.getContext().getSystemService(InputMethodManager.class)
- .showSoftInput(view, flags, resultReceiver);
+ final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+ if (fallbackImm != null) {
+ return fallbackImm.showSoftInput(view, flags, resultReceiver);
}
checkFocus();
@@ -1398,8 +1426,9 @@ public final class InputMethodManager {
*/
public void restartInput(View view) {
// Re-dispatch if there is a context mismatch.
- if (shouldDispatchToViewContext(view)) {
- view.getContext().getSystemService(InputMethodManager.class).restartInput(view);
+ final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+ if (fallbackImm != null) {
+ fallbackImm.restartInput(view);
return;
}
@@ -1827,9 +1856,9 @@ public final class InputMethodManager {
public void updateSelection(View view, int selStart, int selEnd,
int candidatesStart, int candidatesEnd) {
// Re-dispatch if there is a context mismatch.
- if (shouldDispatchToViewContext(view)) {
- view.getContext().getSystemService(InputMethodManager.class)
- .updateSelection(view, selStart, selEnd, candidatesStart, candidatesEnd);
+ final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+ if (fallbackImm != null) {
+ fallbackImm.updateSelection(view, selStart, selEnd, candidatesStart, candidatesEnd);
return;
}
@@ -1871,8 +1900,9 @@ public final class InputMethodManager {
*/
public void viewClicked(View view) {
// Re-dispatch if there is a context mismatch.
- if (shouldDispatchToViewContext(view)) {
- view.getContext().getSystemService(InputMethodManager.class).viewClicked(view);
+ final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+ if (fallbackImm != null) {
+ fallbackImm.viewClicked(view);
return;
}
@@ -1941,9 +1971,9 @@ public final class InputMethodManager {
@Deprecated
public void updateCursor(View view, int left, int top, int right, int bottom) {
// Re-dispatch if there is a context mismatch.
- if (shouldDispatchToViewContext(view)) {
- view.getContext().getSystemService(InputMethodManager.class)
- .updateCursor(view, left, top, right, bottom);
+ final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+ if (fallbackImm != null) {
+ fallbackImm.updateCursor(view, left, top, right, bottom);
return;
}
@@ -1979,9 +2009,9 @@ public final class InputMethodManager {
return;
}
// Re-dispatch if there is a context mismatch.
- if (shouldDispatchToViewContext(view)) {
- view.getContext().getSystemService(InputMethodManager.class)
- .updateCursorAnchorInfo(view, cursorAnchorInfo);
+ final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+ if (fallbackImm != null) {
+ fallbackImm.updateCursorAnchorInfo(view, cursorAnchorInfo);
return;
}
@@ -2031,9 +2061,9 @@ public final class InputMethodManager {
*/
public void sendAppPrivateCommand(View view, String action, Bundle data) {
// Re-dispatch if there is a context mismatch.
- if (shouldDispatchToViewContext(view)) {
- view.getContext().getSystemService(InputMethodManager.class)
- .sendAppPrivateCommand(view, action, data);
+ final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
+ if (fallbackImm != null) {
+ fallbackImm.sendAppPrivateCommand(view, action, data);
return;
}
@@ -2209,9 +2239,9 @@ public final class InputMethodManager {
public void dispatchKeyEventFromInputMethod(@Nullable View targetView,
@NonNull KeyEvent event) {
// Re-dispatch if there is a context mismatch.
- if (shouldDispatchToViewContext(targetView)) {
- targetView.getContext().getSystemService(InputMethodManager.class)
- .dispatchKeyEventFromInputMethod(targetView, event);
+ final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(targetView);
+ if (fallbackImm != null) {
+ fallbackImm.dispatchKeyEventFromInputMethod(targetView, event);
return;
}