Android Studio: Package Native Libraries into APK

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Android Studio: Package Native Libraries into APK

bmcclint
This post was updated on .
I am trying to get JOGAMPs JOAL (OpenAL) to package into an APK for my Android applications.  The code compiles and generates the expected APK file but when executed on the device it gets a dlopen failed: library "//natives/android-aarch64//libgluegen-rt.so" not found.  

I understand the error, or so I think.  It cannot find the native library libgluegen-rt.so.  

I agree it's not on the phone.  I'm hoping to package it inside the APK.  I suspect the error is telling me it cannot find it at the root of the classpath.  Then IF it was in the APK I think it would find it.  Or is it telling me it cannot find the .so file at the device level file system?  I'm not sure.  

For reference I created the same project first as a regular Java application in eclipse, using Gradle and shadowJar and I get the 'fat' jar I am expecting.  At the root of the jar, there is a natives folder along with the expected folders and files.  Runs as expected.  Replicating as close as possible in a simple 'empty activity' project it compiles as well as installs and aborts.  

I'd love to attach both the desktop and android projects for completeness but it looks like that's not an option.  

Included in this post is the android build.gradle from the app folder, followed by the build.gradle from the java application.  Lastly the output from logcat in android studio with the output from the execution.  Line of interest is ... java.lang.UnsatisfiedLinkError: dlopen failed: library "//natives/android-aarch64//libgluegen-rt.so" not found ... but the stack trace is there.  

Am I misreading the error message?  I know the APK does not contain a natives folder via inspection.  Just not sure what I am chasing.  I wouldn't think I'd need to include all the sources manually and go down the NDK route but...  

I'd appreciate any insight into what I am missing to build a 'fat' APK or 'uber' APK as I have seen it mentioned.  

With respect to what I have tried, I've chased adding libs in the app folder, attempting the file tree route, many others I can't even remember right now.  I suspect its something small I am missing OR it's just not possible.  I'd hope for the former.  

If there is something that needs clarification please ask.  The development platform is ubuntu 19.10 up to date.  The device is a galaxy S9.  This is part of a larger OpenGL VR application which is working just fine.  I want to move my positional audio engine from the desktop world to android.

--------------------------------------------------------------------------------
ANDROID

<pre><code>
    apply plugin: 'com.android.application'

    android {
        compileSdkVersion 29

        defaultConfig {
            applicationId "android.joal"
            minSdkVersion 22
            targetSdkVersion 29
            versionCode 1
            versionName "1.0"

            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        }

        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
        }
    }

    dependencies {
        implementation fileTree(dir: "libs", include: ["*.jar"])
        implementation 'androidx.appcompat:appcompat:1.1.0'
        implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

        // Parts of JOAL needed to compile.
        implementation "org.jogamp.gluegen:gluegen-rt-android:2.3.2"
        implementation "org.jogamp.joal:joal-android:2.3.2"
        // Parts of JOAL (Native Libraries) needed to execute.
        // These I suspect should reside at the root of the APK at...
        // /natives/android-aarch64/*.so
        runtimeOnly "org.jogamp.gluegen:gluegen-rt-android:2.3.2:natives-android-aarch64"
        runtimeOnly "org.jogamp.joal:joal-android:2.3.2:natives-android-aarch64"

        testImplementation 'junit:junit:4.12'

        androidTestImplementation 'androidx.test.ext:junit:1.1.1'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    }
</code></pre>

--------------------------------------------------------------------------------
DESKTOP

<code><pre>
    plugins {
        id 'java'
        id 'eclipse'
        id 'com.github.johnrengelman.shadow' version '5.2.0'
    }

    repositories {
        jcenter()
    }

    jar {
        manifest {
            attributes 'Main-Class': 'desktop.joal.App'
        }
    }

    dependencies {
        implementation "org.jogamp.gluegen:gluegen-rt:2.3.2"
       implementation "org.jogamp.joal:joal:2.3.2"

        runtimeOnly "org.jogamp.gluegen:gluegen-rt:2.3.2:natives-linux-amd64"
        runtimeOnly "org.jogamp.joal:joal:2.3.2:natives-linux-amd64"

        // Use JUnit test framework
        testImplementation 'junit:junit:4.13'
    }

    shadowJar {
        zip64 false
        baseName = jar.baseName

        exclude "**/*.sha1", "**/*.git"
    }
    assemble.dependsOn shadowJar
</code></pre>

--------------------------------------------------------------------------------
ANDROID LOGCAT

<code><pre>
    2020-06-27 20:21:04.656 8729-8729/? E/Zygote: isWhitelistProcess - Process is Whitelisted
    2020-06-27 20:21:04.658 8729-8729/? E/Zygote: accessInfo : 1
    2020-06-27 20:21:04.672 8729-8729/? I/android.joal: Late-enabling -Xcheck:jni
    2020-06-27 20:21:04.693 8729-8729/? E/android.joal: Unknown bits set in runtime_flags: 0x8000
    2020-06-27 20:21:04.704 8729-8729/? D/ActivityThread: setConscryptValidator
    2020-06-27 20:21:04.705 8729-8729/? D/ActivityThread: setConscryptValidator - put
    2020-06-27 20:21:04.878 8729-8729/android.joal I/MultiWindowDecorSupport: updateCaptionType >> DecorView@28d56d5[], isFloating: false, isApplication: true, hasWindowDecorCaption: false, hasWindowControllerCallback: true
    2020-06-27 20:21:04.878 8729-8729/android.joal D/MultiWindowDecorSupport: setCaptionType = 0, DecorView = DecorView@28d56d5[]
    2020-06-27 20:21:04.916 8729-8729/android.joal W/android.joal: Accessing hidden method Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z (greylist, reflection, allowed)
    2020-06-27 20:21:04.917 8729-8729/android.joal W/android.joal: Accessing hidden method Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V (greylist, reflection, allowed)
    2020-06-27 20:21:04.966 8729-8729/android.joal D/AndroidRuntime: Shutting down VM
    2020-06-27 20:21:04.969 8729-8729/android.joal E/AndroidRuntime: FATAL EXCEPTION: main
        Process: android.joal, PID: 8729
        java.lang.UnsatisfiedLinkError: dlopen failed: library "//natives/android-aarch64//libgluegen-rt.so" not found
            at java.lang.Runtime.load0(Runtime.java:938)
            at java.lang.System.load(System.java:1631)
            at com.jogamp.common.jvm.JNILibLoaderBase.loadLibraryInternal(JNILibLoaderBase.java:624)
            at com.jogamp.common.jvm.JNILibLoaderBase.access$000(JNILibLoaderBase.java:63)
            at com.jogamp.common.jvm.JNILibLoaderBase$DefaultAction.loadLibrary(JNILibLoaderBase.java:106)
            at com.jogamp.common.jvm.JNILibLoaderBase.loadLibrary(JNILibLoaderBase.java:487)
            at com.jogamp.common.os.DynamicLibraryBundle$GlueJNILibLoader.loadLibrary(DynamicLibraryBundle.java:421)
            at com.jogamp.common.os.Platform$1.run(Platform.java:317)
            at java.security.AccessController.doPrivileged(AccessController.java:43)
            at com.jogamp.common.os.Platform.<clinit>(Platform.java:287)
            at com.jogamp.common.os.Platform.initSingleton(Platform.java:355)
            at com.jogamp.openal.ALFactory.<clinit>(ALFactory.java:68)
            at com.jogamp.openal.ALFactory.getAL(ALFactory.java:123)
            at android.joal.MainActivity$App.initialize(MainActivity.java:57)
            at android.joal.MainActivity.onCreate(MainActivity.java:30)
            at android.app.Activity.performCreate(Activity.java:7955)
            at android.app.Activity.performCreate(Activity.java:7944)
            at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1307)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3423)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3595)
            at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
            at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
            at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2147)
            at android.os.Handler.dispatchMessage(Handler.java:107)
            at android.os.Looper.loop(Looper.java:237)
            at android.app.ActivityThread.main(ActivityThread.java:7811)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1076)
</code></pre>
Reply | Threaded
Open this post in threaded view
|

Re: Android Studio: Package Native Libraries into APK

gouessej
Administrator
Hello

I admit that Android development with JogAmp isn't heavily documented yet, sorry. I hope that Sven will be able to give you some advises.
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: Android Studio: Package Native Libraries into APK

bmcclint
Maybe it makes more sense to attempt to frame the problem a little more and a little more back history.

In the past, and currently, I am able to make JOAL do 'something' by installing the prepackaged APKs below.  All APKs are Version 2.4.0-rc-20200306 and it takes all four for the JOAL test to function.

jogamp-android-launcher.apk
joal-test-android.apk
joal-android-aarch64.apk
gluegen-rt-android-aarch64.apk

These APKs allow the JOAL test application to function and a sound is played.  It appears the 'JogAmp's Android Activity Launcher' 'Features daisy chained APK incl. native libraries, reassembling the Java CLASSLOADER and library path experience'.  Reading this two parts stick out. 'native libraries' and 'reassembling the Java CLASSLOADER and library path'.  I suspect this is the magic to get this to work but I am unclear how.

The error coming from Android Studio 'dlopen failed: library "//natives/android-aarch64//libgluegen-rt.so" not found.' indicates to me that the application is NOT attempting to load 'global' resources, those 'other' libraries on the device, but rather its telling me its looking (or wants to) in the APK itself.

So maybe the first question to answer is...

What does the community think the error message is saying?

Additionally...does anyone know if the sources for the APKs listed above exists?  Maybe there is information there to be revealed.  If I can generate those APKs on my development system and get them to execute I'll have a 'working template'.
Reply | Threaded
Open this post in threaded view
|

Re: Android Studio: Package Native Libraries into APK

gouessej
Administrator
Have you looked at Gluegen?
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: Android Studio: Package Native Libraries into APK

bmcclint
Not sure I understand.  Yes?

Its a required dependency for both the desktop and android platforms.  Its in the build.gradle files and is the error I am currently seeing.  I suspect I need to determine how to get the gluegen runtime packaged in the APK for the executable to run.

Where you suggesting something specific?

If there is a way to upload the two projects (desktop and android) I am willing to do so.  Just not sure if there is a way...
Reply | Threaded
Open this post in threaded view
|

Re: Android Studio: Package Native Libraries into APK

bmcclint
So small progress...I've got the libs packages in the APK but at /lib/<arch>/*.so but adding the following to the APK.

android {
        ...
    sourceSets {
        main {
            jniLibs.srcDirs 'src/main/jniLibs/natives'
        }
    }
}

So maybe another way around this...anyway to change the path the JOAL package is trying to load from?  Its looking in //natives/<arch>//*.so.  MAybe I can make it point to //lib/<arch>//*.so.

Also...I see if the source there is a DEBUG flag in the JOGAMP sources...any way to get that to a TRUE so it spits out all the debug statements being overlooked.

Someone has to have done this?  If not using JOGAMP then anyone have a good source for a recent android OpenAL implementation I can include from source?
Reply | Threaded
Open this post in threaded view
|

Re: Android Studio: Package Native Libraries into APK

bmcclint
Its been a month since I posted this original dilemma of mine.  Since then I have attempted to get other variants of OpenAL for Android to compile and be usable withing a Java domain all ending in the same result of failing with a DLL Open error.  I was able to get the JOGAMP libraries into a 'lib' folder but the JOGAMP libraries are looking for them in 'natives' so no joy.

So, I spent some time researching the "oh I'll just do it myself" approach.  After about 20 hours or so I ended up with a working surrogate using Android's AudioTrack on its own thread in streaming mode using the OpenAL specification as inspiration.  I will say I was rather impressed.  Multi-sample spatial audio with distance fall-off in a VR environment worked really well.  The latency is not what the Internet said, roughly 250ms.  The delay from emission of sample and visual representation to hearing the sample is not evident unless I slow the thread to a 10hz cycle or lower.  It plugged into the interface shared by both the desktop and Android variants.  Test users said there was no noticeable latency and were able to identify which moving sources were emitting what sample in the VR environment.

Thus I have a working Android 3D Audio model (with no external dependency) that when side by side with the desktop variant using JOGAMP OpenAL sound and act virtually identical.  Is it a perfect solution to a JNI implementation...nope!  I suspect there are GC concerns and potential latency problems.  Does it meet the need, solve an immediate problem and function adequately...absolutely!  Funny I spent less time building from the ground up than trying to get other 'available' solutions to simply link in.  Is the new implementation complete...no!  It needs Doppler and other distance effects.  The mixer is a simple summing algorithm suspect to over gain that may require clamping.  I've feed dozens of simultaneous samples to it, looping and single cycle, and thus far no over-gain concerns.  But for a 'listener is here looking this way' and 'source X is here, source Y is here, ...' it works really well.

I hope someday there is a clean way to get these pre-builts in and usable but for now success.
Reply | Threaded
Open this post in threaded view
|

Re: Android Studio: Package Native Libraries into APK

gouessej
Administrator
Why can't you either move the native libraries into the expected location ("natives" directory) or disable the automated native library loading and load them manually?
Julien Gouesse | Personal blog | Website