Skip to content

Commit

Permalink
Ignore reentrancy for ImeThread triggering
Browse files Browse the repository at this point in the history
In the delayed activation of input connection, we call requestFocus()
on proxy view, and this triggers HwSecImmHelper.isPasswordInputType()
on Huawei phone, which in turn calls View#onCreateInputConnection() so
there is an infinite loop, which eventually causes stackoverflow.

Presumably isPasswordInputType() is only concerned about whether
onCreateInputConnection()'s outAttrs.inputType is password type or not,
so we fill out outAttrs.inputType and early out when there is a reentrance.

BUG=636197

Review-Url: https://codereview.chromium.org/2246833002
Cr-Commit-Position: refs/heads/master@{#415220}
  • Loading branch information
galmacky authored and Commit bot committed Aug 30, 2016
1 parent 00f5b74 commit f5feceb
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
Expand Down Expand Up @@ -106,15 +105,10 @@ public void run() {
mHandler = handler;
}

void initializeOutAttrsOnUiThread(int inputType, int inputFlags, int selectionStart,
int selectionEnd, EditorInfo outAttrs) {
void resetOnUiThread() {
ImeUtils.checkOnUiThread();
mNumNestedBatchEdits = 0;
mPendingAccent = 0;
ImeUtils.computeEditorInfo(inputType, inputFlags, selectionStart, selectionEnd, outAttrs);
if (DEBUG_LOGS) {
Log.w(TAG, "initializeOutAttrs: " + ImeUtils.getEditorInfoDebugString(outAttrs));
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class ThreadedInputConnectionFactory implements ChromiumBaseInputConnecti
private ThreadedInputConnectionProxyView mProxyView;
private ThreadedInputConnection mThreadedInputConnection;
private CheckInvalidator mCheckInvalidator;
private boolean mReentrantTriggering;

// A small class that can be updated to invalidate the check when there is an external event
// such as window focus loss or view focus loss.
Expand Down Expand Up @@ -105,6 +106,14 @@ public ThreadedInputConnection initializeAndGet(
int selectionEnd, EditorInfo outAttrs) {
ImeUtils.checkOnUiThread();

// Compute outAttrs early in case we early out to prevent reentrancy. (crbug.com/636197)
// TODO(changwan): move this up to ImeAdapter once ReplicaInputConnection is deprecated.
ImeUtils.computeEditorInfo(
inputType, inputFlags, selectionStart, selectionEnd, outAttrs);
if (DEBUG_LOGS) {
Log.w(TAG, "initializeAndGet. outAttr: " + ImeUtils.getEditorInfoDebugString(outAttrs));
}

// IMM can internally ignore subsequent activation requests, e.g., by checking
// mServedConnecting.
if (mCheckInvalidator != null) mCheckInvalidator.invalidate();
Expand All @@ -118,14 +127,17 @@ public ThreadedInputConnection initializeAndGet(
if (mThreadedInputConnection == null) {
if (DEBUG_LOGS) Log.w(TAG, "Creating ThreadedInputConnection...");
mThreadedInputConnection = new ThreadedInputConnection(view, imeAdapter, mHandler);
} else {
mThreadedInputConnection.resetOnUiThread();
}
mThreadedInputConnection.initializeOutAttrsOnUiThread(inputType, inputFlags,
selectionStart, selectionEnd, outAttrs);
return mThreadedInputConnection;
}

private void triggerDelayedOnCreateInputConnection(final View view) {
if (DEBUG_LOGS) Log.w(TAG, "triggerDelayedOnCreateInputConnection");
// Prevent infinite loop when View methods trigger onCreateInputConnection
// on some OEM phones. (crbug.com/636197)
if (mReentrantTriggering) return;

// We need to check this before creating invalidator.
if (!view.hasFocus() || !view.hasWindowFocus()) return;
Expand All @@ -135,8 +147,10 @@ private void triggerDelayedOnCreateInputConnection(final View view) {
if (mProxyView == null) {
mProxyView = createProxyView(mHandler, view);
}
mReentrantTriggering = true;
// This does not affect view focus of the real views.
mProxyView.requestFocus();
mReentrantTriggering = false;

view.getHandler().post(new Runnable() {
@Override
Expand Down

0 comments on commit f5feceb

Please sign in to comment.