0

Im attempting to build an Android NDK sample app supplied by Occipital Camera.

My gradle file is this:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "com.example.myoccipitaltestapp"
        minSdkVersion 16
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        ndk {
            abiFilters 'arm64-v8a'
        }

        externalNativeBuild {
            cmake {
                cppFlags '-std=c++11'
                arguments '-DANDROID_STL=c++_shared',
                        '-DANDROID_PLATFORM=android-24'
            }
        }

    }

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

    externalNativeBuild {
        cmake {
            path 'src/main/CMakeLists.txt'
        }

    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

My CMakeLists.txt is (I had to hard code path to MainActivity, it was not being resolved otherwise):

# Autogenerated, do not edit

    cmake_minimum_required(VERSION 3.6)
    project(MainActivity)
    add_library(MainActivity SHARED C:/Users/Fsydn/Desktop/occipital/StructureSDK-CrossPlatform-0.7.3-ROS/Libraries/MyOccipitalTestApp/app/src/main/cpp/MainActivity.cpp)
    set_target_properties(MainActivity PROPERTIES LINK_FLAGS "-Wl,-rpath,'$ORIGIN'")
    target_link_libraries(MainActivity PRIVATE
        ${PROJECT_SOURCE_DIR}/lib/${ANDROID_ABI}/libStructure.so
    )

MainActivity.cpp with JNI wrappers is this:

// Autogenerated, do not edit

#include "AppInterface.h"
#include <jni.h>
#include <thread>

extern "C" JNIEXPORT void JNICALL Java_com_occipital_OccipitalTest_MainActivity_setupApp(JNIEnv*, jobject) {
    AppInterface::setup();
}

extern "C" JNIEXPORT void JNICALL Java_com_occipital_OccipitalTest_MainActivity_teardownApp(JNIEnv*, jobject) {
    AppInterface::teardown();
}

extern "C" JNIEXPORT void JNICALL Java_com_occipital_OccipitalTest_MainActivity_renderFrame(JNIEnv*, jobject, jint currentWidth, jint currentHeight, jfloat scaleFactor) {
    AppInterface::renderFrame((unsigned)currentWidth, (unsigned)currentHeight, scaleFactor);
}

extern "C" JNIEXPORT void JNICALL Java_com_occipital_OccipitalTest_MainActivity_updateMouseState(JNIEnv*, jobject, jboolean down, jint x, jint y) {
    AppInterface::updateMouseState(down, x, y);
}

extern "C" JNIEXPORT void JNICALL Java_com_occipital_OccipitalTest_MainActivity_plugStructureCoreFileDescriptor(JNIEnv*, jobject, jint fd) {
    AppInterface::plugStructureCoreFileDescriptor(fd);
}

AppInterface.h:

#pragma once

/** These functions define the interface between cross-platform sample
    application code and platform-specific wrappers. */
namespace AppInterface {
    /** On desktop platforms this function is called in main() before
        runUntilWindowClosed(). On Android it is called when the main activity
        is created. */
    void setup();

    /** On desktop platforms this function is called in main() after
        runUntilWindowClosed(). On Android it is called when the main activity
        is destroyed. */
    void teardown();

#if __ANDROID__
    /** See Window::renderFrameInGLSurfaceViewContext(). */
    void renderFrame(unsigned currentWidth, unsigned currentHeight, float scaleFactor);

    /** See Window::updateMouseState(). */
    void updateMouseState(bool down, int x, int y);

    /** For applications that require Structure Core USB support. The argument
        is a file descriptor from the Android UsbDeviceConnection API and should
        be passed to ST::registerSensorByUSBFileDescriptor() or equivalent. */
    void plugStructureCoreFileDescriptor(int fd);
#else
    /** Called in main(). */
    void runUntilWindowClosed();
#endif
}

MainActivity.java (were libStructure.so is statically loaded):

package com.example.myoccipitaltestapp;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.os.Bundle;

import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.usb.UsbDeviceConnection;
import android.Manifest;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.opengl.GLSurfaceView;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.egl.EGLConfig;

public class MainActivity extends Activity implements com.example.myoccipitaltestapp.StructureCoreUSBHelper.Delegate, ActivityCompat.OnRequestPermissionsResultCallback {
    private class GLRenderer implements GLSurfaceView.Renderer {
        private MainActivity owner;
        private int width;
        private int height;

        public GLRenderer(MainActivity owner) {
            this.owner = owner;
        }

        public void onSurfaceCreated(GL10 gl, EGLConfig config) {}

        public void onSurfaceChanged(GL10 gl, int width, int height) {
            this.width = width;
            this.height = height;
        }

        public void onDrawFrame(GL10 gl) {
            owner.renderFrame(width, height, owner.scaleFactor);
        }
    }

    private class GLView extends GLSurfaceView {
        private MainActivity owner;
        private boolean mouseDown;

        public GLView(Context context, MainActivity owner) {
            super(context);
            this.owner = owner;
        }

        public boolean onTouchEvent(MotionEvent event) {
            float x = event.getX();
            float y = event.getY();
            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    mouseDown = true;
                    owner.updateMouseState(mouseDown, (int)x, (int)y);
                    return true;

                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    mouseDown = false;
                    owner.updateMouseState(mouseDown, (int)x, (int)y);
                    return true;

                case MotionEvent.ACTION_MOVE:
                    owner.updateMouseState(mouseDown, (int)x, (int)y);
                    return true;

                default:
                    return false;
            }
        }
    }

    private GLView glView;
    private volatile float scaleFactor = 1.f;
    private com.example.myoccipitaltestapp.StructureCoreUSBHelper usbHelper;

    private float computeScaleFactor() {
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        return metrics.density;
    }

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setupApp();

        scaleFactor = computeScaleFactor();
        glView = new GLView(this, this);
        glView.setEGLContextClientVersion(3);
        glView.setPreserveEGLContextOnPause(true);
        glView.setRenderer(new GLRenderer(this));
        glView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
        setContentView(glView);

        usbHelper = new com.example.myoccipitaltestapp.StructureCoreUSBHelper(this, this);
    }

    protected void onDestroy() {
        super.onDestroy();
        teardownApp();
    }

    public void onConfigurationChanged(Configuration config) {
        super.onConfigurationChanged(config);
        scaleFactor = computeScaleFactor();
        glView.requestRender();
    }

    protected void onPause() {
        super.onPause();
        glView.onPause();
    }

    protected void onResume() {
        super.onResume();
        glView.onResume();
        ActivityCompat.requestPermissions(this, new String[] {
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
        }, 1);
    }

    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    }

    public void newStructureCoreDevice(UsbDeviceConnection conn) {
        plugStructureCoreFileDescriptor(conn.getFileDescriptor());
    }

    private native void setupApp();
    private native void teardownApp();
    private native void renderFrame(int currentWidth, int currentHeight, float scaleFactor);
    private native void updateMouseState(boolean down, int x, int y);
    private native void plugStructureCoreFileDescriptor(int fd);

    static {
        System.loadLibrary("Structure");
    }

/*
Also tried 

 static {
        System.loadLibrary("libStructure");
    }


*/

}

When i run build in Android Studio, Im getting these errors:

1) issue:

    Build command failed.
    Error while executing process C:\Users\Fsydn\AppData\Local\Android\Sdk\cmake\3.10.2.4988404\bin\ninja.exe with arguments {-C C:\Users\Fsydn\Desktop\occipital\StructureSDK-CrossPlatform-0.7.3-ROS\Libraries\MyOccipitalTestApp\app\.cxx\cmake\debug\arm64-v8a MainActivity}
    ninja: Entering directory `C:\Users\Fsydn\Desktop\occipital\StructureSDK-CrossPlatform-0.7.3-ROS\Libraries\MyOccipitalTestApp\app\.cxx\cmake\debug\arm64-v8a'
    [1/2] Building CXX object CMakeFiles/MainActivity.dir/cpp/MainActivity.cpp.o
    [2/2] Linking CXX shared library C:\Users\Fsydn\Desktop\occipital\StructureSDK-CrossPlatform-0.7.3-ROS\Libraries\MyOccipitalTestApp\app\build\intermediates\cmake\debug\obj\arm64-v8a\libMainActivity.so
    FAILED: C:/Users/Fsydn/Desktop/occipital/StructureSDK-CrossPlatform-0.7.3-ROS/Libraries/MyOccipitalTestApp/app/build/intermediates/cmake/debug/obj/arm64-v8a/libMainActivity.so 
    cmd.exe /C "cd . && C:\Users\Fsydn\AppData\Local\Android\Sdk\ndk\21.1.6352462\toolchains\llvm\prebuilt\windows-x86_64\bin\clang++.exe --target=aarch64-none-linux-android24 --gcc-toolchain=C:/Users/Fsydn/AppData/Local/Android/Sdk/ndk/21.1.6352462/toolchains/llvm/prebuilt/windows-x86_64 --sysroot=C:/Users/Fsydn/AppData/Local/Android/Sdk/ndk/21.1.6352462/toolchains/llvm/prebuilt/windows-x86_64/sysroot -fPIC -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security  -std=c++11 -O0 -fno-limit-debug-info  -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libgcc_real.a -Wl,--exclude-libs,libatomic.a -Wl,--build-id -Wl,--fatal-warnings -Wl,--no-undefined -Qunused-arguments   -Wl,-rpath,'$ORIGIN' -shared -Wl,-soname,libMainActivity.so -o C:\Users\Fsydn\Desktop\occipital\StructureSDK-CrossPlatform-0.7.3-ROS\Libraries\MyOccipitalTestApp\app\build\intermediates\cmake\debug\obj\arm64-v8a\libMainActivity.so CMakeFiles/MainActivity.dir/cpp/MainActivity.cpp.o -LC:/Users/Fsydn/Desktop/occipital/StructureSDK-CrossPlatform-0.7.3-ROS/Libraries/MyOccipitalTestApp/app/src/main/lib/arm64-v8a -lStructure -latomic -lm && cd ."
    CMakeFiles/MainActivity.dir/cpp/MainActivity.cpp.o: In function `Java_com_occipital_OccipitalTest_MainActivity_setupApp':
    C:/Users/Fsydn/Desktop/occipital/StructureSDK-CrossPlatform-0.7.3-ROS/Libraries/MyOccipitalTestApp/app/src/main/cpp/MainActivity.cpp:8: undefined reference to `AppInterface::setup()'
    CMakeFiles/MainActivity.dir/cpp/MainActivity.cpp.o: In function `Java_com_occipital_OccipitalTest_MainActivity_teardownApp':
    C:/Users/Fsydn/Desktop/occipital/StructureSDK-CrossPlatform-0.7.3-ROS/Libraries/MyOccipitalTestApp/app/src/main/cpp/MainActivity.cpp:12: undefined reference to `AppInterface::teardown()'
    CMakeFiles/MainActivity.dir/cpp/MainActivity.cpp.o: In function `Java_com_occipital_OccipitalTest_MainActivity_renderFrame':
    C:/Users/Fsydn/Desktop/occipital/StructureSDK-CrossPlatform-0.7.3-ROS/Libraries/MyOccipitalTestApp/app/src/main/cpp/MainActivity.cpp:16: undefined reference to `AppInterface::renderFrame(unsigned int, unsigned int, float)'
    CMakeFiles/MainActivity.dir/cpp/MainActivity.cpp.o: In function `Java_com_occipital_OccipitalTest_MainActivity_updateMouseState':
    C:/Users/Fsydn/Desktop/occipital/StructureSDK-CrossPlatform-0.7.3-ROS/Libraries/MyOccipitalTestApp/app/src/main/cpp/MainActivity.cpp:20: undefined reference to `AppInterface::updateMouseState(bool, int, int)'
    CMakeFiles/MainActivity.dir/cpp/MainActivity.cpp.o: In function `Java_com_occipital_OccipitalTest_MainActivity_plugStructureCoreFileDescriptor':
    C:/Users/Fsydn/Desktop/occipital/StructureSDK-CrossPlatform-0.7.3-ROS/Libraries/MyOccipitalTestApp/app/src/main/cpp/MainActivity.cpp:24: undefined reference to `AppInterface::plugStructureCoreFileDescriptor(int)'
    clang++: error: linker command failed with exit code 1 (use -v to see invocation)
    ninja: build stopped: subcommand failed.

2) issue, Also the Android IDE complains about , these lines in MainActivity.java

  private native void setupApp();
    private native void teardownApp();
    private native void renderFrame(int currentWidth, int currentHeight, float scaleFactor);
    private native void updateMouseState(boolean down, int x, int y);
    private native void plugStructureCoreFileDescriptor(int fd);

    cannot resolve corresponding JNI functions...

How can I resolve this problems to build this sample app?

cyber101
  • 2,822
  • 14
  • 50
  • 93
  • I see it's trying to generate libStructure.so somewhere, do you have a java class Structure.java? Show us that file as well. – Hack06 May 03 '20 at 16:00
  • @Hack06, not at my computer now, i dont have Structure.java. I have a MainActivity that calls libStructure.so, the .so file was already supplied by manufacturer. Im able to load it using system.load() but i was getting UnsatisfiedLinkError after calling a native function. So i thought i should include the CMake instructions...it seems i dont need to rebuild,since i already have libStructure.so?? – cyber101 May 03 '20 at 17:57
  • How do you link to that libStructure.so file? Show the class of static/dynamic linking. A common error is missing the 'lib' prefix. – Hack06 May 03 '20 at 17:59
  • @Hack06, added MainActivity.java that attempts to load libStructure.so. I think the problem is at compilation with udefined references and not .so loading, thats well after. – cyber101 May 03 '20 at 18:58
  • @Hack06 , it also seems the original code is attempting to load "MainActivity.so" , while attempting to load libStructure.so, which is the supplied .so? – cyber101 May 03 '20 at 19:20
  • .so files are what you get when you compile the native code. The library name must start with 'lib' but you must not include it in the lib name inside the java code (so it should be System.loadLibrary("Structure"); and not System.loadLibrary("libStructure"); ). Regarding to those 5 private native functions that you have in your java code, those are the connections with the native code (JNI-stuff). But I don't know what exactly may cause your problem, so the best I can advise is to localize the problem by commenting out the code to minimum, until you get compiled and run then start uncommenting – Hack06 May 03 '20 at 19:26
  • A wild-ass guess is to try changing the lib name all lowercase letters everywhere (structure instead of Structure), and check your 'libs' folder, it should contain the last letter 's' - which is one of the common errors with NDK ;) – Hack06 May 03 '20 at 19:31
  • is this somehow related to your problem? https://stackoverflow.com/a/61579331/4410376 – Hack06 May 04 '20 at 18:41

0 Answers0