kiwix / kiwix-android

Kiwix for Android
https://android.kiwix.org
GNU General Public License v3.0
889 stars 446 forks source link

When the app is installed from outside Google Play it crashes often with UnsatisfiedLinkError for io.objectbox.NativeLibraryLoader #2904

Open julianharty opened 2 years ago

julianharty commented 2 years ago

I admit this may be awkward to fix, the main reason I've added it here is this crash is the most frequent one that occurs currently for production releases of the Kiwix Android when it has not been installed from Google Play. It also happens less often (but still enough to appear in the top few crashes) when the app is installed from Google Play.

If we can find a way to address this problem then even those users who find other ways to install the app will be able to use it reliably.

Reported Impact This crash appears to impact both users who installed the app from Google Play and those who installed it from somewhere else. It affects those who installed the app from somewhere else more frequently than those who used Google Play. It does not occur in the top 40 crashes for the Chemistry and Physics simulation or WikiMed (en) apps and it's unlikely it happens in other ones; that said they may not be downloaded as frequently outside Google Play.

There were 6 crash clusters in the first 25 rows (which are ranked in descending order of occurrences) for this crash cluster: java.lang.UnsatisfiedLinkError io.objectbox.internal.NativeLibraryLoader.<clinit>

Occurrences in last 30 days in Google Play Occurrences in last 30 days total Total occurrences Impacted users URL to crash cluster
123 1990 2090 161 Cluster 1f31cfb3
66 411 411 14 Cluster 1aa80b6e
54 384 1120 120 Cluster 6b09a04c
3 26 119 24 Cluster 6c6e4c6c
0 18 22 4 Cluster 0cd991b5
0 17 17 2 Cluster 9b1318f4

The first column is when the Installed from Google Play filter is not used, the second column is when that filter has been applied. The rest of the numbers are unchanged regardless of whether that filter is applied. Tip, the URL has an extra parameter appended when we filter the installs e.g. Cluster 9b1318f4 with &installedFrom=PLAY_STORE

The crash occurs in various app bundles (unique app versions), on various device models, and on various Android versions. It also occurs when the app is in the foreground and in the background (occasionally).

Steps to reproduce the behavior: The steps to reproduce this issue are not known currently, this issue is being reported in Android Vitals, see the first screenshot with the following filters applied:

Screenshots Screenshot 2022-07-09 at 15 17 40

Top crash cluster with Installed from Google Play filter Cluster 1f31cfb3 Screenshot 2022-07-09 at 15 34 59

Second crash cluster without the Installed from Google Play filter: Screenshot 2022-07-09 at 15 59 13

Expected behavior The app should not crash.

That said, perhaps the best we can do is detect an issue where the app cannot find the relevant runtime file and display a message to help the end-user recognise what the problem is, or perhaps we could help them work out how to manually install the file, etc?

Environment Production releases of Kiwix Android 3.4.5 at the time this bug was raised.

Logs Here's an example of the most recent stacktrace for this crash from the most frequent (top) crash cluster, Cluster 1f31cfb3 :

java.lang.LinkageError: 
  at io.objectbox.internal.NativeLibraryLoader.<clinit> (NativeLibraryLoader.java:109)
  at io.objectbox.internal.NativeLibraryLoader.ensureLoaded (NativeLibraryLoader.java)
  at io.objectbox.BoxStore.<init> (BoxStore.java:257)
  at io.objectbox.BoxStoreBuilder.build (BoxStoreBuilder.java:498)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule.providesBoxStore (DatabaseModule.java:44)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesBoxStoreFactory.providesBoxStore (DatabaseModule_ProvidesBoxStoreFactory.java:40)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesBoxStoreFactory.get (DatabaseModule_ProvidesBoxStoreFactory.java:31)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesBoxStoreFactory.get (DatabaseModule_ProvidesBoxStoreFactory.java:10)
  at dagger.internal.DoubleCheck.get (DoubleCheck.java:47)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesFetchDownloadDaoFactory.get (DatabaseModule_ProvidesFetchDownloadDaoFactory.java:35)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesFetchDownloadDaoFactory.get (DatabaseModule_ProvidesFetchDownloadDaoFactory.java:11)
  at dagger.internal.DoubleCheck.get (DoubleCheck.java:47)
  at org.kiwix.kiwixmobile.core.downloader.fetch.FetchDownloadMonitor_Factory.get (FetchDownloadMonitor_Factory.java:30)
  at org.kiwix.kiwixmobile.core.downloader.fetch.FetchDownloadMonitor_Factory.get (FetchDownloadMonitor_Factory.java:9)
  at org.kiwix.kiwixmobile.core.di.modules.ApplicationModule_ProvideDownloadMonitor$core_releaseFactory.get (ApplicationModule_ProvideDownloadMonitor.java:31)
  at org.kiwix.kiwixmobile.core.di.modules.ApplicationModule_ProvideDownloadMonitor$core_releaseFactory.get (ApplicationModule_ProvideDownloadMonitor.java:10)
  at dagger.internal.DoubleCheck.get (DoubleCheck.java:47)
  at org.kiwix.kiwixmobile.core.di.components.DaggerCoreComponent.injectCoreApp (DaggerCoreComponent.java:437)
  at org.kiwix.kiwixmobile.core.di.components.DaggerCoreComponent.inject (DaggerCoreComponent.java:409)
  at org.kiwix.kiwixmobile.core.CoreApp.onCreate (CoreApp.kt:85)
  at android.app.Instrumentation.callApplicationOnCreate (Instrumentation.java:1189)
  at android.app.ActivityThread.handleBindApplication (ActivityThread.java:6603)
  at android.app.ActivityThread.access$1500 (ActivityThread.java:235)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1916)
  at android.os.Handler.dispatchMessage (Handler.java:107)
  at android.os.Looper.loop (Looper.java:225)
  at android.app.ActivityThread.main (ActivityThread.java:7563)
  at java.lang.reflect.Method.invoke (Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:539)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:994)
Caused by: java.lang.UnsatisfiedLinkError: 
  at java.lang.Runtime.loadLibrary0 (Runtime.java:1067)
  at java.lang.Runtime.loadLibrary0 (Runtime.java:1007)
  at java.lang.System.loadLibrary (System.java:1667)
  at io.objectbox.internal.NativeLibraryLoader.<clinit> (NativeLibraryLoader.java:84)
  at io.objectbox.internal.NativeLibraryLoader.ensureLoaded (NativeLibraryLoader.java)
  at io.objectbox.BoxStore.<init> (BoxStore.java:257)
  at io.objectbox.BoxStoreBuilder.build (BoxStoreBuilder.java:498)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule.providesBoxStore (DatabaseModule.java:44)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesBoxStoreFactory.providesBoxStore (DatabaseModule_ProvidesBoxStoreFactory.java:40)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesBoxStoreFactory.get (DatabaseModule_ProvidesBoxStoreFactory.java:31)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesBoxStoreFactory.get (DatabaseModule_ProvidesBoxStoreFactory.java:10)
  at dagger.internal.DoubleCheck.get (DoubleCheck.java:47)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesFetchDownloadDaoFactory.get (DatabaseModule_ProvidesFetchDownloadDaoFactory.java:35)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesFetchDownloadDaoFactory.get (DatabaseModule_ProvidesFetchDownloadDaoFactory.java:11)
  at dagger.internal.DoubleCheck.get (DoubleCheck.java:47)
  at org.kiwix.kiwixmobile.core.downloader.fetch.FetchDownloadMonitor_Factory.get (FetchDownloadMonitor_Factory.java:30)
  at org.kiwix.kiwixmobile.core.downloader.fetch.FetchDownloadMonitor_Factory.get (FetchDownloadMonitor_Factory.java:9)
  at org.kiwix.kiwixmobile.core.di.modules.ApplicationModule_ProvideDownloadMonitor$core_releaseFactory.get (ApplicationModule_ProvideDownloadMonitor.java:31)
  at org.kiwix.kiwixmobile.core.di.modules.ApplicationModule_ProvideDownloadMonitor$core_releaseFactory.get (ApplicationModule_ProvideDownloadMonitor.java:10)
  at dagger.internal.DoubleCheck.get (DoubleCheck.java:47)
  at org.kiwix.kiwixmobile.core.di.components.DaggerCoreComponent.injectCoreApp (DaggerCoreComponent.java:437)
  at org.kiwix.kiwixmobile.core.di.components.DaggerCoreComponent.inject (DaggerCoreComponent.java:409)
  at org.kiwix.kiwixmobile.core.CoreApp.onCreate (CoreApp.kt:85)
  at android.app.Instrumentation.callApplicationOnCreate (Instrumentation.java:1189)
  at android.app.ActivityThread.handleBindApplication (ActivityThread.java:6603)
  at android.app.ActivityThread.access$1500 (ActivityThread.java:235)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1916)
  at android.os.Handler.dispatchMessage (Handler.java:107)
  at android.os.Looper.loop (Looper.java:225)
  at android.app.ActivityThread.main (ActivityThread.java:7563)
  at java.lang.reflect.Method.invoke (Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:539)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:994)

For the second most frequent crash cluster, Cluster 1aa80b6e :

java.lang.LinkageError: 
  at io.objectbox.internal.NativeLibraryLoader.<clinit> (NativeLibraryLoader.java:109)
  at io.objectbox.BoxStore.<init> (BoxStore.java:257)
  at io.objectbox.BoxStoreBuilder.build (BoxStoreBuilder.java:498)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule.providesBoxStore (DatabaseModule.java:44)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesBoxStoreFactory.providesBoxStore (DatabaseModule_ProvidesBoxStoreFactory.java:40)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesBoxStoreFactory.get (DatabaseModule_ProvidesBoxStoreFactory.java:31)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesBoxStoreFactory.get (DatabaseModule_ProvidesBoxStoreFactory.java:10)
  at dagger.internal.DoubleCheck.get (DoubleCheck.java:47)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesFetchDownloadDaoFactory.get (DatabaseModule_ProvidesFetchDownloadDaoFactory.java:35)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesFetchDownloadDaoFactory.get (DatabaseModule_ProvidesFetchDownloadDaoFactory.java:11)
  at dagger.internal.DoubleCheck.get (DoubleCheck.java:47)
  at org.kiwix.kiwixmobile.core.downloader.fetch.FetchDownloadMonitor_Factory.get (FetchDownloadMonitor_Factory.java:30)
  at org.kiwix.kiwixmobile.core.downloader.fetch.FetchDownloadMonitor_Factory.get (FetchDownloadMonitor_Factory.java:9)
  at org.kiwix.kiwixmobile.core.di.modules.ApplicationModule_ProvideDownloadMonitor$core_releaseFactory.get (ApplicationModule_ProvideDownloadMonitor.java:31)
  at org.kiwix.kiwixmobile.core.di.modules.ApplicationModule_ProvideDownloadMonitor$core_releaseFactory.get (ApplicationModule_ProvideDownloadMonitor.java:10)
  at dagger.internal.DoubleCheck.get (DoubleCheck.java:47)
  at org.kiwix.kiwixmobile.core.di.components.DaggerCoreComponent.injectCoreApp (DaggerCoreComponent.java:437)
  at org.kiwix.kiwixmobile.core.di.components.DaggerCoreComponent.inject (DaggerCoreComponent.java:409)
  at org.kiwix.kiwixmobile.core.CoreApp.onCreate (CoreApp.kt:85)
  at android.app.Instrumentation.callApplicationOnCreate (Instrumentation.java:1192)
  at android.app.ActivityThread.handleBindApplication (ActivityThread.java:7573)
  at android.app.ActivityThread.access$1500 (ActivityThread.java:301)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2166)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loop (Looper.java:246)
  at android.app.ActivityThread.main (ActivityThread.java:8633)
  at java.lang.reflect.Method.invoke (Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:602)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1130)
Caused by: java.lang.UnsatisfiedLinkError: 
  at java.lang.Runtime.loadLibrary0 (Runtime.java:1087)
  at java.lang.Runtime.loadLibrary0 (Runtime.java:1008)
  at java.lang.System.loadLibrary (System.java:1664)
  at io.objectbox.internal.NativeLibraryLoader.<clinit> (NativeLibraryLoader.java:84)
  at io.objectbox.BoxStore.<init> (BoxStore.java:257)
  at io.objectbox.BoxStoreBuilder.build (BoxStoreBuilder.java:498)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule.providesBoxStore (DatabaseModule.java:44)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesBoxStoreFactory.providesBoxStore (DatabaseModule_ProvidesBoxStoreFactory.java:40)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesBoxStoreFactory.get (DatabaseModule_ProvidesBoxStoreFactory.java:31)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesBoxStoreFactory.get (DatabaseModule_ProvidesBoxStoreFactory.java:10)
  at dagger.internal.DoubleCheck.get (DoubleCheck.java:47)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesFetchDownloadDaoFactory.get (DatabaseModule_ProvidesFetchDownloadDaoFactory.java:35)
  at org.kiwix.kiwixmobile.core.di.modules.DatabaseModule_ProvidesFetchDownloadDaoFactory.get (DatabaseModule_ProvidesFetchDownloadDaoFactory.java:11)
  at dagger.internal.DoubleCheck.get (DoubleCheck.java:47)
  at org.kiwix.kiwixmobile.core.downloader.fetch.FetchDownloadMonitor_Factory.get (FetchDownloadMonitor_Factory.java:30)
  at org.kiwix.kiwixmobile.core.downloader.fetch.FetchDownloadMonitor_Factory.get (FetchDownloadMonitor_Factory.java:9)
  at org.kiwix.kiwixmobile.core.di.modules.ApplicationModule_ProvideDownloadMonitor$core_releaseFactory.get (ApplicationModule_ProvideDownloadMonitor.java:31)
  at org.kiwix.kiwixmobile.core.di.modules.ApplicationModule_ProvideDownloadMonitor$core_releaseFactory.get (ApplicationModule_ProvideDownloadMonitor.java:10)
  at dagger.internal.DoubleCheck.get (DoubleCheck.java:47)
  at org.kiwix.kiwixmobile.core.di.components.DaggerCoreComponent.injectCoreApp (DaggerCoreComponent.java:437)
  at org.kiwix.kiwixmobile.core.di.components.DaggerCoreComponent.inject (DaggerCoreComponent.java:409)
  at org.kiwix.kiwixmobile.core.CoreApp.onCreate (CoreApp.kt:85)
  at android.app.Instrumentation.callApplicationOnCreate (Instrumentation.java:1192)
  at android.app.ActivityThread.handleBindApplication (ActivityThread.java:7573)
  at android.app.ActivityThread.access$1500 (ActivityThread.java:301)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2166)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loop (Looper.java:246)
  at android.app.ActivityThread.main (ActivityThread.java:8633)
  at java.lang.reflect.Method.invoke (Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:602)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1130)

I've not included screenshots or stack traces for crash clusters 3 to 6 to save space. They can be added if they're useful.

julianharty commented 2 years ago

This is the first issue reported as part of https://github.com/kiwix/kiwix-android/issues/2903

kelson42 commented 2 years ago

@MohitMaliFtechiz Do you have an explanation? Would that bug be easy to fix?

MohitMaliFtechiz commented 2 years ago

hi @kelson42 ,

This bug usually comes when we installed apk outside the google play due to mismatch installation of architecture. please check the following link https://github.com/objectbox/objectbox-java/issues/605. Some users using architecture which is not supported by ObjectBox.

kelson42 commented 2 years ago

@MohitMaliFtechiz Thx, we have not to identify such a situation at start and make managed exit with clear error? I wonder as well a bit that kiwix starts at all if the code does not match the architecture!

kelson42 commented 2 years ago

@MohitMaliFtechiz Any feedabck? We should avoid the crash and detect the problem earlier to have a managed exit. What can be done?

MohitMaliFtechiz commented 2 years ago

Hi @kelson42 , Proper handling for this situation is being done on the above PR.

julianharty commented 2 years ago

@MohitMaliFtechiz and @kelson42 Sorry for the late code review, however I've noticed the current message is a bit unclear for the users and I think we can make the message clearer. I've added a code review to the PR.

MohitMaliFtechiz commented 2 years ago

2959

MohitMaliFtechiz commented 2 years ago

reopen now because of F-Droid publish FOSS issue.

kelson42 commented 2 years ago

Copied from https://github.com/kiwix/kiwix-android/issues/2959#issuecomment-1219460710

@kelson42 @MohitMaliFtechiz Here are some ideas about ways we can address the needs of #2904 for both the F-Droid Build and the Google Play Builds while also helping the end users. I expect there will be some details to resolve in terms of the implementation.

  1. We want the apps to work regardless of where users obtain them from.
  2. Google provides an API and a library that can a) detect when the files are missing and b) presents the end-user with a dialog asking them to visit Google Play to download the app again, c) it provides the underlying functionality to download the missing files (possibly as a complete re-install, I don't know how it works exactly).
  3. If we incorporate the Google Android approach (as we have done recently) users who are willing and able to use Google Play can download the missing files and use the app without the objectbox crash. However, this approach does not work well for:
    • the F-droid app.
    • Users who can't, don't, or won't use Google Play.
    • Users who want to share the app locally.

Potentially we could write our own detection code and UI (e.g. to help end users find a local solution e.g. if they've copied the app binary locally perhaps they could also copy across the missing files). And similarly, we could potentially write our own download code to obtain the missing files from a) Google Play (if that's practical and they let us do so programmatically) and b) our own servers.

If the download code + hosting the necessary files isn't viable then the first two aspects (writing our own detection code and UI) combined with code that then exits the app (similar to how the Google approach works) could be an alternative.

Another suggestion is that we only implement the Google-based code in the builds that target Google Play, however that'd probably add to the complexity of our code.

What do you think? would any of these suggestions help? Thank you

Julian

kelson42 commented 2 years ago

AFAIK, we need a FOSS compatible (to the contrary of previous attempt) way to check at start if the APK architecture is the same at the runtime architecture. I'm not in favour to write our own stuff, but maybe there just a simple smart way to do so. Actually I wonder a bit that the runtime engine does not check it!

kelson42 commented 2 years ago

@gouri-panda I think this whole problem won't exist anymore with Room. Please confirm. If "yes", we should clean all the PR which have already been done for this ticket, in particular #2915.

gouri-panda commented 2 years ago

@kelson42 I don't know much about this ticket but yes if the objectbox causing this issue it won't exist after the migration. We'll see after the migration :)