Native libraries not found in ApplicationInfo.nativeLibraryDir when building app bundle for arm64 Android phone
Asked Answered
S

1

20

I am trying to migrate my app from a monolithic APK to the app bundle format. I need to set LD_LIBRARY_PATH environment variable for an exec() call, therefore I need the location of my native libraries. With the original APK I would call getApplicationInfo().nativeLibDir and the libraries were there.

With the app bundle they are not. I can see the correct abi split APK installed, but for some reason the libraries are not extracted.

I have tried installing with bundletool and through Google Play,

Tried to run 'ls -alR' and I can clearly see the directory exists as well as the split apk, but the libraries are simply not extracted. I guess I could extract them manually as a workaround but that would seem unnecessary..?

Here is the output of ls on the parent folder of nativeLibPath

genLibraryPath: Dir Contents: /data/app/com.unseenonline-raAFLhJMQpjqWkVdG1Vocg==:
        total 16704
        drwxr-xr-x   4 system system      4096 2019-06-11 12:41 .
        drwxrwx--x 114 system system     12288 2019-06-11 12:41 ..
        -rw-r--r--   1 system system   5688352 2019-06-11 12:41 base.apk
        drwxr-xr-x   3 system system      4096 2019-06-11 12:41 lib
        drwxrwx--x   3 system install     4096 2019-06-11 12:41 oat
        -rw-r--r--   1 system system  11226112 2019-06-11 12:41 split_config.arm64_v8a.apk
        -rw-r--r--   1 system system     35636 2019-06-11 12:41 split_config.en.apk
        -rw-r--r--   1 system system     90443 2019-06-11 12:41 split_config.xxhdpi.apk

        /data/app/com.unseenonline-raAFLhJMQpjqWkVdG1Vocg==/lib:
        total 24
        drwxr-xr-x 3 system system 4096 2019-06-11 12:41 .
        drwxr-xr-x 4 system system 4096 2019-06-11 12:41 ..
        drwxr-xr-x 2 system system 4096 2019-06-11 12:41 arm64

        /data/app/com.unseenonline-raAFLhJMQpjqWkVdG1Vocg==/lib/arm64:
        total 16
        drwxr-xr-x 2 system system 4096 2019-06-11 12:41 .
        drwxr-xr-x 3 system system 4096 2019-06-11 12:41 ..

As you can see the split apks are there but the libraries are not extracted.

Libraries should be extracted to the same location as they were with the original apk

Surpassing answered 11/6, 2019 at 20:7 Comment(0)
F
24

By default, APKs generated from the Android App Bundle have the native libraries uncompressed on devices with Android M / API level 23 or higher (source). Not only does that often reduce the download size but that also considerably reduce the size of the app on devices since the Android platform can directly read the native libraries from the APK instead of having to extract them to a separate location. There was a talk at last I/O on how to reduce the size of your app and how that impacts install numbers, and they detailed how this works if you're interested in understanding this better.

So, now that you know why Google Play is doing this, you have the following options:

  • You can choose to revert to the original APK behaviour, and this can be done by adding the flag android.bundle.enableUncompressedNativeLibs=false in your gradle.properties file. This will effectively disable this optimization, leading to a bigger size of your app for all your users on M+.

  • You can ensure that the native library is loaded by the Android platform (e.g. using System.loadLibrary) or you if you're reading the library directly yourself for some reason, read it from the APK directly as well.

If the native libraries are loaded by a third party library you're depending on, consider filing a bug for them to address this issue so they follow the same logic as the platform.

Hope that helps,

Flexuous answered 11/6, 2019 at 20:50 Comment(8)
Thanks for you the answer. Is there some standard way to get the byte offset of the native library from the apk? I assume Google wants us to use System.loadLibrary() but what about binaries using shared libraries? Is this completely unsupported now?Surpassing
Perhaps I just need to add this library as an asset if I am using it directly (you are correct, it is a 3rd party library, Otherwise I would statically link it)Surpassing
To find the shared lib in your APK, you can use the AssetManager.openNonAssetFd(). Instead of dlopen(), you should use the platform-specific android_dlopen_ext(), and pass the AssetFileDescriptor's handle and offset. Note that the AssetManager has native API, too.Steeplechase
I believe you can also do dlopen("/path/to/MyApp.apk!libfoo.so", ...). If that doesn't work, could you file a bug? We should make sure there's an easy way to do this (I'm thinking there's should probably be an API that's just "open this library from my APK" so you don't need to figure out the path to your APK... file a bug asking for the API if that's something you want)Latrishalatry
@DanAlbert according to this answer you can call System.loadLibrary() from java and then dlsym(0,..) will work with any library loaded. Not sure if this works for processes spawned with exec() as well or just JNI calls. You could also try to call loadLibrary() java command from within your native code.Surpassing
@EladLevin, thanks for sharing my answer. The nice bonus is that System.loadLibrary() works transparently with non-extracted libraries. Unfortunately, these libraries cannot be shared with spawned executables.Steeplechase
Actually, as @DanAlbert pointed out elsewhre, you can simply use dlopen(libfoo.so), and this will automagically look for the lib in all supported locations, including not-extracted-from the APK or App Bundle.Steeplechase
I think that actually doesn't work if the library isn't extracted... but it ought to. Filed github.com/android-ndk/ndk/issues/1061Latrishalatry

© 2022 - 2024 — McMap. All rights reserved.