How can I load private native libraries from another app

770 Views Asked by At

I have two apps A and B. App A uses DexClassLoader to load a custom view class from app B. This works fine.

However if the custom view in app B loads private native libraries bundled with apk B via java.lang.System.loadLibrary and dalvik.system.PathClassLoader. Then I get

 java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.app-b-xxx==/base.apk"],nativeLibraryDirectories=[/system/lib, /vendor/lib]]] couldn't find "my_native_lib.so"

If I start app B by itself, it is able to load the private native libraries just fine.

Is this supported? I know that DexClassLoader only loads classes, but can those loaded classes then call into their own bundled native libs?

2

There are 2 best solutions below

3
On

You really shouldn't be loading classes directly like that at all, you'll probably get in trouble with Google for it. I believe it's against the TOS of the Play Store. It's also just not safe- if some other app with the same name is installed pretending to be you, you just allowed their code to run as your user, a massive security issue.

But let's ignore that. Here's why it isn't working- your app doesn't have that library. When you load via loadLibrary, it's looking in a particular path that's local to your app for the .so file. "your app" in this context is the app that's running, not the app that the code comes from. So it's looking in the wrong place. It might work if you include the .so file in both apps. But you'd obviously need to know that implementation detail to make it work, you couldn't use this to load any random class' libraries.

0
On

If you're willing to use a bit of reflection, the DexClassLoader contains a private field called pathList that then holds a method called addNativePath(Collection<String>).

To get App B's native library directory, you can load it through the package manager.

// assumes currently in an activity

val nativeLibraryPath = packageManager.getPackageInfo("com.package.name", 0).applicationInfo.nativeLibraryDir

val clClass = classLoader.javaClass
val plField = clClass.superclass.getDeclaredField("pathList")
plField.isAccessible = true

val plInst = plField.get(classLoader)
val plClass = plClass.javaClass

val anpMethod = plClass.getDeclaredMethod("addNativePath", Collection::class.java)
anpMethod.invoke(
  plInst,
  listOf(nativeLibraryPath)
)

Keep in mind that as this uses hidden API methods, this may stop working in some newer version of Android. (The code I wrote was primarily tested on Android 11)

If you're using System.loadLibrary to load the native library (which doesn't seem to be the case based on the provided error), just using System.load with an absolute path to the native library from App B will work without playing around with the class loader.