• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012, Collabora Ltd.
3  *   Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
4  * Copyright (C) 2013, Fluendo S.A.
5  *   Author: Andoni Morales <amorales@fluendo.com>
6  * Copyright (C) 2014, Sebastian Dröge <sebastian@centricular.com>
7  * Copyright (C) 2014, Collabora Ltd.
8  *   Author: Matthieu Bouron <matthieu.bouron@collabora.com>
9  * Copyright (C) 2015, Sebastian Dröge <sebastian@centricular.com>
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation
14  * version 2.1 of the License.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
24  *
25  */
26 
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 
31 #include <gst/gst.h>
32 #include <pthread.h>
33 #include <gmodule.h>
34 
35 #include "gstjniutils.h"
36 
37 static GModule *java_module;
38 static jint (*get_created_java_vms) (JavaVM ** vmBuf, jsize bufLen,
39     jsize * nVMs);
40 static jint (*create_java_vm) (JavaVM ** p_vm, JNIEnv ** p_env, void *vm_args);
41 static JavaVM *java_vm;
42 static gboolean started_java_vm = FALSE;
43 static pthread_key_t current_jni_env;
44 static jobject (*get_class_loader) (void);
45 
46 gint
gst_amc_jni_get_android_level()47 gst_amc_jni_get_android_level ()
48 {
49   JNIEnv *env;
50   gint ret = __ANDROID_API__;
51   jfieldID sdkIntFieldID = NULL;
52 
53   env = gst_amc_jni_get_env ();
54 
55   jclass versionClass = (*env)->FindClass (env, "android/os/Build$VERSION");
56   if (versionClass == NULL)
57     goto done;
58 
59   sdkIntFieldID = (*env)->GetStaticFieldID (env, versionClass, "SDK_INT", "I");
60   if (sdkIntFieldID == NULL)
61     goto done;
62 
63   ret = (*env)->GetStaticIntField (env, versionClass, sdkIntFieldID);
64 done:
65   return ret;
66 }
67 
68 jclass
gst_amc_jni_get_class(JNIEnv * env,GError ** err,const gchar * name)69 gst_amc_jni_get_class (JNIEnv * env, GError ** err, const gchar * name)
70 {
71   jclass tmp, ret = NULL;
72 
73   GST_DEBUG ("Retrieving Java class %s", name);
74 
75   tmp = (*env)->FindClass (env, name);
76   if ((*env)->ExceptionCheck (env) || !tmp) {
77     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
78         GST_LIBRARY_ERROR_FAILED, "Failed to find class %s", name);
79     goto done;
80   }
81 
82   ret = (*env)->NewGlobalRef (env, tmp);
83   if (!ret) {
84     GST_ERROR ("Failed to get %s class global reference", name);
85   }
86 
87 done:
88   if (tmp)
89     (*env)->DeleteLocalRef (env, tmp);
90   tmp = NULL;
91 
92   return ret;
93 }
94 
95 jmethodID
gst_amc_jni_get_method_id(JNIEnv * env,GError ** err,jclass klass,const gchar * name,const gchar * signature)96 gst_amc_jni_get_method_id (JNIEnv * env, GError ** err, jclass klass,
97     const gchar * name, const gchar * signature)
98 {
99   jmethodID ret;
100 
101   ret = (*env)->GetMethodID (env, klass, name, signature);
102   if ((*env)->ExceptionCheck (env) || !ret) {
103     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
104         GST_LIBRARY_ERROR_FAILED, "Failed to get method ID %s (%s)", name,
105         signature);
106   }
107   return ret;
108 }
109 
110 jmethodID
gst_amc_jni_get_static_method_id(JNIEnv * env,GError ** err,jclass klass,const gchar * name,const gchar * signature)111 gst_amc_jni_get_static_method_id (JNIEnv * env, GError ** err, jclass klass,
112     const gchar * name, const gchar * signature)
113 {
114   jmethodID ret;
115 
116   ret = (*env)->GetStaticMethodID (env, klass, name, signature);
117   if ((*env)->ExceptionCheck (env) || !ret) {
118     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
119         GST_LIBRARY_ERROR_FAILED, "Failed to get static method ID %s (%s)",
120         name, signature);
121   }
122   return ret;
123 }
124 
125 jfieldID
gst_amc_jni_get_field_id(JNIEnv * env,GError ** err,jclass klass,const gchar * name,const gchar * type)126 gst_amc_jni_get_field_id (JNIEnv * env, GError ** err, jclass klass,
127     const gchar * name, const gchar * type)
128 {
129   jfieldID ret;
130 
131   ret = (*env)->GetFieldID (env, klass, name, type);
132   if ((*env)->ExceptionCheck (env) || !ret) {
133     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
134         GST_LIBRARY_ERROR_FAILED, "Failed to get field ID %s (%s)", name, type);
135   }
136   return ret;
137 }
138 
139 jfieldID
gst_amc_jni_get_static_field_id(JNIEnv * env,GError ** err,jclass klass,const gchar * name,const gchar * type)140 gst_amc_jni_get_static_field_id (JNIEnv * env, GError ** err, jclass klass,
141     const gchar * name, const gchar * type)
142 {
143   jfieldID ret;
144 
145   ret = (*env)->GetStaticFieldID (env, klass, name, type);
146   if ((*env)->ExceptionCheck (env) || !ret) {
147     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
148         GST_LIBRARY_ERROR_FAILED, "Failed to get static field ID %s (%s)", name,
149         type);
150   }
151   return ret;
152 }
153 
154 jobject
gst_amc_jni_new_object(JNIEnv * env,GError ** err,gboolean global,jclass klass,jmethodID constructor,...)155 gst_amc_jni_new_object (JNIEnv * env, GError ** err, gboolean global,
156     jclass klass, jmethodID constructor, ...)
157 {
158   jobject tmp;
159   va_list args;
160 
161   va_start (args, constructor);
162   tmp = (*env)->NewObjectV (env, klass, constructor, args);
163   va_end (args);
164 
165   if ((*env)->ExceptionCheck (env) || !tmp) {
166     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
167         GST_LIBRARY_ERROR_FAILED, "Failed to create object");
168     return NULL;
169   }
170 
171   if (global)
172     return gst_amc_jni_object_make_global (env, tmp);
173   else
174     return tmp;
175 }
176 
177 jobject
gst_amc_jni_new_object_from_static(JNIEnv * env,GError ** err,gboolean global,jclass klass,jmethodID method,...)178 gst_amc_jni_new_object_from_static (JNIEnv * env, GError ** err,
179     gboolean global, jclass klass, jmethodID method, ...)
180 {
181   jobject tmp;
182   va_list args;
183 
184   va_start (args, method);
185   tmp = (*env)->CallStaticObjectMethodV (env, klass, method, args);
186   va_end (args);
187 
188   if ((*env)->ExceptionCheck (env) || !tmp) {
189     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
190         GST_LIBRARY_ERROR_FAILED, "Failed to create object");
191     return NULL;
192   }
193 
194   if (global)
195     return gst_amc_jni_object_make_global (env, tmp);
196   else
197     return tmp;
198 }
199 
200 jobject
gst_amc_jni_object_make_global(JNIEnv * env,jobject object)201 gst_amc_jni_object_make_global (JNIEnv * env, jobject object)
202 {
203   jobject ret;
204 
205   ret = (*env)->NewGlobalRef (env, object);
206   if (!ret) {
207     GST_ERROR ("Failed to create global reference");
208   }
209   gst_amc_jni_object_local_unref (env, object);
210 
211   return ret;
212 }
213 
214 jobject
gst_amc_jni_object_ref(JNIEnv * env,jobject object)215 gst_amc_jni_object_ref (JNIEnv * env, jobject object)
216 {
217   jobject ret;
218 
219   ret = (*env)->NewGlobalRef (env, object);
220   if (!ret) {
221     GST_ERROR ("Failed to create global reference");
222   }
223   return ret;
224 }
225 
226 void
gst_amc_jni_object_unref(JNIEnv * env,jobject object)227 gst_amc_jni_object_unref (JNIEnv * env, jobject object)
228 {
229   g_return_if_fail (object != NULL);
230 
231   (*env)->DeleteGlobalRef (env, object);
232 }
233 
234 void
gst_amc_jni_object_local_unref(JNIEnv * env,jobject object)235 gst_amc_jni_object_local_unref (JNIEnv * env, jobject object)
236 {
237   g_return_if_fail (object != NULL);
238 
239   (*env)->DeleteLocalRef (env, object);
240 }
241 
242 jstring
gst_amc_jni_string_from_gchar(JNIEnv * env,GError ** err,gboolean global,const gchar * string)243 gst_amc_jni_string_from_gchar (JNIEnv * env, GError ** err,
244     gboolean global, const gchar * string)
245 {
246   jstring tmp;
247 
248   tmp = (*env)->NewStringUTF (env, string);
249   if ((*env)->ExceptionCheck (env)) {
250     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
251         GST_LIBRARY_ERROR_FAILED, "Failed to call Java method");
252     tmp = NULL;
253   }
254 
255   if (global)
256     return gst_amc_jni_object_make_global (env, tmp);
257   else
258     return tmp;
259 }
260 
261 gchar *
gst_amc_jni_string_to_gchar(JNIEnv * env,jstring string,gboolean release)262 gst_amc_jni_string_to_gchar (JNIEnv * env, jstring string, gboolean release)
263 {
264   const gchar *s = NULL;
265   gchar *ret = NULL;
266 
267   s = (*env)->GetStringUTFChars (env, string, NULL);
268   if (!s) {
269     GST_ERROR ("Failed to convert string to UTF8");
270     goto done;
271   }
272 
273   ret = g_strdup (s);
274   (*env)->ReleaseStringUTFChars (env, string, s);
275 
276 done:
277   if (release) {
278     (*env)->DeleteLocalRef (env, string);
279   }
280   return ret;
281 }
282 
283 /* getExceptionSummary() and getStackTrace() taken from Android's
284  *   platform/libnativehelper/JNIHelp.cpp
285  * Modified to work with normal C strings and without C++.
286  *
287  * Copyright (C) 2006 The Android Open Source Project
288  *
289  * Licensed under the Apache License, Version 2.0 (the "License");
290  * you may not use this file except in compliance with the License.
291  * You may obtain a copy of the License at
292  *
293  * http://www.apache.org/licenses/LICENSE-2.0
294  *
295  * Unless required by applicable law or agreed to in writing, software
296  * distributed under the License is distributed on an "AS IS" BASIS,
297  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
298  * See the License for the specific language governing permissions and
299  * limitations under the License.
300  */
301 
302 /*
303  * Returns a human-readable summary of an exception object. The buffer will
304  * be populated with the "binary" class name and, if present, the
305  * exception message.
306  */
307 static gchar *
getExceptionSummary(JNIEnv * env,jthrowable exception)308 getExceptionSummary (JNIEnv * env, jthrowable exception)
309 {
310   GString *gs = g_string_new ("");
311   jclass exceptionClass = NULL, classClass = NULL;
312   jmethodID classGetNameMethod, getMessage;
313   jstring classNameStr = NULL, messageStr = NULL;
314   const char *classNameChars, *messageChars;
315 
316   /* get the name of the exception's class */
317   exceptionClass = (*env)->GetObjectClass (env, exception);
318   classClass = (*env)->GetObjectClass (env, exceptionClass);
319   classGetNameMethod =
320       (*env)->GetMethodID (env, classClass, "getName", "()Ljava/lang/String;");
321 
322   classNameStr =
323       (jstring) (*env)->CallObjectMethod (env, exceptionClass,
324       classGetNameMethod);
325 
326   if (classNameStr == NULL) {
327     if ((*env)->ExceptionCheck (env))
328       (*env)->ExceptionClear (env);
329     g_string_append (gs, "<error getting class name>");
330     goto done;
331   }
332 
333   classNameChars = (*env)->GetStringUTFChars (env, classNameStr, NULL);
334   if (classNameChars == NULL) {
335     if ((*env)->ExceptionCheck (env))
336       (*env)->ExceptionClear (env);
337     g_string_append (gs, "<error getting class name UTF-8>");
338     goto done;
339   }
340 
341   g_string_append (gs, classNameChars);
342 
343   (*env)->ReleaseStringUTFChars (env, classNameStr, classNameChars);
344 
345   /* if the exception has a detail message, get that */
346   getMessage =
347       (*env)->GetMethodID (env, exceptionClass, "getMessage",
348       "()Ljava/lang/String;");
349   messageStr = (jstring) (*env)->CallObjectMethod (env, exception, getMessage);
350   if (messageStr == NULL) {
351     if ((*env)->ExceptionCheck (env))
352       (*env)->ExceptionClear (env);
353     goto done;
354   }
355   g_string_append (gs, ": ");
356 
357   messageChars = (*env)->GetStringUTFChars (env, messageStr, NULL);
358   if (messageChars != NULL) {
359     g_string_append (gs, messageChars);
360     (*env)->ReleaseStringUTFChars (env, messageStr, messageChars);
361   } else {
362     if ((*env)->ExceptionCheck (env))
363       (*env)->ExceptionClear (env);
364     g_string_append (gs, "<error getting message>");
365   }
366 
367 done:
368   if (exceptionClass)
369     (*env)->DeleteLocalRef (env, exceptionClass);
370   if (classClass)
371     (*env)->DeleteLocalRef (env, classClass);
372   if (classNameStr)
373     (*env)->DeleteLocalRef (env, classNameStr);
374   if (messageStr)
375     (*env)->DeleteLocalRef (env, messageStr);
376 
377   return g_string_free (gs, FALSE);
378 }
379 
380 /*
381  * Returns an exception (with stack trace) as a string.
382  */
383 static gchar *
getStackTrace(JNIEnv * env,jthrowable exception)384 getStackTrace (JNIEnv * env, jthrowable exception)
385 {
386   GString *gs = g_string_new ("");
387   jclass stringWriterClass = NULL, printWriterClass = NULL;
388   jclass exceptionClass = NULL;
389   jmethodID stringWriterCtor, stringWriterToStringMethod;
390   jmethodID printWriterCtor, printStackTraceMethod;
391   jobject stringWriter = NULL, printWriter = NULL;
392   jstring messageStr = NULL;
393   const char *utfChars;
394 
395   stringWriterClass = (*env)->FindClass (env, "java/io/StringWriter");
396 
397   if (stringWriterClass == NULL) {
398     g_string_append (gs, "<error getting java.io.StringWriter class>");
399     goto done;
400   }
401 
402   stringWriterCtor =
403       (*env)->GetMethodID (env, stringWriterClass, "<init>", "()V");
404   stringWriterToStringMethod =
405       (*env)->GetMethodID (env, stringWriterClass, "toString",
406       "()Ljava/lang/String;");
407 
408   printWriterClass = (*env)->FindClass (env, "java/io/PrintWriter");
409   if (printWriterClass == NULL) {
410     g_string_append (gs, "<error getting java.io.PrintWriter class>");
411     goto done;
412   }
413 
414   printWriterCtor =
415       (*env)->GetMethodID (env, printWriterClass, "<init>",
416       "(Ljava/io/Writer;)V");
417   stringWriter = (*env)->NewObject (env, stringWriterClass, stringWriterCtor);
418   if (stringWriter == NULL) {
419     if ((*env)->ExceptionCheck (env))
420       (*env)->ExceptionClear (env);
421     g_string_append (gs, "<error creating new StringWriter instance>");
422     goto done;
423   }
424 
425   printWriter =
426       (*env)->NewObject (env, printWriterClass, printWriterCtor, stringWriter);
427   if (printWriter == NULL) {
428     if ((*env)->ExceptionCheck (env))
429       (*env)->ExceptionClear (env);
430     g_string_append (gs, "<error creating new PrintWriter instance>");
431     goto done;
432   }
433 
434   exceptionClass = (*env)->GetObjectClass (env, exception);
435   printStackTraceMethod =
436       (*env)->GetMethodID (env, exceptionClass, "printStackTrace",
437       "(Ljava/io/PrintWriter;)V");
438   (*env)->CallVoidMethod (env, exception, printStackTraceMethod, printWriter);
439   if ((*env)->ExceptionCheck (env)) {
440     (*env)->ExceptionClear (env);
441     g_string_append (gs, "<exception while printing stack trace>");
442     goto done;
443   }
444 
445   messageStr = (jstring) (*env)->CallObjectMethod (env, stringWriter,
446       stringWriterToStringMethod);
447   if (messageStr == NULL) {
448     if ((*env)->ExceptionCheck (env))
449       (*env)->ExceptionClear (env);
450     g_string_append (gs, "<failed to call StringWriter.toString()>");
451     goto done;
452   }
453 
454   utfChars = (*env)->GetStringUTFChars (env, messageStr, NULL);
455   if (utfChars == NULL) {
456     if ((*env)->ExceptionCheck (env))
457       (*env)->ExceptionClear (env);
458     g_string_append (gs, "<failed to get UTF chars for message>");
459     goto done;
460   }
461 
462   g_string_append (gs, utfChars);
463 
464   (*env)->ReleaseStringUTFChars (env, messageStr, utfChars);
465 
466 done:
467   if (stringWriterClass)
468     (*env)->DeleteLocalRef (env, stringWriterClass);
469   if (printWriterClass)
470     (*env)->DeleteLocalRef (env, printWriterClass);
471   if (exceptionClass)
472     (*env)->DeleteLocalRef (env, exceptionClass);
473   if (stringWriter)
474     (*env)->DeleteLocalRef (env, stringWriter);
475   if (printWriter)
476     (*env)->DeleteLocalRef (env, printWriter);
477   if (messageStr)
478     (*env)->DeleteLocalRef (env, messageStr);
479 
480   return g_string_free (gs, FALSE);
481 }
482 
483 static JNIEnv *
gst_amc_jni_attach_current_thread(void)484 gst_amc_jni_attach_current_thread (void)
485 {
486   JNIEnv *env;
487   JavaVMAttachArgs args;
488   gint ret;
489 
490   GST_DEBUG ("Attaching thread %p", g_thread_self ());
491   args.version = JNI_VERSION_1_6;
492   args.name = NULL;
493   args.group = NULL;
494 
495   if ((ret = (*java_vm)->AttachCurrentThread (java_vm, &env, &args)) != JNI_OK) {
496     GST_ERROR ("Failed to attach current thread: %d", ret);
497     return NULL;
498   }
499 
500   return env;
501 }
502 
503 static void
gst_amc_jni_detach_current_thread(void * env)504 gst_amc_jni_detach_current_thread (void *env)
505 {
506   gint ret;
507 
508   GST_DEBUG ("Detaching thread %p", g_thread_self ());
509   if ((ret = (*java_vm)->DetachCurrentThread (java_vm)) != JNI_OK) {
510     GST_DEBUG ("Failed to detach current thread: %d", ret);
511   }
512 }
513 
514 static JavaVM *
get_application_java_vm(void)515 get_application_java_vm (void)
516 {
517   GModule *module = NULL;
518   JavaVM *(*get_java_vm) (void);
519   JavaVM *vm = NULL;
520 
521   module = g_module_open (NULL, G_MODULE_BIND_LOCAL);
522 
523   if (!module) {
524     return NULL;
525   }
526 
527   if (g_module_symbol (module, "gst_android_get_java_vm",
528           (gpointer *) & get_java_vm) && get_java_vm) {
529     vm = get_java_vm ();
530   }
531 
532   g_module_close (module);
533 
534   return vm;
535 }
536 
537 static gboolean
check_nativehelper(void)538 check_nativehelper (void)
539 {
540   GModule *module;
541   void **jni_invocation = NULL;
542   gboolean ret = FALSE;
543 
544   module = g_module_open (NULL, G_MODULE_BIND_LOCAL);
545   if (!module)
546     return ret;
547 
548   /* Check if libnativehelper is loaded in the process and if
549    * it has these awful wrappers for JNI_CreateJavaVM and
550    * JNI_GetCreatedJavaVMs that crash the app if you don't
551    * create a JniInvocation instance first. If it isn't we
552    * just fail here and don't initialize anything.
553    * See this code for reference:
554    * https://android.googlesource.com/platform/libnativehelper/+/master/JniInvocation.cpp
555    */
556   if (!g_module_symbol (module, "_ZN13JniInvocation15jni_invocation_E",
557           (gpointer *) & jni_invocation)) {
558     ret = TRUE;
559   } else {
560     ret = (jni_invocation != NULL && *jni_invocation != NULL);
561   }
562 
563   g_module_close (module);
564 
565   return ret;
566 }
567 
568 static gboolean
load_java_module(const gchar * name)569 load_java_module (const gchar * name)
570 {
571   java_module = g_module_open (name, G_MODULE_BIND_LOCAL);
572   if (!java_module)
573     goto load_failed;
574 
575   if (!g_module_symbol (java_module, "JNI_CreateJavaVM",
576           (gpointer *) & create_java_vm)) {
577     GST_ERROR ("Could not find 'JNI_CreateJavaVM' in '%s': %s",
578         GST_STR_NULL (name), g_module_error ());
579     create_java_vm = NULL;
580   }
581 
582   if (!g_module_symbol (java_module, "JNI_GetCreatedJavaVMs",
583           (gpointer *) & get_created_java_vms))
584     goto symbol_error;
585 
586   return TRUE;
587 
588 load_failed:
589   {
590     GST_ERROR ("Failed to load Java module '%s': %s", GST_STR_NULL (name),
591         g_module_error ());
592     return FALSE;
593   }
594 symbol_error:
595   {
596     GST_ERROR ("Failed to locate required JNI symbols in '%s': %s",
597         GST_STR_NULL (name), g_module_error ());
598     g_module_close (java_module);
599     java_module = NULL;
600     return FALSE;
601   }
602 }
603 
604 static gboolean
check_application_class_loader(void)605 check_application_class_loader (void)
606 {
607   gboolean ret = TRUE;
608   GModule *module = NULL;
609 
610   module = g_module_open (NULL, G_MODULE_BIND_LOCAL);
611   if (!module) {
612     return FALSE;
613   }
614   if (!g_module_symbol (module, "gst_android_get_application_class_loader",
615           (gpointer *) & get_class_loader)) {
616     ret = FALSE;
617   }
618 
619   g_module_close (module);
620 
621   return ret;
622 }
623 
624 static gboolean
initialize_classes(void)625 initialize_classes (void)
626 {
627   if (!check_application_class_loader ()) {
628     GST_ERROR ("Could not find application class loader provider");
629     return FALSE;
630   }
631 
632   return TRUE;
633 }
634 
635 static gboolean
gst_amc_jni_initialize_java_vm(void)636 gst_amc_jni_initialize_java_vm (void)
637 {
638   jsize n_vms;
639   gint ret;
640 
641   if (java_vm) {
642     GST_DEBUG ("Java VM already provided by the application");
643     return initialize_classes ();
644   }
645 
646   java_vm = get_application_java_vm ();
647   if (java_vm) {
648     GST_DEBUG ("Java VM successfully requested from the application");
649     return initialize_classes ();
650   }
651 
652   /* Returns TRUE if we can safely
653    * a) get the current VMs and
654    * b) start a VM if none is started yet
655    *
656    * FIXME: On Android >= 4.4 we won't be able to safely start a
657    * VM on our own without using private C++ API!
658    */
659   if (!check_nativehelper ()) {
660     GST_ERROR ("Can't safely check for VMs or start a VM");
661     return FALSE;
662   }
663 
664   if (!load_java_module (NULL)) {
665     if (!load_java_module ("libdvm"))
666       return FALSE;
667   }
668 
669   n_vms = 0;
670   if ((ret = get_created_java_vms (&java_vm, 1, &n_vms)) != JNI_OK)
671     goto get_created_failed;
672 
673   if (n_vms > 0) {
674     GST_DEBUG ("Successfully got existing Java VM %p", java_vm);
675   } else if (create_java_vm) {
676     JNIEnv *env;
677     JavaVMInitArgs vm_args;
678     JavaVMOption options[4];
679 
680     GST_DEBUG ("Found no existing Java VM, trying to start one");
681 
682     options[0].optionString = "-verbose:jni";
683     options[1].optionString = "-verbose:gc";
684     options[2].optionString = "-Xcheck:jni";
685     options[3].optionString = "-Xdebug";
686 
687     vm_args.version = JNI_VERSION_1_4;
688     vm_args.options = options;
689     vm_args.nOptions = 4;
690     vm_args.ignoreUnrecognized = JNI_TRUE;
691     if ((ret = create_java_vm (&java_vm, &env, &vm_args)) != JNI_OK)
692       goto create_failed;
693     GST_DEBUG ("Successfully created Java VM %p", java_vm);
694 
695     started_java_vm = TRUE;
696   } else {
697     GST_ERROR ("JNI_CreateJavaVM not available");
698     java_vm = NULL;
699   }
700 
701   if (java_vm == NULL)
702     return FALSE;
703 
704   return initialize_classes ();
705 
706 get_created_failed:
707   {
708     GST_ERROR ("Failed to get already created VMs: %d", ret);
709     g_module_close (java_module);
710     java_module = NULL;
711     return FALSE;
712   }
713 create_failed:
714   {
715     GST_ERROR ("Failed to create a Java VM: %d", ret);
716     g_module_close (java_module);
717     java_module = NULL;
718     return FALSE;
719   }
720 }
721 
722 static void
gst_amc_jni_set_error_string(JNIEnv * env,GError ** err,GQuark domain,gint code,const gchar * message)723 gst_amc_jni_set_error_string (JNIEnv * env, GError ** err, GQuark domain,
724     gint code, const gchar * message)
725 {
726   jthrowable exception;
727 
728   if (!err) {
729     if ((*env)->ExceptionCheck (env))
730       (*env)->ExceptionClear (env);
731     return;
732   }
733 
734   if ((*env)->ExceptionCheck (env)) {
735     if ((exception = (*env)->ExceptionOccurred (env))) {
736       gchar *exception_description, *exception_stacktrace;
737 
738       /* Clear exception so that we can call Java methods again */
739       (*env)->ExceptionClear (env);
740 
741       exception_description = getExceptionSummary (env, exception);
742       exception_stacktrace = getStackTrace (env, exception);
743       g_set_error (err, domain, code, "%s: %s\n%s", message,
744           exception_description, exception_stacktrace);
745       g_free (exception_description);
746       g_free (exception_stacktrace);
747 
748       (*env)->DeleteLocalRef (env, exception);
749     } else {
750       (*env)->ExceptionClear (env);
751       g_set_error (err, domain, code, "%s", message);
752     }
753   } else {
754     g_set_error (err, domain, code, "%s", message);
755   }
756 }
757 
758 G_GNUC_PRINTF (5, 6)
gst_amc_jni_set_error(JNIEnv * env,GError ** err,GQuark domain,gint code,const gchar * format,...)759      void gst_amc_jni_set_error (JNIEnv * env, GError ** err, GQuark domain,
760     gint code, const gchar * format, ...)
761 {
762   gchar *message;
763   va_list var_args;
764 
765   va_start (var_args, format);
766   message = g_strdup_vprintf (format, var_args);
767   va_end (var_args);
768 
769   gst_amc_jni_set_error_string (env, err, domain, code, message);
770 
771   g_free (message);
772 }
773 
774 static gpointer
gst_amc_jni_initialize_internal(gpointer data)775 gst_amc_jni_initialize_internal (gpointer data)
776 {
777   pthread_key_create (&current_jni_env, gst_amc_jni_detach_current_thread);
778   return gst_amc_jni_initialize_java_vm ()? GINT_TO_POINTER (1) : NULL;
779 }
780 
781 /* Allow the application to set the Java VM */
782 void
gst_amc_jni_set_java_vm(JavaVM * vm)783 gst_amc_jni_set_java_vm (JavaVM * vm)
784 {
785   GST_DEBUG ("Application provides Java VM %p", vm);
786   java_vm = vm;
787 }
788 
789 gboolean
gst_amc_jni_initialize(void)790 gst_amc_jni_initialize (void)
791 {
792   GOnce once = G_ONCE_INIT;
793 
794   g_once (&once, gst_amc_jni_initialize_internal, NULL);
795   return once.retval != NULL;
796 }
797 
798 JNIEnv *
gst_amc_jni_get_env(void)799 gst_amc_jni_get_env (void)
800 {
801   JNIEnv *env;
802 
803   if ((env = pthread_getspecific (current_jni_env)) == NULL) {
804     env = gst_amc_jni_attach_current_thread ();
805     pthread_setspecific (current_jni_env, env);
806   }
807 
808   return env;
809 }
810 
811 gboolean
gst_amc_jni_is_vm_started(void)812 gst_amc_jni_is_vm_started (void)
813 {
814   return started_java_vm;
815 }
816 
817 jclass
gst_amc_jni_get_application_class(JNIEnv * env,const gchar * name,GError ** err)818 gst_amc_jni_get_application_class (JNIEnv * env, const gchar * name,
819     GError ** err)
820 {
821   jclass tmp = NULL;
822   jclass class = NULL;
823   jstring name_jstr = NULL;
824 
825   jobject class_loader = NULL;
826   jclass class_loader_cls = NULL;
827   jmethodID load_class_id = 0;
828 
829   GST_LOG ("attempting to retrieve class %s", name);
830 
831   if (!get_class_loader) {
832     g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
833         "Could not retrieve application class loader function");
834     goto done;
835   }
836 
837   class_loader = get_class_loader ();
838   if (!class_loader) {
839     g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
840         "Could not retrieve application class loader");
841     goto done;
842   }
843 
844   class_loader_cls = (*env)->GetObjectClass (env, class_loader);
845   if (!class_loader_cls) {
846     g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
847         "Could not retrieve application class loader java class");
848     goto done;
849   }
850 
851   load_class_id =
852       gst_amc_jni_get_method_id (env, err, class_loader_cls, "loadClass",
853       "(Ljava/lang/String;)Ljava/lang/Class;");
854   if (!load_class_id) {
855     goto done;
856   }
857 
858   name_jstr = gst_amc_jni_string_from_gchar (env, err, FALSE, name);
859   if (!name_jstr) {
860     goto done;
861   }
862 
863   if (gst_amc_jni_call_object_method (env, err, class_loader,
864           load_class_id, &tmp, name_jstr)) {
865     class = gst_amc_jni_object_make_global (env, tmp);
866   }
867 
868 done:
869   gst_amc_jni_object_local_unref (env, name_jstr);
870   gst_amc_jni_object_local_unref (env, class_loader_cls);
871 
872   return class;
873 }
874 
875 #define CALL_STATIC_TYPE_METHOD(_type, _name,  _jname)                                                     \
876 gboolean gst_amc_jni_call_static_##_name##_method (JNIEnv *env, GError ** err, jclass klass, jmethodID methodID, _type * value, ...)   \
877   {                                                                                                          \
878     gboolean ret = TRUE;                                                                                     \
879     va_list args;                                                                                            \
880     va_start(args, value);                                                                                \
881     *value = (*env)->CallStatic##_jname##MethodV(env, klass, methodID, args);                                \
882     if ((*env)->ExceptionCheck (env)) {                                                                      \
883       gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,                               \
884           "Failed to call static Java method");                                                         \
885       ret = FALSE;                                                                                           \
886     }                                                                                                        \
887     va_end(args);                                                                                            \
888     return ret;                                                                                              \
889   }
890 
891 CALL_STATIC_TYPE_METHOD (gboolean, boolean, Boolean);
892 CALL_STATIC_TYPE_METHOD (gint8, byte, Byte);
893 CALL_STATIC_TYPE_METHOD (gshort, short, Short);
894 CALL_STATIC_TYPE_METHOD (gint, int, Int);
895 CALL_STATIC_TYPE_METHOD (gchar, char, Char);
896 CALL_STATIC_TYPE_METHOD (gint64, long, Long);
897 CALL_STATIC_TYPE_METHOD (gfloat, float, Float);
898 CALL_STATIC_TYPE_METHOD (gdouble, double, Double);
899 CALL_STATIC_TYPE_METHOD (jobject, object, Object);
900 
901 gboolean
gst_amc_jni_call_static_void_method(JNIEnv * env,GError ** err,jclass klass,jmethodID methodID,...)902 gst_amc_jni_call_static_void_method (JNIEnv * env, GError ** err, jclass klass,
903     jmethodID methodID, ...)
904 {
905   gboolean ret = TRUE;
906   va_list args;
907   va_start (args, methodID);
908 
909   (*env)->CallStaticVoidMethodV (env, klass, methodID, args);
910   if ((*env)->ExceptionCheck (env)) {
911     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
912         GST_LIBRARY_ERROR_FAILED, "Failed to call static Java method");
913     ret = FALSE;
914   }
915   va_end (args);
916   return ret;
917 }
918 
919 #define CALL_TYPE_METHOD(_type, _name,  _jname)                                                              \
920 gboolean gst_amc_jni_call_##_name##_method (JNIEnv *env, GError ** err, jobject obj, jmethodID methodID, _type *value, ...)   \
921   {                                                                                                          \
922     gboolean ret = TRUE;                                                                                     \
923     va_list args;                                                                                            \
924     va_start(args, value);                                                                                \
925     *value = (*env)->Call##_jname##MethodV(env, obj, methodID, args);                                        \
926     if ((*env)->ExceptionCheck (env)) {                                                                      \
927       gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,                               \
928           "Failed to call Java method");                                                                \
929       ret = FALSE;                                                                                           \
930     }                                                                                                        \
931     va_end(args);                                                                                            \
932     return ret;                                                                                              \
933   }
934 
935 CALL_TYPE_METHOD (gboolean, boolean, Boolean);
936 CALL_TYPE_METHOD (gint8, byte, Byte);
937 CALL_TYPE_METHOD (gshort, short, Short);
938 CALL_TYPE_METHOD (gint, int, Int);
939 CALL_TYPE_METHOD (gchar, char, Char);
940 CALL_TYPE_METHOD (gint64, long, Long);
941 CALL_TYPE_METHOD (gfloat, float, Float);
942 CALL_TYPE_METHOD (gdouble, double, Double);
943 CALL_TYPE_METHOD (jobject, object, Object);
944 
945 gboolean
gst_amc_jni_call_void_method(JNIEnv * env,GError ** err,jobject obj,jmethodID methodID,...)946 gst_amc_jni_call_void_method (JNIEnv * env, GError ** err, jobject obj,
947     jmethodID methodID, ...)
948 {
949   gboolean ret = TRUE;
950   va_list args;
951   va_start (args, methodID);
952 
953   (*env)->CallVoidMethodV (env, obj, methodID, args);
954   if ((*env)->ExceptionCheck (env)) {
955     gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
956         GST_LIBRARY_ERROR_FAILED, "Failed to call Java method");
957     ret = FALSE;
958   }
959   va_end (args);
960   return ret;
961 }
962 
963 #define GET_TYPE_FIELD(_type, _name, _jname)                                               \
964 gboolean gst_amc_jni_get_##_name##_field (JNIEnv *env, GError ** err, jobject obj, jfieldID fieldID, _type *value)   \
965   {                                                                                                 \
966     gboolean ret = TRUE;                                                                            \
967                                                                                                     \
968     *value = (*env)->Get##_jname##Field(env, obj, fieldID);                                         \
969     if ((*env)->ExceptionCheck (env)) {                                                             \
970       gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,                      \
971           "Failed to get Java field");                                                         \
972       ret = FALSE;                                                                                  \
973     }                                                                                               \
974     return ret;                                                                                     \
975   }
976 
977 GET_TYPE_FIELD (gboolean, boolean, Boolean);
978 GET_TYPE_FIELD (gint8, byte, Byte);
979 GET_TYPE_FIELD (gshort, short, Short);
980 GET_TYPE_FIELD (gint, int, Int);
981 GET_TYPE_FIELD (gchar, char, Char);
982 GET_TYPE_FIELD (gint64, long, Long);
983 GET_TYPE_FIELD (gfloat, float, Float);
984 GET_TYPE_FIELD (gdouble, double, Double);
985 GET_TYPE_FIELD (jobject, object, Object);
986 
987 #define GET_STATIC_TYPE_FIELD(_type, _name, _jname)                                               \
988 gboolean gst_amc_jni_get_static_##_name##_field (JNIEnv *env, GError ** err, jclass klass, jfieldID fieldID, _type *value)   \
989   {                                                                                                 \
990     gboolean ret = TRUE;                                                                            \
991                                                                                                     \
992     *value = (*env)->GetStatic##_jname##Field(env, klass, fieldID);                                 \
993     if ((*env)->ExceptionCheck (env)) {                                                             \
994       gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,                      \
995           "Failed to get static Java field");                                                  \
996       ret = FALSE;                                                                                  \
997     }                                                                                               \
998     return ret;                                                                                     \
999   }
1000 
1001 GET_STATIC_TYPE_FIELD (gboolean, boolean, Boolean);
1002 GET_STATIC_TYPE_FIELD (gint8, byte, Byte);
1003 GET_STATIC_TYPE_FIELD (gshort, short, Short);
1004 GET_STATIC_TYPE_FIELD (gint, int, Int);
1005 GET_STATIC_TYPE_FIELD (gchar, char, Char);
1006 GET_STATIC_TYPE_FIELD (gint64, long, Long);
1007 GET_STATIC_TYPE_FIELD (gfloat, float, Float);
1008 GET_STATIC_TYPE_FIELD (gdouble, double, Double);
1009 GET_STATIC_TYPE_FIELD (jobject, object, Object);
1010