keymanapp / keyman

Keyman cross platform input methods system running on Android, iOS, Linux, macOS, Windows and mobile and desktop web
https://keyman.com/
Other
391 stars 109 forks source link

bug(android): missing handling for keystrokes dropped due to missing IME->app connection #11549

Open sentry-io[bot] opened 4 months ago

sentry-io[bot] commented 4 months ago

Sentry Issue: KEYMAN-ANDROID-4KA

insertText failed: InputConnection is null
    at com.keyman.engine.KMKeyboardJSHandler$1.run(KMKeyboardJSHandler.java:118)
    at android.os.Handler.handleCallback(Handler.java:942)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loopOnce(Looper.java:226)
    at android.os.Looper.loop(Looper.java:313)
    at android.app.ActivityThread.main(ActivityThread.java:8762)
    at java.lang.reflect.Method.invoke(Method.java)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:604)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)

Corresponds to the following block:

https://github.com/keymanapp/keyman/blob/b068f94537e6bf8703068143473aec7d30ff584c/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardJSHandler.java#L116-L120

This is early on during the process of applying an incoming keystroke. The internal Web engine has already processed it and is actively attempting to synchronize the host-app's context. However, the inputConnection necessary for this... is null. (We currently aren't given an indication as to why.)

Naturally, this will result in a temporary context desync due to the dropped keystroke. Perhaps we could 'store' the keystroke and attempt to apply it again on receiving new input? Or... does this occur during scenarios where the keyboard should no longer be producing output due to being disconnected after the triggering events were received? In the latter case, dropping them does seem reasonable. Sadly... we currently don't know which of the two cases this error is arising from - and that's assuming that it only comes from one or the other.

As an extra note, if we do allow the keystrokes to be dropped in certain scenarios... we probably need to run resetContext() when we make that decision. That will re-synchronize the hosted Web engine's version of the context with what the app has, after all.

jahorton commented 4 months ago

Thinking on it, it's possible that the keyboard is being (or, has been) deactivated in these cases.

Related details:

To me, the implications of this are that the cause is a backgrounded keyboard instance that has been lagging behind and is still processing input. Due to being backgrounded, there is no active context - and thus, no valid InputConnection.

If I'm right about that conclusion, we know that when the keyboard is next "loaded in", we're going to trigger resetContext - and that will resynchronize the app context with the internal Web engine's context.

In case of rapid back-and-forth keyboard swapping, it is theoretically possible for "zombie keystrokes" to exist from prior sessions if the engine hasn't yet caught up with its buffered event queue from before it was backgrounded. I don't think we've gotten any reports of this yet, though, and it would probably take significant work to "flush" them forcefully.