Android compatibility with 32-bit libraries on a 64-bit device

Recently I run into a problem with an app we’re working on: suddenly it stopped to work on a Samsung Galaxy S6 Edge. It was still working on all the other, older devices. The first idea was: the app is using a JNI library, so it’s a 64-bit compatibility problem. And bingo! This was the reason… but in an unexpected way.

The strange thing was that the app used to work on this device, and with this library (SQLcipher, to keep data secure on rest). So we started to see what changed: the OS recently got an update, to 5.1.1. Maybe there was a compatibility provision on the OS, but after this OS upgrade this had been disabled.

After some investigation, I found the following post in stackoverflow.com: Use 32-bit jni libraries on 64-bit android. Basically the answer stated that as far as you use System.loadLibrary() to load your JNI libraries, a 64-bit OS will try to load the 32-bit version of your library if it doesn’t find a 64-bit version. But this was not happening any more!

After some further investigation, I found that recently we added a module provided by a third party, which had a dependency to another library that had compatibility to both 32 and 64-bit. I’ve added ** next to each of the offending files.

$ unzip -l myapp-development-debug.apk
...
  186244 09-10-15 11:31 lib/armeabi/libdatabase_sqlcipher.so
 2478904 09-10-15 11:31 lib/armeabi/libsqlcipher_android.so
  390456 09-10-15 11:31 lib/armeabi/libstlport_shared.so
  182156 09-10-15 11:31 lib/armeabi-v7a/libdatabase_sqlcipher.so
 2421584 09-10-15 11:31 lib/armeabi-v7a/libsqlcipher_android.so
  374076 09-10-15 11:31 lib/armeabi-v7a/libstlport_shared.so
 1580868 09-10-15 11:31 lib/x86/libdatabase_sqlcipher.so
 3897372 09-10-15 11:31 lib/x86/libsqlcipher_android.so
  563148 09-10-15 11:31 lib/x86/libstlport_shared.so
   34224 09-09-15 13:29 lib/arm64-v8a/libpl_droidsonroids_gif.so **
    9624 09-09-15 13:29 lib/arm64-v8a/libpl_droidsonroids_gif_surface.so **
   38064 09-09-15 13:29 lib/armeabi/libpl_droidsonroids_gif.so
   17572 09-09-15 13:29 lib/armeabi/libpl_droidsonroids_gif_surface.so
   29884 09-09-15 13:29 lib/armeabi-v7a/libpl_droidsonroids_gif.so
   13488 09-09-15 13:29 lib/armeabi-v7a/libpl_droidsonroids_gif_surface.so
   73488 09-09-15 13:29 lib/mips/libpl_droidsonroids_gif.so **
   71132 09-09-15 13:29 lib/mips/libpl_droidsonroids_gif_surface.so **
   41544 09-09-15 13:29 lib/mips64/libpl_droidsonroids_gif.so **
   10360 09-09-15 13:29 lib/mips64/libpl_droidsonroids_gif_surface.so **
   33868 09-09-15 13:29 lib/x86/libpl_droidsonroids_gif.so
    9280 09-09-15 13:29 lib/x86/libpl_droidsonroids_gif_surface.so
   34416 09-09-15 13:29 lib/x86_64/libpl_droidsonroids_gif.so **
    9816 09-09-15 13:29 lib/x86_64/libpl_droidsonroids_gif_surface.so **
   95397 09-14-15 15:31 META-INF/MANIFEST.MF
   95426 09-14-15 15:31 META-INF/CERT.SF
    1318 09-14-15 15:31 META-INF/CERT.RSA
 --------               -------
 44599565               947 files

 
By simply using unzip tool we can clearly see the directories for the arm64-v8a and x86_64 architectures, and also the mips and mips64. In this directories the only present library are libpl_droidsonroids_gif and libpl_droidsonroids_gif_surface.

A quick and dirty solution to get rid of this libraries is to manually use “zip -d” to remove the unwanted architectures, like this:

zip -d myapp-app-development-debug.apk "lib/x86_64/*"
zip -d myapp-app-development-debug.apk "lib/mips64/*"
zip -d myapp-app-development-debug.apk "lib/mips/*"
zip -d myapp-app-development-debug.apk "lib/arm64-v8a/*"

 
Of course, this is useful to test that this is the cause of the crash, but not something you want to do on each build.

So, to automate this steps in Gradle we can use packagingOptions.exclude in the android section, like this (parts omitted):

android {
    compileSdkVersion Integer.parseInt(project.ANDROID_COMPILE_SDK_VERSION)
    buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION

    defaultConfig {
        ...
    }

    ...

    packagingOptions {
        exclude 'META-INF/LICENSE.txt'

        exclude 'lib/arm64-v8a/libpl_droidsonroids_gif.so'
        exclude 'lib/arm64-v8a/libpl_droidsonroids_gif_surface.so'
        exclude 'lib/x86_64/libpl_droidsonroids_gif.so'
        exclude 'lib/x86_64/libpl_droidsonroids_gif_surface.so'
        exclude 'lib/mips/libpl_droidsonroids_gif.so'
        exclude 'lib/mips/libpl_droidsonroids_gif_surface.so'
        exclude 'lib/mips64/libpl_droidsonroids_gif.so'
        exclude 'lib/mips64/libpl_droidsonroids_gif_surface.so'
    }

}

 
Notice that exclude doesn’t allow globs or wildcards, and we have to specify each file manually.