Why do some Android phones cause our app to throw an java.lang.UnsatisfiedLinkError?

EDIT: Since I got another crash report yesterday for one of my apps I dug a bit deeper into the matter and found a third very likely explanation for that problem:

Google Play Partial APK Update Goes Wrong

To be honest, I did not know about that feature. The APK file name suffix “-2.apk” made me suspicious. It is mentioned in the crash message of this question here and I could also find that suffix in the crash report of that customer of mine.

I believe the “-2.apk” hints at a partial update that probably delivers a smaller delta to Android devices. That delta apparently does not contain native libraries when they did not change since the previous version.

For whatever reason the System.loadLibrary function tries to look up the native library from the partial update (where it doesn’t exist). It’s both a flaw in Android and in Google Play.

Here is a very relevant bug report with an interesting discussion about similar observations: https://code.google.com/p/android/issues/detail?id=35962

It looks like Jelly Bean may be flawed in terms of native library installation and loading (both crash reports that came in were Jelly Bean devices).

If this is really the case I suspect that some forced change in the NDK library code may fix the problem, like changing some unused dummy variable for each release. However, that should be done in a way that the compiler preserves that variable without optimizing it away.

EDIT (12/19/13): The idea of changing the native library code for each build unfortunately does not work. I tried it with one of my apps and got an “unsatisfied link error” crash report from a customer who updated anyway.

Incomplete APK installation

That’s unfortunately just out of my memory and I cannot find a link anymore. Last year I read a blog article about that unsatisfied link issue. The author said that this is a bug in the APK installation routine.

When copying native libraries to their target directory fails for whatever reason (device ran out of storage space, maybe also messed up directory write permissions…) the installer still returns “success”, as if native libraries were just “optional extensions” to an app.

In this case the only workaround would be reinstalling the APK while making sure there’s enough storage space for the app.

However, I cannot find any Android bug ticket nor the original blog article anymore and I searched for it for quite a bit. So this explanation may be in the realms of myths and legends.

“armeabi-v7a” directory takes precedence over “armeabi” directory

This bug ticket discussion hints at a “concurrency issue” with installed native libraries, see Android bug ticket #9089.

If there is an “armeabi-v7a” directory present with just a single native library, the whole directory for that architecture takes precedence over the “armeabi” directory.

If you try to load a library that is just present in “armeabi” you’ll get an UnsatisfiedLinkException. That bug has been flagged as “works as intended” by the way.

Possible workaround

In either case: I found an interesting answer to a similar question here on SO. It all boils down to packaging all native libraries as raw resources to your APK and copy on first app start the correct ones for the current processor architecture to the (app private) file system. Use System.load with the full file paths to load these libraries.

However this workaround has a flaw: since the native libraries will reside as resources in the APK Google Play won’t be able to find them and create device filters for the mandatory processor architectures anymore. It could be worked around by putting “dummy” native libraries into the lib folder for all target architectures.

Overall I do believe this issue should be properly communicated to Google. It seems as if both Jelly Bean and Google Play are flawed.

It usually helps to tell the customer with that problem to reinstall the app. This is unfortunately not a good solution if app data loss is a concern.

Leave a Comment