[FIXED] Structure Core Depth Camera Android App UnsatisfiedLinkError: cannot locate symbol "__sfp_handle_exceptions"

Issue

I am trying to create an android application for the depth camera Structure Core. This product comes with an Android API but the problem is that the api documentation is very short and not very helpful. It has one prebuilt Android app in the form of an APK file, and sample project codes for Windows, Linux and Android. The problem is the sample Android project is very old. I managed to build it and install to a device, but the app does not launch with the error java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "__sfp_handle_exceptions".
Therefore I tried to make a very simple application of mine from scratch, but in the end the same error keeps popping up (the application builds and installs successfully):

2021-11-30 10:48:02.844 25827-25827/com.bridgewiz.structurecore.coreandroidtrial E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.bridgewiz.structurecore.coreandroidtrial, PID: 25827
    java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "__sfp_handle_exceptions" referenced by "/data/app/com.bridgewiz.structurecore.coreandroidtrial-sHH5hFqRXfZv5IT1fQYgmg==/base.apk!/lib/arm64-v8a/libStructure.so"...

The API includes the core library .so file named libStructure.so and utility libraries for different sample projects (libDepthTester.so,libPlayground.so etc.).

My question is whether the error given above results from issues on my implementation side or there is something missing in compilation of the library .so files supplied by the producer?

The entry information of the .so files (obtained by aarch64-linux-android-readelf.exe) is here

In my own project, the app level gradle file is as follows:

plugins {
    id 'com.android.application'
    id 'kotlin-android'
}

android {
    compileSdk 31

    defaultConfig {
        applicationId "com.bridgewiz.structurecore.coreandroidtrial"
        minSdk 27
        targetSdk 31
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                arguments '-DANDROID_STL=c++_shared'
                targets 'coreandroidtrial'
                abiFilters 'arm64-v8a'
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
    externalNativeBuild {
        cmake {
            path file('CMakeLists.txt')
            version '3.10.2'
        }
    }
    buildFeatures {
        viewBinding true
    }
    sourceSets {
        main {
            jniLibs.srcDirs += ['libs']
        }
    }
}

dependencies {

    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.4.0'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

The corresponding CMAKELists.txt file is:


cmake_minimum_required(VERSION 3.10.2)

project("coreandroidtrial")

add_library( # Sets the name of the library.
        coreandroidtrial

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        src/main/cpp/native-lib.cpp)

set_target_properties(coreandroidtrial PROPERTIES LINK_FLAGS "-Wl,-rpath,'$ORIGIN'")

find_library(
        log-lib

        log)


target_link_libraries( # Specifies the target library.
        coreandroidtrial PRIVATE
        ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI}/libDepthTester.so
        ${log-lib})

Based on the documentation, the needed AppInterface.h native file is as below:

#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();

    /** 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);


    /** For applications requiring CLI command support, this function will be processed before getting any GUI level setup*/
    void cliCmdHandler(int argc, char **argv);
}

Inside my default native-lib.cpp file there is only the JNIEXPORT function definitions with their respective calls to the ApplicationInterface:

#include <jni.h>
#include <string>
#include <thread>

#include "AppInterface.h"


extern "C" JNIEXPORT jstring JNICALL
Java_com_bridgewiz_structurecore_coreandroidtrial_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++ v4";
    return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT void JNICALL
Java_com_bridgewiz_structurecore_coreandroidtrial_MainActivity_setupApp(JNIEnv *env, jobject thiz) {
    AppInterface::setup();
}
extern "C"
JNIEXPORT void JNICALL
Java_com_bridgewiz_structurecore_coreandroidtrial_MainActivity_teardownApp(JNIEnv *env,
                                                                           jobject thiz) {
    AppInterface::teardown();
}
extern "C"
JNIEXPORT void JNICALL
Java_com_bridgewiz_structurecore_coreandroidtrial_MainActivity_renderFrame(JNIEnv *env,
                                                                           jobject thiz,
                                                                           jint current_width,
                                                                           jint current_height,
                                                                           jfloat scale_factor) {
    AppInterface::renderFrame((unsigned)current_width, (unsigned)current_height, scale_factor);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_bridgewiz_structurecore_coreandroidtrial_MainActivity_updateMouseState(JNIEnv *env,
                                                                                jobject thiz,
                                                                                jboolean down,
                                                                                jint x, jint y) {
    AppInterface::updateMouseState(down, x, y);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_bridgewiz_structurecore_coreandroidtrial_MainActivity_plugStructureCoreFileDescriptor(
        JNIEnv *env, jobject thiz, jint fd) {
    AppInterface::plugStructureCoreFileDescriptor(fd);
}

You may see the project file and folder structure here

Solution

From a bit of searching it looks like __sfp_handle_exceptions is part of libstdc++ (from GCC), while modern Android NDKs ship with libc++ (Clang).
Your best bet is to ask the vendor for a library compiled against libc++.

If they cannot or will not provide such a thing, you could try stubbing the function or copying its implementation from here, but I do not know if the latter is allowed license-wise. Even so, no guarantees that you will end up with a working app.

Answered By – Botje

Answer Checked By – Pedro (Easybugfix Volunteer)

Leave a Reply

(*) Required, Your email will not be published