GNOME Bugzilla – Bug 785406
android: gstjniutils.c fails to find application class loader provider on Android 7.0
Last modified: 2017-11-08 12:29:03 UTC
On android 7.0 (Samsung Galaxy S8/S6) attempting to register androidmedia plugin fails in gstjniutils.c. Seems check_application_class_loader returns null: 07-25 17:20:44.953 23946 23946 I GStreamer+GST_PLUGIN_LOADING: 0:00:00.104817693 0x7ccf5ff600 gstplugin.c:225:gst_plugin_register_static registered static plugin "spandsp" 07-25 17:20:44.953 23946 23946 I GStreamer+GST_PLUGIN_LOADING: 0:00:00.104837116 0x7ccf5ff600 gstplugin.c:227:gst_plugin_register_static added static plugin "spandsp", result: 1 07-25 17:20:44.953 23946 23946 I GStreamer+GST_PLUGIN_LOADING: 0:00:00.105211347 0x7ccf5ff600 gstplugin.c:225:gst_plugin_register_static registered static plugin "sbc" 07-25 17:20:44.953 23946 23946 I GStreamer+GST_PLUGIN_LOADING: 0:00:00.105234270 0x7ccf5ff600 gstplugin.c:227:gst_plugin_register_static added static plugin "sbc", result: 1 07-25 17:20:44.954 23946 23946 E GStreamer+default: 0:00:00.105604731 0x7ccf5ff600 gstjniutils.c:671:initialize_classes Could not find application class loader provider 07-25 17:20:44.954 23946 23946 W GStreamer+GST_PLUGIN_LOADING: 0:00:00.105656693 0x7ccf5ff600 gstplugin.c:527:gst_plugin_register_func plugin "(NULL)" failed to initialise For more info refer to this thread on gstreamer mailing list: http://gstreamer-devel.966125.n4.nabble.com/Hardware-decoder-availability-on-exynos-CPU-td4683878.html
What application is that, and can this be reproduced with any of the example applications too? This specific error comes from: https://cgit.freedesktop.org/gstreamer/gst-plugins-bad/tree/sys/androidmedia/gstjniutils.c#n590 Which is called from: https://cgit.freedesktop.org/gstreamer/gst-plugins-bad/tree/sys/androidmedia/gstjniutils.c#n670 The symbol in question should be found, it's part of gstreamer_android-1.0.c.in: https://cgit.freedesktop.org/gstreamer/cerbero/tree/data/ndk-build/gstreamer_android-1.0.c.in#n410 Can you check if it's available in your libgstreamer_android.so? Maybe this is another case of Android breaking the dlopen/dlsym functionality in weird ways. Which ARM variant is this running on, 32 or 64 bit? See this part of the code for something that might be relevant: https://git.gnome.org/browse/glib/tree/gmodule/gmodule-dl.c#n121 Maybe this fixed this misbehaviour now with Android 7, and now our workaround fails.
Indeed, searching in gstreamer_android.so I have found the "check_application_class_loader" in a group like this (whitespaces disappeard after copypasting): failed_getExceptionSummary_gst_amc_jni_call_long_method_gst_amc_jni_initialize_java_vm_getStackTrace_gst_amc_jni_call_static_byte_method_gst_amc_jni_get_static_float_field_gst_amc_jni_get_boolean_field_get_created_java_vms_pthread_key_create_n_vms_pthread_getspecific_exception_description_gst_amc_jni_get_short_field_check_application_class_loader_gst_amc_jni_call_static_char_method_load_java_module_exception_stacktrace_gst_amc_jni_get_static_boolean_field_printWriterCtor_create_java_vm_gst_amc_jni_attach_current_thread_set_autofocus_gst_ahc_src_unlock_PROP_DEVICE_get_capabilities__white_balance_to_enum__data_queue_item_free Yes, this is a 64 bit device (however on android 6.0.1, also 64 bit it run well).
Someone would have to check the Android's libc/libdl code (it's all inside bionic) for changes around that, and then the workaround has to be updated. Can you check if it works well with a 32 bit ARM binary on the same device with Android 7? Also you can easily check if this is broken by just trying to use g_module_open(NULL) and then g_module_symbol() with some local, non-static symbol you have. If that fails, things are wrong.
> Can you check if it works well with a 32 bit ARM binary on the same device with Android 7? I do not know how to do that, can you give me some details? > Also you can easily check if this is broken by just trying to use g_module_open(NULL) and then g_module_symbol() with some local, non-static symbol you have. If that fails, things are wrong. Give me a few hours
(In reply to Jeremi Wójcicki from comment #4) > > Can you check if it works well with a 32 bit ARM binary on the same device with Android 7? > > I do not know how to do that, can you give me some details? You could remove 'arm64-v8a' from the targeted ABIs (APP_ABI in Application.mk if you use ndk-build). And keep 'armeabi' 'armeabi-v7a' 'arm64-v8a' 'x86' 'x86_64'
1. Using 32 bit ARM binary the problem still occurs 2. I have used a following code (correct me it its wrong, I do not have any experience with glib): ---------------- #include <gmodule.h> void hello_fcn(){ LOG_INFO("Hello function"); } void g_module_test() { GModule *m; gpointer *ptr; m = g_module_open(NULL, G_MODULE_BIND_LOCAL); if (!m) { LOG_ERROR("module open - False"); } else { LOG_ERROR("module open - True"); } if (!g_module_symbol(m, "hello_fcn", ptr)) { LOG_ERROR("symbol - False"); } else { LOG_ERROR("symbol - True"); } hello_fcn(); } ------------- I call g_module_test somewhere in gstreamer mainloop. Is this what you asked for? The result is following: 07-26 18:29:46.051 28198-28275/org.freedesktop.gstreamer.tutorials.tutorial_3 E/GST_log: module open - True 07-26 18:29:46.051 28198-28275/org.freedesktop.gstreamer.tutorials.tutorial_3 E/GLib+GModule: g_module_symbol: assertion 'symbol != NULL' failed 07-26 18:29:46.051 28198-28275/org.freedesktop.gstreamer.tutorials.tutorial_3 E/GST_log: symbol - False 07-26 18:29:46.051 28198-28275/org.freedesktop.gstreamer.tutorials.tutorial_3 I/GST_log: Hello function
(In reply to Jeremi Wójcicki from comment #6 > if (!g_module_symbol(m, "hello_fcn", ptr)) { Almost: gpointer ptr; g_module_symbol(m, "hello_fcn", &ptr) Then that assertion should disappear and we will know more :)
My bad! I updated the code and here's the result: 07-27 00:26:03.051 3325-3542/org.freedesktop.gstreamer.tutorials.tutorial_3 E/GST_log: module open - True 07-27 00:26:03.052 3325-3542/org.freedesktop.gstreamer.tutorials.tutorial_3 E/GST_log: symbol - False 07-27 00:26:03.052 3325-3542/org.freedesktop.gstreamer.tutorials.tutorial_3 I/GST_log: Hello function Just for reference I enclose the updated code: void g_module_test() { GModule *m; gpointer ptr; m = g_module_open(NULL, G_MODULE_BIND_LOCAL); if (!m) { LOG_ERROR("module open - False"); } else { LOG_ERROR("module open - True"); } if (!g_module_symbol(m, "hello_fcn", &ptr)) { LOG_ERROR("symbol - False"); } else { LOG_ERROR("symbol - True"); } hello_fcn(); }
Thanks for confirming. So it's indeed the GModule code that fails to load anything from the NULL/self module on Android 7, most likely meaning that our workaround for bugs in previous Android versions breaks things now.
Hi guys! Are there any news on this bug? Development of my application using gstreamer relies on resolution of this problem, so it is quite urgent to me... Are there plans to resolve it in the nearest future or should I look for some sort of workaround?
I'm busy with other things and don't have a device set up with 7.0 currently. Basically what is needed here is someone going through the relevant bionic code and check what has changed there between 6.x and 7.0, and then we have to find yet another workaround that hopefully works for all versions.
Hmm... Even though I have a device I do not posses skills to fix that bug, as I have limited experience with gstreamer and no experience at all with glib. Possibly I'll try to work around the problem by terminating pipeline before decoding with appsink and try to natively use Android's MediaCodec to get the job done. I'll post on forum if I bump into gstreamer related problems while doing that. Any suggestions are welcome!
This is not really GLib or GStreamer related, just related to how dlopen() and dlsym() work on Android. And in this specific case how to get a symbol from the current process image.
Ok. Give me a few days to investigate the topic, perhaps I could contribute to the solution of this issue.
Shout if you need help or pointers where to look :)
OK, so following you intuition about dlopen() and dlsym() calls, I looked into changes that were introduced since Android 7.0. It seems we've pin-pointed the issue: for target APIs of 24(Nougat) and higher dynamic linking seems to be restricted (https://developer.android.com/about/versions/nougat/android-7.0-changes.html#ndk). To verify that, I have actually downgraded my compile and target api to 21 and changed at the same time target to armeabi-v7a (64bit instruction set is supported api level 24 up). After the changes I could see the class loader and amc plugin properly registered! My app begun crashing, tough, when I was touching samples received through appsink in another thread (which did not occur before). Its possible that I have some mixed 32 (gstreamer) and 64 (oculus mobile sdk) bit libs and hence the issue, and it should rather not be related to the main problem. Now, what makes me wonder is that Android 7.0 changelog says that the NDK public libraries can still be dynamically linked. AMC and OpenMAX are public, aren't they? Is gstreamer dynamically linking to itself (gstreamer.so)? Perhaps somehow modifying this behavior could work as a quickfix to the issue. Otherwise, I guess, the approach to handling AMC and OMX by gstremer should be changed (I guess static library loading etc.). That is all I have learnt about this issue. Anyhow, the workaround using the downgraded target api and using 32bit build seems to be OK for now. I'll post more if I find other related problems or solutions.
These changes are probably related, but more in an unintentional way. There is no problem here with dynamic linking and loading libraries at runtime, but it's the same API that is used. We only do static linking, except for public Android libraries but those are also not dynamically loaded at runtime (that is, not via dlopen(), etc). What we do is to try to retrieve a symbol of the current process, not from a separate library. My guess is that somewhere related to the changes you list, they changed behaviour (unintentionally probably) to load symbols from the current process.
Code in question would be here btw: https://android.googlesource.com/platform/bionic.git/+/master Maybe something relevant can be found in the GIT history. But what could also make sense is to write a simple, non-GStreamer/GLib example application that just tries to get a symbol from the current process via dlopen(). Show that it works pre-24 and not anymore, and file a bug in the Android bug tracker.
The problem is that 'dlsym' fails if it uses a handle which is created by 'dlopen(NULL)' on Android 64 bit. I am not sure if it is a correct way, but using RTLD_DEFAULT solves the problem. See https://bugzilla.gnome.org/show_bug.cgi?id=788270
Let's continue discussions there then *** This bug has been marked as a duplicate of bug 788270 ***
Created attachment 361033 [details] [review] glib: Allow using RTLD_DEFAULT on 64bit Android I attached a backport patch here rather than creating another issue.
(In reply to Justin J. Kim from comment #21) > I attached a backport patch here rather than creating another issue. I created another issue; https://bugzilla.gnome.org/show_bug.cgi?id=790058