-
-
Notifications
You must be signed in to change notification settings - Fork 103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bug(android): getKeyboardInfo with invalid index: -1 #6703
Comments
This fixes crash reported as #6703. This issue was first reported in 14.0.282-stable. I have done a careful review of changes in 14.0.282 (and 14.0.281) but have been unable to find any changes that could have bearing on this. The basic issue is that there appears to be some circumstances where `KMManager` thinks that it has a keyboard loaded (ref `SystemKeyboardLoaded` variable), but `KMKeyboard.currentKeyboard` is still `null`. The crash has been reported for only a very small set of users, 119 at time of fix, but average reports per user is over 100. As is usual with this type of thing, a small fraction of those users are reporting the majority of crashes. I have not found any real commonality across the error reports -- they are geographically dispersed, across multiple device types and Android versions. Note that this addresses the error at hand but as I am unable to reproduce the issue, does not necessarily address the root problem, so there may still be other issues reported even after this is fixed. A longer-term refactor would eliminate `SystemKeyboardLoaded` because from what I can tell, we should always be able to determine that from the state of `KMKeyboard.currentKeyboard`. However, the state entanglement is a lot deeper than just those two variables, with cross references to keyboard indexes between `KMManager` and `KMKeyboard` which need to be resolved (`KMKeyboard` should *never* refer to `KMManager`).
This fixes crash reported as #6703. This issue was first reported in 14.0.282-stable. I have done a careful review of changes in 14.0.282 (and 14.0.281) but have been unable to find any changes that could have bearing on this. The basic issue is that there appears to be some circumstances where `KMManager` thinks that it has a keyboard loaded (ref `SystemKeyboardLoaded` variable), but `KMKeyboard.currentKeyboard` is still `null`. The crash has been reported for only a very small set of users, 119 at time of fix, but average reports per user is over 100. As is usual with this type of thing, a small fraction of those users are reporting the majority of crashes. I have not found any real commonality across the error reports -- they are geographically dispersed, across multiple device types and Android versions. Note that this addresses the error at hand but as I am unable to reproduce the issue, does not necessarily address the root problem, so there may still be other issues reported even after this is fixed. A longer-term refactor would eliminate `SystemKeyboardLoaded` because from what I can tell, we should always be able to determine that from the state of `KMKeyboard.currentKeyboard`. However, the state entanglement is a lot deeper than just those two variables, with cross references to keyboard indexes between `KMManager` and `KMKeyboard` which need to be resolved (`KMKeyboard` should *never* refer to `KMManager`).
Reference for part of that call stack: keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java Lines 1745 to 1748 in 4a2cb36
Upon initial digging, it's pretty clear that keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/KeyboardController.java Lines 160 to 172 in 4a2cb36
So yeah. Definitely a value of My current running theory: if there is no current keyboard somehow, Part of why: for the frame before it in the call-stack... keyman/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java Lines 115 to 149 in 4a2cb36
So, this is happening when the system keyboard is getting loaded for the first time, during initialization. It's possible that it's simply too early in the process for the active keyboard to have been set. Fortunately... the code asking about the keyboard's info just passively skips the affected operation set. This makes me wonder if we have any issues about the prediction banner acting funny or going missing on the initial load of the system keyboard... but I don't see evidence of such an issue in the repo. |
Except that this crashes the app. Please see the fix I've already written in #6704. |
From said fix's description:
I found myself with some extra time to try looking into the root problem, so I did a little digging. I'd rather find the source if it's easy enough to do so. |
Are we sure about this? Granted, the Sentry logs for Android don't exactly make clear when the app or keyboard is truly crashed or if it was a logged error, but... keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/KeyboardController.java Lines 160 to 172 in 4a2cb36
Note line 170 - that's quite clearly a logged error, with no exception thrown. Given that there's no pressing issue in the repo or on the Community site we can point to as an analogue, I think it's just that - a logged error. We should know something if it were an egregious crash of the keyboard. For verification, here's the definition of keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/util/KMLog.java Lines 29 to 42 in adb6ed9
|
(Further investigation, as of #6703 (comment), has all but disproven this.) Further investigation - this is the expected codepath for system keyboard's initialization of the initial keyboard: keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java Lines 2458 to 2481 in adb6ed9
Note this section in particular: keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java Lines 2476 to 2481 in adb6ed9
This code is called whenever the keyboard host page has finished loading. Note how this, by its nature, is almost certainly an asynchronous callback from the WebView; I know this to be the case for the iOS app. Likewise, the true entrypoint for the system keyboard is So, my guess is that we have two asynchronous methods operating in parallel, with one of them expected to finish before the other. But... is there anything guaranteeing this? Is this a race condition? |
Continuing the investigation, keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/KeyboardController.java Lines 160 to 172 in 4a2cb36
However, that same function... doesn't actually initialize the field for tracking the active keyboard. It certainly loads all data for installed keyboards, I'll give you that. But... there's no call to set the keyboard's KeyboardController.initialize() definition, for reference.keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/KeyboardController.java Lines 49 to 113 in adb6ed9
|
An important note for any potential reproduction attempts:
Alternatively... just don't set it as the default system keyboard from the app.
This will ensure that the manufacturer-default keyboard has a globe key for keyboard swapping, allowing you to swap to the Keyman system keyboard. The first time you do so, with the app currently terminated, you'll be able to test how the system keyboard initializes when set to be immediately active. ... not that I've actually achieved a full repro yet; I still haven't reproduced the actual log message when testing against That said... there are logs on Sentry that strongly suggest that a few select users have a far more consistent trigger; their installations will produce log messages at or over 3 times a minute sometimes. It's hard to say what that is without further investigation, though I'm pretty sure that the rate above indicates that it's not happening from just fresh-boot loads of the keyboard. |
It's taken a few passes and some serious debug-logging, but I'm starting to make better sense of some of the codepaths involved now... keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java Lines 2458 to 2469 in adb6ed9
Line 2471 there is the only line in the codebase that sets keyman/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java Lines 131 to 145 in adb6ed9
Line 142 there... keyman/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java Line 142 in adb6ed9
is essentially } else if (SystemKeyboardLoaded) { Therefore... the host page must have loaded for us to reach line 145... keyman/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java Line 145 in adb6ed9
which is the line seen in the Sentry logs' call stack.
Well, I found the "anything": it's that And after a bit more investigation... all lines seen above appear to be executed on the same thread, according to my investigation via Android Studio's debugger. Huh. |
Oh, and other good news - where I'd thought we might have a scenario disrespecting 'privacy mode' for password entry, the same investigation leading to the previous comment's notes has also demonstrated that such a scenario (fortunately) doesn't exist. The active keyboard isn't even referenced when the "prediction override" toggle is set. It just took a number of passes through the codebase to recognize that toggle's related logic for what it was. |
OK, so I've been trying from a new angle. I'm using a couple of recently discovered bugs to try to repro this one.
By using both of these bugs, it's possible to get the app into a state best represented by the following screenshots: I can safely return from Settings without the keyboard resetting. It is possible to leave English installed ( Even then, for our current scenario... by the way our mobile apps work internally, the displayed keyboard uses a different keyboard-language pairing than the only one currently installed. Therefore, if my understanding is correct, the currently active keyboard will not be found in the app's list of installed keyboards. (Yes, even if it's using the same keyboard ID.) From here, this may take some exploration, but this should be a good foundation, usable somehow to get at least one form of repro for this bug. Attempts:
It's also possible that I should try deleting both the installed pairings and just leave the default EuroLatin in place, as I can do so while maintaining the Baka installation initially. That said, some of the failsafes in the code attempt to fall back on the default keyboard; I was hoping to get far enough to trip over even those aspects, as it is possible to uninstall the default keyboard from the app. |
Well... turns out this particular avenue doesn't appear to be viable for a repro attempt.
keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java Lines 2481 to 2491 in adb6ed9
With the repro attempt steps listed above (using other bugs to try reproducing this one), we actually reach line 2490 there; the system defaults back to index 0 when the originally-specified index returned no keyboard info. But, this occurs before any call that would result in this issue's error... ensuring that afterward, a valid index exists. |
…-logging chore(android): adds diagnostic logging to assist a fix for #6703
So, a minor update, given the logging added in #6761: That's almost an absolutely perfect correlation between the two error-log events. |
For that to happen, it's worth noting that the basic keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/KeyboardController.java Lines 222 to 249 in dd1d166
Lines 225, 229, and 248 all return keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/KeyboardController.java Lines 222 to 230 in dd1d166
I point all of the above details out because of how the two Sentry events relate: keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java Lines 2003 to 2016 in dd1d166
Any of the three lines noted above (that return the So, what does that tell us? keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/KeyboardController.java Lines 224 to 227 in dd1d166
Passing the block above means that:
keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/KeyboardController.java Lines 234 to 244 in dd1d166
Again, In the logs, I see that this sometimes arises when the app is resumed after being paused, and sometimes occurs when the device "wakes up" after having been locked / "put to sleep". |
Just a thought here, but... keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/KeyboardController.java Line 237 in dd1d166
Is it possible that case sensitivity is at play here? This code, as written, does assume that case sensitivity is not a factor - that some form of casing standardization has already been applied. And we've run into situations that arose from a lack of enforcement in other areas. I remember it being a factor in #6733. Looking into the code a bit, I can find keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/packages/PackageProcessor.java Lines 72 to 73 in dd1d166
That suggests that the first half of the condition is probably safe. But for the second half... I don't see a clear, definitive answer. I guess there's also keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/KeyboardController.java Line 238 in dd1d166
to look into as well in this regard, though I think that case is sufficiently handled: keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/util/BCP47.java Lines 17 to 23 in dd1d166
|
Following up on the #6732 angle (which was kinda tied to #6733)... The PR that fixed #6732 - that is, #7239 - had this to say early on in its description:
So... are we sure that this only ever affected the temporary list and never the permanent one? Is it possible that after fixing previous case-insensitivity bugs like this... that we never actually re-validated the language list and/or prior keyboard setting? If user settings weren't updated to ensure standardized casing, and thus unstandardized forms of the keyboard ID live on for a select few devices & users... perhaps that could be our underlying cause? It is kinda noteworthy just how few users this bug affects, especially given the frequency at which it affects the users who do experience it. Worth noticing: the core keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/Keyboard.java Lines 56 to 62 in dd1d166
If this line of thinking is correct... maybe just tweaking keyman/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/KeyboardController.java Line 237 in dd1d166
to use Of course, it's probably best to fully patch everything up, lest we have this assumption be invalidated somewhere else in the future again by mistake. @darcywong00: thoughts? While I personally am not 100% convinced on this... there's that old, famous maxim about eliminating the impossible:
I can't say that literally everything impossible has been eliminated, but this is the first angle of "possible" I've been able to find for this issue given all the other things we know thus far. |
Found something interesting while analyzing Sentry error reports today: https://sentry.io/share/issue/bed2060d7ba24e44854b8f70078d6229/ We have proof of at least one wild mixed-case scenario, unless my interpretation's off somehow? Either way, this is clearly within the Keyman for Android app (or engine), and the language ID lasted long enough to be passed off to KMW, as this error came from the Web side. |
From #7415 (comment)
... I can't help but notice now, especially given the odd frequency of related error logs talking about |
I'm confused -- why? |
Because of the language IDs in the package, not the package IDs. |
Yes, language ID comparisons should always have been case-insensitive. |
I think this can be closed now. The languages on that list are not currently installed. |
Sentry Issue: KEYMAN-ANDROID-75
The text was updated successfully, but these errors were encountered: