Issue metadata
Sign in to add a comment
|
Android WebView hangs when WebView is in the keyboard process itself
Reported by
o...@swiftkey.com,
Oct 26 2016
|
||||||||||||||||||||||
Issue descriptionSteps to reproduce the problem: 1. Select a keyboard app 2. Open the settings from the launcher icon for the keyboard (android appears to 3. Navigate to a screen with a WebView (in this case a login screen for Twitter so users can use their tweets to build a more personal set of predictions) 4. Keyboard appears after a long delay and any key presses aren't reflected in the input element What is the expected behavior? The keyboard doesn't lag and the text typed appears in the text field What went wrong? I think we've managed to track this down to a recent rollout of the ThreadedInputConnection to Android's WebView: https://bugs.chromium.org/p/chromium/issues/detail?id=551193 The InputConnectionHandlerThread hangs. This causes the keyboard to appear laggy because it's constantly timing out (I think Android sets this timeout to 2 seconds by default) and we can't communicate with the WebView in order to insert and edit text successfully We seem to be able to replicate this across all versions of our app including ones that previously worked I wasn't able to follow the native traces. I hold out hope that we might be able to remedy this on our side - if you have any advice for deciphering these native traces it would be appreciated I've attached a trace to this report but please let me know if you would prefer an APK to help reproduce this issue Did this work before? Yes Does this work in other browsers? Yes Chrome version: M54 Channel: n/a OS Version: L&M&N Flash Version:
,
Oct 31 2016
,
Oct 31 2016
cc'ing chanwan@ and aelias@ for IME stuff :)
,
Oct 31 2016
,
Oct 31 2016
It looks like "main" and "InputConnectionHandlerThread" deadlocked each other? My gueses: * this is an edge case where the keyboard is in process, so binder calls just become local calls * webview generally allows InputConnectionHandlerThread to block and wait on the UI thread, as the stack shows * app blocks UI thread on the same thread? ie whatever InputContextCallback.waitForResultLocked is waiting for. "main" prio=5 tid=1 TimedWaiting | group="main" sCount=1 dsCount=0 obj=0x75ba6cf0 self=0x7f8fb3ba00 | sysTid=30913 nice=-4 cgrp=default sched=0/0 handle=0x7f93174fe8 | state=S schedstat=( 20235459927 4233715214 15910 ) utm=1406 stm=617 core=0 HZ=100 | stack=0x7fc9070000-0x7fc9072000 stackSize=8MB | held mutexes= at java.lang.Object.wait!(Native method) - waiting on <0x0d3e6c38> (a com.android.internal.view.InputConnectionWrapper$InputContextCallback) at java.lang.Object.wait(Object.java:423) at com.android.internal.view.InputConnectionWrapper$InputContextCallback.waitForResultLocked(InputConnectionWrapper.java:186) at com.android.internal.view.InputConnectionWrapper.getTextAfterCursor(InputConnectionWrapper.java:203) - locked <0x0d3e6c38> (a com.android.internal.view.InputConnectionWrapper$InputContextCallback) at com.touchtype.KeyboardService.onStartInput(KeyboardService.java:140) at android.inputmethodservice.InputMethodService.doStartInput(InputMethodService.java:1663) at android.inputmethodservice.InputMethodService$InputMethodImpl.startInput(InputMethodService.java:408) at android.inputmethodservice.IInputMethodWrapper.executeMessage(IInputMethodWrapper.java:172) at com.android.internal.os.HandlerCaller$MyHandler.handleMessage(HandlerCaller.java:37) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:234) at android.app.ActivityThread.main(ActivityThread.java:5526) at java.lang.reflect.Method.invoke!(Native method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) "InputConnectionHandlerThread" prio=5 tid=73 Waiting | group="main" sCount=1 dsCount=0 obj=0x13c37160 self=0x7f726d4a00 | sysTid=980 nice=5 cgrp=default sched=0/0 handle=0x7f67719440 | state=S schedstat=( 2376718 2881461 6 ) utm=0 stm=0 core=0 HZ=100 | stack=0x7f67617000-0x7f67619000 stackSize=1037KB | held mutexes= at java.lang.Object.wait!(Native method) - waiting on <0x0f28e7dc> (a java.lang.Object) at java.lang.Thread.parkFor$(Thread.java:1220) - locked <0x0f28e7dc> (a java.lang.Object) at sun.misc.Unsafe.park(Unsafe.java:299) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:158) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2013) at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:410) at org.chromium.content.browser.input.ThreadedInputConnection.blockAndGetStateUpdate(ThreadedInputConnection.java:281) at org.chromium.content.browser.input.ThreadedInputConnection.requestAndWaitForTextInputState(ThreadedInputConnection.java:241) at org.chromium.content.browser.input.ThreadedInputConnection.getExtractedText(ThreadedInputConnection.java:394) at com.android.internal.view.IInputConnectionWrapper.executeMessage(IInputConnectionWrapper.java:273) at com.android.internal.view.IInputConnectionWrapper$MyHandler.handleMessage(IInputConnectionWrapper.java:78) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:234) at android.os.HandlerThread.run(HandlerThread.java:61)
,
Oct 31 2016
We reached the same conclusions - that when keyboard and web view are in the same process, they both end up wanting the same resource - we're running on the assumption that it is the main thread, but I'm not 100% sure given I don't know how to decipher the native stack traces. This only seems to happen for .get* calls on the input connection, and also seem to sometimes hang when we call getCurrentInputConnection() although this doesn't seem to be every time.
,
Nov 1 2016
OK, I can repro. After installing Swiftkey and going through all first-run setup, go to SwiftKey homescreen icon -> Account -> Personalization -> Twitter. Then tap on "Username" field, and SwiftKey will ANR.
Looks like Keyboard UI thread and WebView UI thread are the same thread in this scenario ("main", I believe). InputConnectionHandlerThread requires the WebView UI thread to be lively, and the keyboard UI thread blocks on response from InputConnectionHandlerThread. Because of some other proxying, it seems like it doesn't matter that Binder is synchronous -- the problem is the explicit blocking wait in https://github.com/android/platform_frameworks_base/blob/master/core/java/com/android/internal/view/InputConnectionWrapper.java . The problem applies to all InputConnection method named "get":
- getTextAfterCursor(), getTextBeforeCursor(), getSelectedText(), getCursorCapsMode(), and getExtractedText().
(Note that requestCursorUpdates() does not have a blocking wait on the InputConnectionHandlerThread side, so it's not affected.)
Here is some brainstorming on possible ways to fix:
A) SwiftKey creates a new proxy shim thread for their use of InputConnection. Uses of these synchronous getters would all need to be rewritten to using an async-await pattern instead of normal function calls.
B) SwiftKey creates these WebViews in a separate service with its own process, activity and UI thread.
C) SwiftKey uses a Chrome Custom Tab instead of a WebView for these.
D) SwiftKey finds some way to reject being attached to these textboxes so that the stock keyboard (Google keyboard or OEM keyboard) is used for them instead.
E) WebView rewrites its IME message queue logic to avoid blocking on the UI thread (the main goal is to block on Blink thread).
None of these jump out as obviously super quick and minimal workarounds, unfortunately.
,
Nov 1 2016
Unfortunately, although we generally have the goal of not to causing any app-side regressions, I don't think we can take any action on the WebView side to help you for 54. We already went stable and the possible fixes (option E, or rolling back to 53-style ReplicaInputConnection implementation) pose too many risks of causing other regressions. I skimmed the last 3 days of reviews for SwiftKey on the Play Store (several hundred reviews) and saw 4 reviews complaining about the hang, so this UI flow seems rarely used enough that it doesn't justify taking a risk on our end. So please consider the workaround options above and choose the one that's easiest for you. In the future, I'd encourage all SwiftKey developers/testers to join the WebView beta channel and file bugs as soon as you notice a bad interaction with SwiftKey, so we can prevent issues like this from making it to stable channel.
,
Nov 1 2016
Some general advice that would apply regardless of this issue: WebView costs a lot of RAM in a process that uses it, and some of that RAM is never freed, even if all webviews are destroyed (chromium's global state can't be cleaned up). Therefore, if your app has a long-running component which does not itself use WebView (in this case, the keyboard itself, which can live a very long time if it's the selected IME), and some short-lived components that do use WebView (your configuration activity), there can be a significant benefit to memory usage to put these components in separate processes. We've previously given the same advice to apps like music players that have a long running playback service but use WebView in part of their UI. So, while this isn't necessarily something you can do rapidly as an immediate resolution to this issue, it may be worth doing it anyway, even if this issue is resolved another way.
,
Nov 1 2016
Thanks for investigating this with such urgency. I think our preference would be to go for workaround A or B, however, as this bug affects all versions of the app, we'll need to roll out a patch urgently. There are few negative reviews but we're certainly seeing noise from other support channels and are seeing an order of magnitude more daily ANRs in the app which seems to correlate with the rollout of 54 (attached). We'd rather not leave the patch in the codebase permanently, is this something you are considering fixing in a future release or should we plan to put in a more permanent fix sooner?
,
Nov 2 2016
Me and changwan@ discussed the problem today and we decided that it's not reasonable for us to fix this in a future release. Option E is the only path forward for us and it would be a large and disruptive change. The only known app that would see a benefit is SwiftKey, but by the time we would ship the fix, it would kind of not, given the workaround on your side would beat us to the punch anyway. So please assume this threading restriction is permanent from now on. I'll be happy to answer any questions you may have about how best to implement option A or B. I apologize for the regression and I'm sorry we can't help with more than advice.
,
Nov 12 2016
seems there is nothing we can do. |
|||||||||||||||||||||||
►
Sign in to add a comment |
|||||||||||||||||||||||
Comment 1 by o...@swiftkey.com
, Oct 26 2016