Search code examples
javaandroidjava-native-interfacevoice

OpenSmile library fails to analyze on Android


I'm using the openSmile 2.0-rc1 library in Android and I'm facing a very annoying problem. When I run runAnalysis the first time it passes without any problems. I get valid results. However, when I run the same function twice with the same parameters my app crashes and I get this error:

07-18 11:47:22.609 5128-5128/com.test A/libc: Fatal signal 6 (SIGABRT), code -6 in tid 5128 (com.test)
    07-18 11:47:22.711 196-196/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    07-18 11:47:22.711 196-196/? A/DEBUG: Build fingerprint: 'google/hammerhead/hammerhead:6.0.1/user/release-keys'
    07-18 11:47:22.711 196-196/? A/DEBUG: Revision: '11'
    07-18 11:47:22.711 196-196/? A/DEBUG: ABI: 'arm'
    07-18 11:47:22.711 196-196/? A/DEBUG: pid: 5128, tid: 5128, name: com.test  >>> com.test <<<
    07-18 11:47:22.711 196-196/? A/DEBUG: signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    07-18 11:47:22.723 196-196/? A/DEBUG:     r0 00000000  r1 00001408  r2 00000006  r3 b6f7eb7c
    07-18 11:47:22.723 196-196/? A/DEBUG:     r4 b6f7eb84  r5 b6f7eb34  r6 0000000b  r7 0000010c
    07-18 11:47:22.723 196-196/? A/DEBUG:     r8 9eb91e40  r9 9c97a9c0  sl aa125d80  fp 00000001
    07-18 11:47:22.723 196-196/? A/DEBUG:     ip 00000006  sp bedf5848  lr b6cedb61  pc b6ceff50  cpsr 400f0010
    07-18 11:47:22.737 196-196/? A/DEBUG: backtrace:
    07-18 11:47:22.737 196-196/? A/DEBUG:     #00 pc 00041f50  /system/lib/libc.so (tgkill+12)
    07-18 11:47:22.737 196-196/? A/DEBUG:     #01 pc 0003fb5d  /system/lib/libc.so (pthread_kill+32)
    07-18 11:47:22.737 196-196/? A/DEBUG:     #02 pc 0001c30f  /system/lib/libc.so (raise+10)
    07-18 11:47:22.737 196-196/? A/DEBUG:     #03 pc 000194c1  /system/lib/libc.so (__libc_android_abort+34)
    07-18 11:47:22.737 196-196/? A/DEBUG:     #04 pc 000174ac  /system/lib/libc.so (abort+4)
    07-18 11:47:22.737 196-196/? A/DEBUG:     #05 pc 000c12e7  /data/app/com.test-1/lib/arm/libopenSmile.so (_ZN9__gnu_cxx27__verbose_terminate_handlerEv+226)
    07-18 11:47:22.737 196-196/? A/DEBUG:     #06 pc 00091e05  /data/app/com.test-1/lib/arm/libopenSmile.so (_ZN10__cxxabiv111__terminateEPFvvE+4)
    07-18 11:47:22.737 196-196/? A/DEBUG:     #07 pc 00091e79  /data/app/com.test-1/lib/arm/libopenSmile.so (_ZSt9terminatev+8)
    07-18 11:47:22.737 196-196/? A/DEBUG:     #08 pc 00091f9d  /data/app/com.test-1/lib/arm/libopenSmile.so (__cxa_throw+120)
    07-18 11:47:22.737 196-196/? A/DEBUG:     #09 pc 00033a73  /data/app/com.test-1/lib/arm/libopenSmile.so (_ZNK10ConfigType10findFieldHEPKcPiPPKS_S2_PPc+346)
    07-18 11:47:22.737 196-196/? A/DEBUG:     #10 pc 00034d5f  /data/app/com.test-1/lib/arm/libopenSmile.so (_ZN17cFileConfigReader11getInstanceEPKcPK10ConfigTypeP14cConfigManager+854)
    07-18 11:47:22.737 196-196/? A/DEBUG:     #11 pc 00036a73  /data/app/com.test-1/lib/arm/libopenSmile.so (_ZN14cConfigManager10readConfigEv+102)
    07-18 11:47:22.737 196-196/? A/DEBUG:     #12 pc 00090df3  /data/app/com.test-1/lib/arm/libopenSmile.so (runAnalysis+194)
    07-18 11:47:22.738 196-196/? A/DEBUG:     #13 pc 00fe573f  /data/app/com.test-1/oat/arm/base.odex (offset 0xb75000) (void com.test.probing.probe.voiceAnalysis.OpenSmile.runAnalysis(java.lang.String, java.lang.String, java.lang.String, java.lang.String)+170)
    07-18 11:47:22.738 196-196/? A/DEBUG:     #14 pc 00fe532d  /data/app/com.test-1/oat/arm/base.odex (offset 0xb75000) (java.lang.String com.test.probing.probe.voiceAnalysis.OpenSmile.runAnalysis(android.content.Context, java.lang.String)+872)
    07-18 11:47:22.738 196-196/? A/DEBUG:     #15 pc 0187d1c9  /data/app/com.test-1/oat/arm/base.odex (offset 0xb75000) (int com.test.probing.probe.voiceAnalysis.VoiceAnalyserService.onStartCommand(android.content.Intent, int, int)+292)
    07-18 11:47:22.738 196-196/? A/DEBUG:     #16 pc 72a4f10f  /data/dalvik-cache/arm/system@[email protected] (offset 0x1ed6000)


                                          --------- beginning of system
    07-18 11:47:23.199 778-6481/? W/ActivityManager:   Force finishing activity com.test/.app.MainActivity
    07-18 11:47:23.203 196-196/? A/DEBUG: Tombstone written to: /data/tombstones/tombstone_00
    07-18 11:47:23.203 196-196/? E/DEBUG: AM write failed: Broken pipe

I'm using the open smile as JNI and I have a service running the analysis. I have tried on different Android version, on different architectures, but I face the same problem.

I don't understand how can this happen when the first time it runs flawlessly.

I'd be grateful for any help regarding this. Thank you in advance.


Solution

  • The problem itself was not with the openSmile library. Since it successfully runs on the first try and fails on the second run.

    After activity restarts calls to native methods may fail.

    This can happen because shared library does not get reloaded. There is no symmetric method to System.loadLibrary(String libName) to manually unload or reload library. The shared library on Android will be unloaded when there are no processes that use it. Even when activity finishes its lifecycle and its onDestroy() method is called the host process of activity may still run. From the Android docs "the process hosting the activity may killed by the system at any time" after activity is finished. I think it would be killed when OS will need more memory for other tasks.

    So, if the old process still sticks around so does the shared library. After activity starts again, its onCreate() called etc, it attaches to the same running process and deals with the same shared library instance. The shared library's data section where all static and global variables are stored is still in the state it was carrying old values.

    Now, to highlight the possible problem, suppose we have a Singleton class on a native side. We call in to a native function to create it from activity's onCreate method. The native method may look like this.

    CSomeSingleton* CSomeSingleton::GetInstance()
    {
        if (m_Instance == NULL)
            m_Instance = new CSomeSingleton();
        return m_Instance;
    }
    

    In activity's onDestroy we call in to another native method that destroys it. Suppose it looks like this.

    void CSomeSingleton::FreeInstance()
    {
        delete m_Instance;
    }
    

    After acivity restarts and attaches to same process the problem arises. m_Instance will not be NULL, it will point to invalid address in memory.

    There are 3 variants to unravel this issue.

    1. Do not deinitialize your native-side objects at all (in activity's onDestroy()). When new activity instance will start it would attach to running process with valid pointers and global variables. If the process would be killed by the time, it would be started again, reloading the shared library, reinitializing the data section.

    2. Find all places where the global/static variables where left in inconsistent state. For the above example that would be

      void CSomeSingleton::FreeInstance() { delete m_Instance; m_Instance = NULL; }

    3. Call System.exit(0) in onDestory() to stop the process and thus unload the shared library. Considered as a bad practice, because you may have yet another component that is running in that process, and this component may be not in the appropriate state to stop its execution (because it may have some unsaved data).


    Credits to: http://choruscode.blogspot.dk/2013/12/on-android-ndk-and-activity-lifecycle.html